From b3893e2466076cf76abd99d335e4de1f52c6f1da Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 19 Sep 2022 09:13:24 +0200 Subject: [PATCH 01/17] add forwardingoptions_dhcprelay_servergroup resource Fix #423 --- CHANGELOG.md | 2 + ...forwardingoptions_dhcprelay_servergroup.md | 51 ++ junos/provider.go | 1 + ...forwardingoptions_dhcprelay_servergroup.go | 449 ++++++++++++++++++ ...rdingoptions_dhcprelay_servergroup_test.go | 131 +++++ 5 files changed, 634 insertions(+) create mode 100644 docs/resources/forwardingoptions_dhcprelay_servergroup.md create mode 100644 junos/resource_forwardingoptions_dhcprelay_servergroup.go create mode 100644 junos/resource_forwardingoptions_dhcprelay_servergroup_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b82be18e..3c0aa8c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ FEATURES: +* add `junos_forwardingoptions_dhcprelay_servergroup` resource (Fixes #423) + ENHANCEMENTS: BUG FIXES: diff --git a/docs/resources/forwardingoptions_dhcprelay_servergroup.md b/docs/resources/forwardingoptions_dhcprelay_servergroup.md new file mode 100644 index 00000000..2c2dcc49 --- /dev/null +++ b/docs/resources/forwardingoptions_dhcprelay_servergroup.md @@ -0,0 +1,51 @@ +--- +page_title: "Junos: junos_forwardingoptions_dhcprelay_servergroup" +--- + +# junos_forwardingoptions_dhcprelay_servergroup + +Provides a DHCP relay server group. + +## Example Usage + +```hcl +# Add a DHCP relay server group +resource "junos_forwardingoptions_dhcprelay_servergroup" "demo" { + name = "demo" + ip_address = [ + "192.0.2.8", + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +- **name** (Required, String, Forces new resource) + Server group name. +- **routing_instance** (Optional, String, Forces new resource) + Routing instance for server group. + Need to be `default` or name of routing instance. + Defaults to `default` +- **version** (Optional, String, Forces new resource) + Version for DHCP or DHCPv6. + Need to be `v4` or `v6`. +- **ip_address** (Optional, List of String) + IP Addresses of DHCP servers. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format `_-__-_`. + +## Import + +Junos forwarding-options dhcp-relay server-group can be imported using an id made up of +`_-__-_`, e.g. + +```shell +$ terraform import junos_forwardingoptions_dhcprelay_servergroup.demo demo_-_default_-_v4 +``` diff --git a/junos/provider.go b/junos/provider.go index f317d471..a91fab21 100644 --- a/junos/provider.go +++ b/junos/provider.go @@ -126,6 +126,7 @@ func Provider() *schema.Provider { "junos_evpn": resourceEvpn(), "junos_firewall_filter": resourceFirewallFilter(), "junos_firewall_policer": resourceFirewallPolicer(), + "junos_forwardingoptions_dhcprelay_servergroup": resourceForwardingOptionsDhcpRelayServerGroup(), "junos_forwardingoptions_sampling_instance": resourceForwardingoptionsSamplingInstance(), "junos_generate_route": resourceGenerateRoute(), "junos_group_dual_system": resourceGroupDualSystem(), diff --git a/junos/resource_forwardingoptions_dhcprelay_servergroup.go b/junos/resource_forwardingoptions_dhcprelay_servergroup.go new file mode 100644 index 00000000..f1783fb9 --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay_servergroup.go @@ -0,0 +1,449 @@ +package junos + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +type fwdOptsDhcpRelSrvGrpOptions struct { + name string + routingInstance string + version string + ipAddress []string +} + +func resourceForwardingOptionsDhcpRelayServerGroup() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceForwardingOptionsDhcpRelayServerGroupCreate, + ReadWithoutTimeout: resourceForwardingOptionsDhcpRelayServerGroupRead, + UpdateWithoutTimeout: resourceForwardingOptionsDhcpRelayServerGroupUpdate, + DeleteWithoutTimeout: resourceForwardingOptionsDhcpRelayServerGroupDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceForwardingOptionsDhcpRelayServerGroupImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "routing_instance": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: defaultW, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "v4", + ValidateFunc: validation.StringInSlice([]string{"v4", "v6"}, false), + }, + "ip_address": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.IsIPAddress, + }, + }, + }, + } +} + +func resourceForwardingOptionsDhcpRelayServerGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeCreateSetFile != "" { + if err := setForwardingOptionsDhcpRelayServerGroup(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(nameArg + idSeparator + routingInstanceArg + idSeparator + versionArg) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if routingInstanceArg != defaultW { + instanceExists, err := checkRoutingInstanceExists(routingInstanceArg, clt, junSess) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !instanceExists { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, + diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", routingInstanceArg))...) + } + } + fwdOptsDhcpRelSrvGrpExists, err := checkForwardingOptionsDhcpRelayServerGroupExists( + nameArg, + routingInstanceArg, + versionArg, + clt, junSess) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if fwdOptsDhcpRelSrvGrpExists { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + if versionArg == "v6" { + return append(diagWarns, + diag.FromErr(fmt.Errorf("forwarding-options dhcp-relay dhcpv6 server-group %v"+ + " already exists in routing-instance %s", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, + diag.FromErr(fmt.Errorf("forwarding-options dhcp-relay server-group %v"+ + " already exists in routing-instance %s", nameArg, routingInstanceArg))...) + } + + if err := setForwardingOptionsDhcpRelayServerGroup(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("create resource junos_forwardingoptions_dhcprelay_servergroup", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + fwdOptsDhcpRelSrvGrpExists, err = checkForwardingOptionsDhcpRelayServerGroupExists( + nameArg, + routingInstanceArg, + versionArg, + clt, junSess) + if err != nil { + return append(diagWarns, diag.FromErr(err)...) + } + if fwdOptsDhcpRelSrvGrpExists { + d.SetId(nameArg + idSeparator + routingInstanceArg + idSeparator + versionArg) + } else { + if versionArg == "v6" { + return append(diagWarns, diag.FromErr(fmt.Errorf( + "forwarding-options dhcp-relay dhcpv6 server-group %v not exists in routing_instance %s after commit "+ + "=> check your config", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, diag.FromErr(fmt.Errorf( + "forwarding-options dhcp-relay server-group %v not exists in routing_instance %s after commit "+ + "=> check your config", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, resourceForwardingOptionsDhcpRelayServerGroupReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayServerGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + + return resourceForwardingOptionsDhcpRelayServerGroupReadWJunSess(d, clt, junSess) +} + +func resourceForwardingOptionsDhcpRelayServerGroupReadWJunSess( + d *schema.ResourceData, clt *Client, junSess *junosSession, +) diag.Diagnostics { + mutex.Lock() + fwdOptsDhcpRelSrvGrpOptions, err := readForwardingOptionsDhcpRelayServerGroup( + d.Get("name").(string), + d.Get("routing_instance").(string), + d.Get("version").(string), + clt, junSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if fwdOptsDhcpRelSrvGrpOptions.name == "" { + d.SetId("") + } else { + fillForwardingOptionsDhcpRelayServerGroupData(d, fwdOptsDhcpRelSrvGrpOptions) + } + + return nil +} + +func resourceForwardingOptionsDhcpRelayServerGroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + d.Partial(true) + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeUpdateAlso { + if err := delForwardingOptionsDhcpRelayServerGroup(nameArg, routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + if err := setForwardingOptionsDhcpRelayServerGroup(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.Partial(false) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelayServerGroup(nameArg, routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if err := setForwardingOptionsDhcpRelayServerGroup(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("update resource junos_forwardingoptions_dhcprelay_servergroup", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.Partial(false) + + return append(diagWarns, resourceForwardingOptionsDhcpRelayServerGroupReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayServerGroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeDeleteAlso { + if err := delForwardingOptionsDhcpRelayServerGroup(nameArg, routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelayServerGroup(nameArg, routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := clt.commitConf("delete resource junos_forwardingoptions_dhcprelay_servergroup", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + return diagWarns +} + +func resourceForwardingOptionsDhcpRelayServerGroupImport(ctx context.Context, d *schema.ResourceData, m interface{}, +) ([]*schema.ResourceData, error) { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return nil, err + } + defer clt.closeSession(junSess) + result := make([]*schema.ResourceData, 1) + idSplit := strings.Split(d.Id(), idSeparator) + if len(idSplit) < 3 { + return nil, fmt.Errorf("missing element(s) in id with separator %v", idSeparator) + } + if idSplit[2] != "v4" && idSplit[2] != "v6" { + return nil, fmt.Errorf("bad version '%s' in id, need to be 'v4' or 'v6' (id must be "+ + ""+idSeparator+""+idSeparator+")", idSplit[2]) + } + fwdOptsDhcpRelSrvGrpExists, err := checkForwardingOptionsDhcpRelayServerGroupExists( + idSplit[0], + idSplit[1], + idSplit[2], + clt, junSess) + if err != nil { + return nil, err + } + if !fwdOptsDhcpRelSrvGrpExists { + if idSplit[2] == "v6" { + return nil, fmt.Errorf("don't find forwarding-options dhcp-relay dhcpv6 server-group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + + return nil, fmt.Errorf("don't find forwarding-options dhcp-relay server-group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + fwdOptsDhcpRelSrvGrpOptions, err := readForwardingOptionsDhcpRelayServerGroup( + idSplit[0], + idSplit[1], + idSplit[2], + clt, junSess) + if err != nil { + return nil, err + } + fillForwardingOptionsDhcpRelayServerGroupData(d, fwdOptsDhcpRelSrvGrpOptions) + + result[0] = d + + return result, nil +} + +func checkForwardingOptionsDhcpRelayServerGroupExists( + name, instance, version string, clt *Client, junSess *junosSession, +) (bool, error) { + showCmd := cmdShowConfig + if instance != defaultW { + showCmd += routingInstancesWS + instance + " " + } + showCmd += "forwarding-options dhcp-relay " + if version == "v6" { + showCmd += "dhcpv6 server-group " + name + } else { + showCmd += "server-group " + name + } + showConfig, err := clt.command(showCmd+pipeDisplaySet, junSess) + if err != nil { + return false, err + } + if showConfig == emptyW { + return false, nil + } + + return true, nil +} + +func setForwardingOptionsDhcpRelayServerGroup(d *schema.ResourceData, clt *Client, junSess *junosSession) error { + configSet := make([]string, 0) + + setPrefix := setLS + if d.Get("routing_instance").(string) != defaultW { + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " + } + if d.Get("version").(string) == "v6" { + setPrefix += "forwarding-options dhcp-relay dhcpv6 server-group " + d.Get("name").(string) + " " + } else { + setPrefix += "forwarding-options dhcp-relay server-group " + d.Get("name").(string) + " " + } + + configSet = append(configSet, setPrefix) + for _, v := range d.Get("ip_address").([]interface{}) { + configSet = append(configSet, setPrefix+v.(string)) + } + + return clt.configSet(configSet, junSess) +} + +func readForwardingOptionsDhcpRelayServerGroup(name, instance, version string, clt *Client, junSess *junosSession, +) (fwdOptsDhcpRelSrvGrpOptions, error) { + var confRead fwdOptsDhcpRelSrvGrpOptions + + showCmd := cmdShowConfig + if instance != defaultW { + showCmd += routingInstancesWS + instance + " " + } + showCmd += "forwarding-options dhcp-relay " + if version == "v6" { + showCmd += "dhcpv6 server-group " + name + } else { + showCmd += "server-group " + name + } + showConfig, err := clt.command(showCmd+pipeDisplaySetRelative, junSess) + if err != nil { + return confRead, err + } + if showConfig != emptyW { + confRead.name = name + confRead.routingInstance = instance + confRead.version = version + for _, item := range strings.Split(showConfig, "\n") { + itemTrim := strings.TrimPrefix(item, setLS) + if strings.Contains(item, xmlStartTagConfigOut) { + continue + } + if strings.Contains(item, xmlEndTagConfigOut) { + break + } + if itemTrim != "" { + confRead.ipAddress = append(confRead.ipAddress, strings.TrimPrefix(itemTrim, " ")) + } + } + } + + return confRead, nil +} + +func delForwardingOptionsDhcpRelayServerGroup(name, instance, version string, clt *Client, junSess *junosSession, +) error { + configSet := make([]string, 0, 1) + switch { + case instance == defaultW && version == "v6": + configSet = append(configSet, deleteLS+"forwarding-options dhcp-relay dhcpv6 server-group "+name) + case instance == defaultW && version == "v4": + configSet = append(configSet, deleteLS+"forwarding-options dhcp-relay server-group "+name) + case instance != defaultW && version == "v6": + configSet = append(configSet, delRoutingInstances+instance+" "+ + "forwarding-options dhcp-relay dhcpv6 server-group "+name) + case instance != defaultW && version == "v4": + configSet = append(configSet, delRoutingInstances+instance+" "+ + "forwarding-options dhcp-relay server-group "+name) + } + + return clt.configSet(configSet, junSess) +} + +func fillForwardingOptionsDhcpRelayServerGroupData( + d *schema.ResourceData, fwdOptsDhcpRelSrvGrpOptions fwdOptsDhcpRelSrvGrpOptions, +) { + if tfErr := d.Set("name", fwdOptsDhcpRelSrvGrpOptions.name); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("routing_instance", fwdOptsDhcpRelSrvGrpOptions.routingInstance); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("version", fwdOptsDhcpRelSrvGrpOptions.version); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("ip_address", fwdOptsDhcpRelSrvGrpOptions.ipAddress); tfErr != nil { + panic(tfErr) + } +} diff --git a/junos/resource_forwardingoptions_dhcprelay_servergroup_test.go b/junos/resource_forwardingoptions_dhcprelay_servergroup_test.go new file mode 100644 index 00000000..46d17643 --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay_servergroup_test.go @@ -0,0 +1,131 @@ +package junos_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccJunosForwardingOptionsDhcpRelayServerGroup_basic(t *testing.T) { + if os.Getenv("TESTACC_SRX") != "" || os.Getenv("TESTACC_ROUTER") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosForwardingOptionsDhcpRelayServerGroupConfigCreate(), + }, + { + Config: testAccJunosForwardingOptionsDhcpRelayServerGroupConfigUpdate(), + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_servergroup_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_servergroup_v6_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccJunosForwardingOptionsDhcpRelayServerGroupConfigUpdate2(), + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_servergroup", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_servergroup_v6", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +func testAccJunosForwardingOptionsDhcpRelayServerGroupConfigCreate() string { + return ` +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup" { + name = "testacc_dhcprelay_servergroup" +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_v6" { + name = "testacc_dhcprelay_servergroup_v6" + version = "v6" +} + +resource "junos_routing_instance" "testacc_dhcprelay_servergroup" { + name = "testacc_dhcprelay_servergroup" +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_ri" { + name = "testacc_dhcprelay_servergroup_ri" + routing_instance = junos_routing_instance.testacc_dhcprelay_servergroup.name +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_v6_ri" { + name = "testacc_dhcprelay_servergroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcprelay_servergroup.name + version = "v6" +} +` +} + +func testAccJunosForwardingOptionsDhcpRelayServerGroupConfigUpdate() string { + return ` +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup" { + name = "testacc_dhcprelay_servergroup" + ip_address = [ + "192.0.2.8", + ] +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_v6" { + name = "testacc_dhcprelay_servergroup_v6" + version = "v6" + ip_address = [ + "fe80::b", + ] +} + +resource "junos_routing_instance" "testacc_dhcprelay_servergroup" { + name = "testacc_dhcprelay_servergroup" +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_ri" { + name = "testacc_dhcprelay_servergroup_ri" + routing_instance = junos_routing_instance.testacc_dhcprelay_servergroup.name + ip_address = [ + "192.0.2.88", + ] +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_v6_ri" { + name = "testacc_dhcprelay_servergroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcprelay_servergroup.name + version = "v6" + ip_address = [ + "fe80::bb", + ] +} +` +} + +func testAccJunosForwardingOptionsDhcpRelayServerGroupConfigUpdate2() string { + return ` +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup" { + name = "testacc_dhcprelay_servergroup" + ip_address = [ + "fe80::b", + "192.0.2.8", + ] +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_servergroup_v6" { + name = "testacc_dhcprelay_servergroup_v6" + version = "v6" + ip_address = [ + "fe80::b", + "192.0.2.9", + "fe80::a", + ] +} +` +} From aacf584d76e98bc0e25a8bfe0ffc23e0006307ff Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 20 Sep 2022 18:56:46 +0200 Subject: [PATCH 02/17] r/system_services_dhcp_localserver_group: add args,fix restriction - add client_id_exclude_headers argument - add client_id_use_automatic_ascii_hex_encoding argument - remove need to version = v6 when set client_id = true inside authentication_username_include block argument --- CHANGELOG.md | 2 ++ .../system_services_dhcp_localserver_group.md | 9 +++-- ..._system_services_dhcp_localserver_group.go | 33 +++++++++++++++++-- ...em_services_dhcp_localserver_group_test.go | 5 ++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c0aa8c3..23fd991b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ FEATURES: ENHANCEMENTS: +* resource/`junos_system_services_dhcp_localserver_group`: add `client_id_exclude_headers` and `client_id_use_automatic_ascii_hex_encoding` argument and remove need to `version` = `v6` when set `client_id` = `true` inside `authentication_username_include` block argument + BUG FIXES: ## 1.30.1 (September 09, 2022) diff --git a/docs/resources/system_services_dhcp_localserver_group.md b/docs/resources/system_services_dhcp_localserver_group.md index 8dd664ef..d7a12a90 100644 --- a/docs/resources/system_services_dhcp_localserver_group.md +++ b/docs/resources/system_services_dhcp_localserver_group.md @@ -52,8 +52,13 @@ The following arguments are supported: - **circuit_type** (Optional, Boolean) Include circuit type. - **client_id** (Optional, Boolean) - Include client ID. - `version` need to be `v6`. + Include client ID. + - **client_id_exclude_headers** (Optional, Boolean) + Exclude all the headers. + `client_id` need to be true. + - **client_id_use_automatic_ascii_hex_encoding** (Optional, Boolean) + Use automatic ascii hex username encoding. + `client_id` need to be true. - **delimiter** (Optional, String) Change delimiter/separator character. One character maximum. diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index 2788534c..2a88987a 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -111,6 +111,16 @@ func resourceSystemServicesDhcpLocalServerGroup() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "client_id_exclude_headers": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.client_id"}, + }, + "client_id_use_automatic_ascii_hex_encoding": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.client_id"}, + }, "delimiter": { Type: schema.TypeString, Optional: true, @@ -1066,10 +1076,19 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, configSet = append(configSet, setPrefix+"authentication username-include circuit-type") } if authenticationUsernameInclude["client_id"].(bool) { - if d.Get("version").(string) == "v4" { - return fmt.Errorf("client_id not compatible when version = v4") - } configSet = append(configSet, setPrefix+"authentication username-include client-id") + if authenticationUsernameInclude["client_id_exclude_headers"].(bool) { + configSet = append(configSet, + setPrefix+"authentication username-include client-id exclude-headers") + } + if authenticationUsernameInclude["client_id_use_automatic_ascii_hex_encoding"].(bool) { + configSet = append(configSet, + setPrefix+"authentication username-include client-id use-automatic-ascii-hex-encoding") + } + } else if authenticationUsernameInclude["client_id_exclude_headers"].(bool) || + authenticationUsernameInclude["client_id_use_automatic_ascii_hex_encoding"].(bool) { + return fmt.Errorf("authentication_username_include.0.client_id need to be true with " + + "client_id_exclude_headers or client_id_use_automatic_ascii_hex_encoding") } if v := authenticationUsernameInclude["delimiter"].(string); v != "" { configSet = append(configSet, setPrefix+"authentication username-include delimiter \""+v+"\"") @@ -1576,6 +1595,8 @@ func readSystemServicesDhcpLocalServerGroup(name, instance, version string, clt confRead.authenticationUsernameInclude = append(confRead.authenticationUsernameInclude, map[string]interface{}{ "circuit_type": false, "client_id": false, + "client_id_exclude_headers": false, + "client_id_use_automatic_ascii_hex_encoding": false, "delimiter": "", "domain_name": "", "interface_description": "", @@ -1597,6 +1618,12 @@ func readSystemServicesDhcpLocalServerGroup(name, instance, version string, clt switch { case itemTrimAuthUserIncl == "circuit-type": confRead.authenticationUsernameInclude[0]["circuit_type"] = true + case itemTrimAuthUserIncl == "client-id exclude-headers": + confRead.authenticationUsernameInclude[0]["client_id_exclude_headers"] = true + confRead.authenticationUsernameInclude[0]["client_id"] = true + case itemTrimAuthUserIncl == "client-id use-automatic-ascii-hex-encoding": + confRead.authenticationUsernameInclude[0]["client_id_use_automatic_ascii_hex_encoding"] = true + confRead.authenticationUsernameInclude[0]["client_id"] = true case itemTrimAuthUserIncl == "client-id": confRead.authenticationUsernameInclude[0]["client_id"] = true case strings.HasPrefix(itemTrimAuthUserIncl, "delimiter "): diff --git a/junos/resource_system_services_dhcp_localserver_group_test.go b/junos/resource_system_services_dhcp_localserver_group_test.go index bc29bfa9..aacf3fa5 100644 --- a/junos/resource_system_services_dhcp_localserver_group_test.go +++ b/junos/resource_system_services_dhcp_localserver_group_test.go @@ -175,7 +175,10 @@ resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v4_ri routing_instance = junos_routing_instance.testacc_dhcpgroup.name authentication_username_include { - option_82 = true + client_id = true + client_id_exclude_headers = true + client_id_use_automatic_ascii_hex_encoding = true + option_82 = true } dynamic_profile = "junos-default-profile" dynamic_profile_aggregate_clients = true From ed389a5c7aa5323162269eadbb92e00b44327056 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 20 Sep 2022 19:36:25 +0200 Subject: [PATCH 03/17] r/system_services_dhcp_localserver_group: fix mismatch name of func --- ...esource_system_services_dhcp_localserver_group.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index 2a88987a..7fd385d7 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -1261,7 +1261,7 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, if d.Get("version").(string) == "v6" { return fmt.Errorf("overrides_v4 not compatible if version = v6") } - configSetOverrides, err := setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4( + configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV4( v.(map[string]interface{}), setPrefix) if err != nil { return err @@ -1272,7 +1272,7 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, if d.Get("version").(string) == "v4" { return fmt.Errorf("overrides_v6 not compatible if version = v4") } - configSetOverrides, err := setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6( + configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV6( v.(map[string]interface{}), setPrefix) if err != nil { return err @@ -1378,7 +1378,7 @@ func setSystemServicesDhcpLocalServerGroupInterface( if version == "v6" { return configSet, fmt.Errorf("overrides_v4 not compatible if version = v6") } - configSetOverrides, err := setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4( + configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV4( v.(map[string]interface{}), setPrefix) if err != nil { return configSet, err @@ -1389,7 +1389,7 @@ func setSystemServicesDhcpLocalServerGroupInterface( if version == "v4" { return configSet, fmt.Errorf("overrides_v6 not compatible if version = v4") } - configSetOverrides, err := setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6( + configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV6( v.(map[string]interface{}), setPrefix) if err != nil { return configSet, err @@ -1415,7 +1415,7 @@ func setSystemServicesDhcpLocalServerGroupInterface( return configSet, nil } -func setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4(overrides map[string]interface{}, setPrefix string, +func setSystemServicesDhcpLocalServerGroupOverridesV4(overrides map[string]interface{}, setPrefix string, ) ([]string, error) { configSet := make([]string, 0) setPrefix += "overrides " @@ -1479,7 +1479,7 @@ func setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4(overrides map[st return configSet, nil } -func setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6(overrides map[string]interface{}, setPrefix string, +func setSystemServicesDhcpLocalServerGroupOverridesV6(overrides map[string]interface{}, setPrefix string, ) ([]string, error) { configSet := make([]string, 0) setPrefix += "overrides " From 4f945bd9c9a0c4f4336d7812aeebbcaf76654adc Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 20 Sep 2022 19:43:58 +0200 Subject: [PATCH 04/17] r/forwardingoptions_sampling_instance: fix typo in name of func --- junos/provider.go | 2 +- ...rce_forwardingoptions_sampling_instance.go | 108 +++++++++--------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/junos/provider.go b/junos/provider.go index a91fab21..1c1731f1 100644 --- a/junos/provider.go +++ b/junos/provider.go @@ -127,7 +127,7 @@ func Provider() *schema.Provider { "junos_firewall_filter": resourceFirewallFilter(), "junos_firewall_policer": resourceFirewallPolicer(), "junos_forwardingoptions_dhcprelay_servergroup": resourceForwardingOptionsDhcpRelayServerGroup(), - "junos_forwardingoptions_sampling_instance": resourceForwardingoptionsSamplingInstance(), + "junos_forwardingoptions_sampling_instance": resourceForwardingOptionsSamplingInstance(), "junos_generate_route": resourceGenerateRoute(), "junos_group_dual_system": resourceGroupDualSystem(), "junos_igmp_snooping_vlan": resourceIgmpSnoopingVlan(), diff --git a/junos/resource_forwardingoptions_sampling_instance.go b/junos/resource_forwardingoptions_sampling_instance.go index 0f643c39..4db59a3a 100644 --- a/junos/resource_forwardingoptions_sampling_instance.go +++ b/junos/resource_forwardingoptions_sampling_instance.go @@ -24,14 +24,14 @@ type samplingInstanceOptions struct { input []map[string]interface{} } -func resourceForwardingoptionsSamplingInstance() *schema.Resource { +func resourceForwardingOptionsSamplingInstance() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceForwardingoptionsSamplingInstanceCreate, - ReadWithoutTimeout: resourceForwardingoptionsSamplingInstanceRead, - UpdateWithoutTimeout: resourceForwardingoptionsSamplingInstanceUpdate, - DeleteWithoutTimeout: resourceForwardingoptionsSamplingInstanceDelete, + CreateWithoutTimeout: resourceForwardingOptionsSamplingInstanceCreate, + ReadWithoutTimeout: resourceForwardingOptionsSamplingInstanceRead, + UpdateWithoutTimeout: resourceForwardingOptionsSamplingInstanceUpdate, + DeleteWithoutTimeout: resourceForwardingOptionsSamplingInstanceDelete, Importer: &schema.ResourceImporter{ - StateContext: resourceForwardingoptionsSamplingInstanceImport, + StateContext: resourceForwardingOptionsSamplingInstanceImport, }, Schema: map[string]*schema.Schema{ "name": { @@ -627,11 +627,11 @@ func resourceForwardingoptionsSamplingInstance() *schema.Resource { } } -func resourceForwardingoptionsSamplingInstanceCreate(ctx context.Context, d *schema.ResourceData, m interface{}, +func resourceForwardingOptionsSamplingInstanceCreate(ctx context.Context, d *schema.ResourceData, m interface{}, ) diag.Diagnostics { clt := m.(*Client) if clt.fakeCreateSetFile != "" { - if err := setForwardingoptionsSamplingInstance(d, clt, nil); err != nil { + if err := setForwardingOptionsSamplingInstance(d, clt, nil); err != nil { return diag.FromErr(err) } d.SetId(d.Get("name").(string)) @@ -647,7 +647,7 @@ func resourceForwardingoptionsSamplingInstanceCreate(ctx context.Context, d *sch return diag.FromErr(err) } var diagWarns diag.Diagnostics - fwdoptsSamplingInstanceExists, err := checkForwardingoptionsSamplingInstanceExists( + fwdoptsSamplingInstanceExists, err := checkForwardingOptionsSamplingInstanceExists( d.Get("name").(string), clt, junSess) if err != nil { @@ -662,7 +662,7 @@ func resourceForwardingoptionsSamplingInstanceCreate(ctx context.Context, d *sch diag.FromErr(fmt.Errorf("forwarding-options sampling instance %v already exists", d.Get("name").(string)))...) } - if err := setForwardingoptionsSamplingInstance(d, clt, junSess); err != nil { + if err := setForwardingOptionsSamplingInstance(d, clt, junSess); err != nil { appendDiagWarns(&diagWarns, clt.configClear(junSess)) return append(diagWarns, diag.FromErr(err)...) @@ -675,7 +675,7 @@ func resourceForwardingoptionsSamplingInstanceCreate(ctx context.Context, d *sch return append(diagWarns, diag.FromErr(err)...) } - fwdoptsSamplingInstanceExists, err = checkForwardingoptionsSamplingInstanceExists( + fwdoptsSamplingInstanceExists, err = checkForwardingOptionsSamplingInstanceExists( d.Get("name").(string), clt, junSess) if err != nil { @@ -688,10 +688,10 @@ func resourceForwardingoptionsSamplingInstanceCreate(ctx context.Context, d *sch "=> check your config", d.Get("name").(string)))...) } - return append(diagWarns, resourceForwardingoptionsSamplingInstanceReadWJunSess(d, clt, junSess)...) + return append(diagWarns, resourceForwardingOptionsSamplingInstanceReadWJunSess(d, clt, junSess)...) } -func resourceForwardingoptionsSamplingInstanceRead(ctx context.Context, d *schema.ResourceData, m interface{}, +func resourceForwardingOptionsSamplingInstanceRead(ctx context.Context, d *schema.ResourceData, m interface{}, ) diag.Diagnostics { clt := m.(*Client) junSess, err := clt.startNewSession(ctx) @@ -700,14 +700,14 @@ func resourceForwardingoptionsSamplingInstanceRead(ctx context.Context, d *schem } defer clt.closeSession(junSess) - return resourceForwardingoptionsSamplingInstanceReadWJunSess(d, clt, junSess) + return resourceForwardingOptionsSamplingInstanceReadWJunSess(d, clt, junSess) } -func resourceForwardingoptionsSamplingInstanceReadWJunSess( +func resourceForwardingOptionsSamplingInstanceReadWJunSess( d *schema.ResourceData, clt *Client, junSess *junosSession, ) diag.Diagnostics { mutex.Lock() - samplingInstanceOptions, err := readForwardingoptionsSamplingInstance(d.Get("name").(string), clt, junSess) + samplingInstanceOptions, err := readForwardingOptionsSamplingInstance(d.Get("name").(string), clt, junSess) mutex.Unlock() if err != nil { return diag.FromErr(err) @@ -715,21 +715,21 @@ func resourceForwardingoptionsSamplingInstanceReadWJunSess( if samplingInstanceOptions.name == "" { d.SetId("") } else { - fillForwardingoptionsSamplingInstanceData(d, samplingInstanceOptions) + fillForwardingOptionsSamplingInstanceData(d, samplingInstanceOptions) } return nil } -func resourceForwardingoptionsSamplingInstanceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}, +func resourceForwardingOptionsSamplingInstanceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}, ) diag.Diagnostics { d.Partial(true) clt := m.(*Client) if clt.fakeUpdateAlso { - if err := delForwardingoptionsSamplingInstance(d.Get("name").(string), clt, nil); err != nil { + if err := delForwardingOptionsSamplingInstance(d.Get("name").(string), clt, nil); err != nil { return diag.FromErr(err) } - if err := setForwardingoptionsSamplingInstance(d, clt, nil); err != nil { + if err := setForwardingOptionsSamplingInstance(d, clt, nil); err != nil { return diag.FromErr(err) } d.Partial(false) @@ -745,12 +745,12 @@ func resourceForwardingoptionsSamplingInstanceUpdate(ctx context.Context, d *sch return diag.FromErr(err) } var diagWarns diag.Diagnostics - if err := delForwardingoptionsSamplingInstance(d.Get("name").(string), clt, junSess); err != nil { + if err := delForwardingOptionsSamplingInstance(d.Get("name").(string), clt, junSess); err != nil { appendDiagWarns(&diagWarns, clt.configClear(junSess)) return append(diagWarns, diag.FromErr(err)...) } - if err := setForwardingoptionsSamplingInstance(d, clt, junSess); err != nil { + if err := setForwardingOptionsSamplingInstance(d, clt, junSess); err != nil { appendDiagWarns(&diagWarns, clt.configClear(junSess)) return append(diagWarns, diag.FromErr(err)...) @@ -765,14 +765,14 @@ func resourceForwardingoptionsSamplingInstanceUpdate(ctx context.Context, d *sch } d.Partial(false) - return append(diagWarns, resourceForwardingoptionsSamplingInstanceReadWJunSess(d, clt, junSess)...) + return append(diagWarns, resourceForwardingOptionsSamplingInstanceReadWJunSess(d, clt, junSess)...) } -func resourceForwardingoptionsSamplingInstanceDelete(ctx context.Context, d *schema.ResourceData, m interface{}, +func resourceForwardingOptionsSamplingInstanceDelete(ctx context.Context, d *schema.ResourceData, m interface{}, ) diag.Diagnostics { clt := m.(*Client) if clt.fakeDeleteAlso { - if err := delForwardingoptionsSamplingInstance(d.Get("name").(string), clt, nil); err != nil { + if err := delForwardingOptionsSamplingInstance(d.Get("name").(string), clt, nil); err != nil { return diag.FromErr(err) } @@ -787,7 +787,7 @@ func resourceForwardingoptionsSamplingInstanceDelete(ctx context.Context, d *sch return diag.FromErr(err) } var diagWarns diag.Diagnostics - if err := delForwardingoptionsSamplingInstance(d.Get("name").(string), clt, junSess); err != nil { + if err := delForwardingOptionsSamplingInstance(d.Get("name").(string), clt, junSess); err != nil { appendDiagWarns(&diagWarns, clt.configClear(junSess)) return append(diagWarns, diag.FromErr(err)...) @@ -803,7 +803,7 @@ func resourceForwardingoptionsSamplingInstanceDelete(ctx context.Context, d *sch return diagWarns } -func resourceForwardingoptionsSamplingInstanceImport(ctx context.Context, d *schema.ResourceData, m interface{}, +func resourceForwardingOptionsSamplingInstanceImport(ctx context.Context, d *schema.ResourceData, m interface{}, ) ([]*schema.ResourceData, error) { clt := m.(*Client) junSess, err := clt.startNewSession(ctx) @@ -813,25 +813,25 @@ func resourceForwardingoptionsSamplingInstanceImport(ctx context.Context, d *sch defer clt.closeSession(junSess) result := make([]*schema.ResourceData, 1) - fwdoptsSamplingInstanceExists, err := checkForwardingoptionsSamplingInstanceExists(d.Id(), clt, junSess) + fwdoptsSamplingInstanceExists, err := checkForwardingOptionsSamplingInstanceExists(d.Id(), clt, junSess) if err != nil { return nil, err } if !fwdoptsSamplingInstanceExists { return nil, fmt.Errorf("don't find forwarding-options sampling instance with id '%v' (id must be )", d.Id()) } - samplingInstanceOptions, err := readForwardingoptionsSamplingInstance(d.Id(), clt, junSess) + samplingInstanceOptions, err := readForwardingOptionsSamplingInstance(d.Id(), clt, junSess) if err != nil { return nil, err } - fillForwardingoptionsSamplingInstanceData(d, samplingInstanceOptions) + fillForwardingOptionsSamplingInstanceData(d, samplingInstanceOptions) result[0] = d return result, nil } -func checkForwardingoptionsSamplingInstanceExists(name string, clt *Client, junSess *junosSession, +func checkForwardingOptionsSamplingInstanceExists(name string, clt *Client, junSess *junosSession, ) (bool, error) { showConfig, err := clt.command(cmdShowConfig+ "forwarding-options sampling instance \""+name+"\""+pipeDisplaySet, junSess) @@ -845,7 +845,7 @@ func checkForwardingoptionsSamplingInstanceExists(name string, clt *Client, junS return true, nil } -func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, junSess *junosSession) error { +func setForwardingOptionsSamplingInstance(d *schema.ResourceData, clt *Client, junSess *junosSession) error { configSet := make([]string, 0) setPrefix := "set forwarding-options sampling instance \"" + d.Get("name").(string) + "\" " @@ -853,7 +853,7 @@ func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, j configSet = append(configSet, setPrefix+"disable") } for _, v := range d.Get("family_inet_input").([]interface{}) { - if err := setForwardingoptionsSamplingInstanceInput(setPrefix, + if err := setForwardingOptionsSamplingInstanceInput(setPrefix, v.(map[string]interface{}), inetW, clt, junSess); err != nil { return err } @@ -862,13 +862,13 @@ func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, j if v == nil { return fmt.Errorf("family_inet_output block is empty") } - if err := setForwardingoptionsSamplingInstanceOutput(setPrefix, + if err := setForwardingOptionsSamplingInstanceOutput(setPrefix, v.(map[string]interface{}), inetW, clt, junSess); err != nil { return err } } for _, v := range d.Get("family_inet6_input").([]interface{}) { - if err := setForwardingoptionsSamplingInstanceInput(setPrefix, + if err := setForwardingOptionsSamplingInstanceInput(setPrefix, v.(map[string]interface{}), inet6W, clt, junSess); err != nil { return err } @@ -877,13 +877,13 @@ func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, j if v == nil { return fmt.Errorf("family_inet6_output block is empty") } - if err := setForwardingoptionsSamplingInstanceOutput(setPrefix, + if err := setForwardingOptionsSamplingInstanceOutput(setPrefix, v.(map[string]interface{}), inet6W, clt, junSess); err != nil { return err } } for _, v := range d.Get("family_mpls_input").([]interface{}) { - if err := setForwardingoptionsSamplingInstanceInput(setPrefix, + if err := setForwardingOptionsSamplingInstanceInput(setPrefix, v.(map[string]interface{}), mplsW, clt, junSess); err != nil { return err } @@ -892,13 +892,13 @@ func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, j if v == nil { return fmt.Errorf("family_mpls_output block is empty") } - if err := setForwardingoptionsSamplingInstanceOutput(setPrefix, + if err := setForwardingOptionsSamplingInstanceOutput(setPrefix, v.(map[string]interface{}), mplsW, clt, junSess); err != nil { return err } } for _, v := range d.Get("input").([]interface{}) { - if err := setForwardingoptionsSamplingInstanceInput(setPrefix, + if err := setForwardingOptionsSamplingInstanceInput(setPrefix, v.(map[string]interface{}), "", clt, junSess); err != nil { return err } @@ -907,7 +907,7 @@ func setForwardingoptionsSamplingInstance(d *schema.ResourceData, clt *Client, j return clt.configSet(configSet, junSess) } -func setForwardingoptionsSamplingInstanceInput( +func setForwardingOptionsSamplingInstanceInput( setPrefix string, input map[string]interface{}, family string, clt *Client, junSess *junosSession, ) error { configSet := make([]string, 0) @@ -949,7 +949,7 @@ func setForwardingoptionsSamplingInstanceInput( return clt.configSet(configSet, junSess) } -func setForwardingoptionsSamplingInstanceOutput( +func setForwardingOptionsSamplingInstanceOutput( setPrefix string, output map[string]interface{}, family string, clt *Client, junSess *junosSession, ) error { configSet := make([]string, 0) @@ -961,7 +961,7 @@ func setForwardingoptionsSamplingInstanceOutput( case mplsW: setPrefix += "family mpls output " default: - return fmt.Errorf("internal error: setForwardingoptionsSamplingInstanceOutput call with bad family") + return fmt.Errorf("internal error: setForwardingOptionsSamplingInstanceOutput call with bad family") } if v := output["aggregate_export_interval"].(int); v != 0 { configSet = append(configSet, setPrefix+"aggregate-export-interval "+strconv.Itoa(v)) @@ -1069,7 +1069,7 @@ func setForwardingoptionsSamplingInstanceOutput( return clt.configSet(configSet, junSess) } -func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *junosSession, +func readForwardingOptionsSamplingInstance(name string, clt *Client, junSess *junosSession, ) (samplingInstanceOptions, error) { var confRead samplingInstanceOptions @@ -1100,7 +1100,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "run_length": -1, }) } - if err := readForwardingoptionsSamplingInstanceInput(confRead.familyInetInput[0], + if err := readForwardingOptionsSamplingInstanceInput(confRead.familyInetInput[0], strings.TrimPrefix(itemTrim, "family inet input ")); err != nil { return confRead, err } @@ -1113,7 +1113,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "run_length": -1, }) } - if err := readForwardingoptionsSamplingInstanceInput(confRead.familyInet6Input[0], + if err := readForwardingOptionsSamplingInstanceInput(confRead.familyInet6Input[0], strings.TrimPrefix(itemTrim, "family inet6 input ")); err != nil { return confRead, err } @@ -1126,7 +1126,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "run_length": -1, }) } - if err := readForwardingoptionsSamplingInstanceInput(confRead.familyMplsInput[0], + if err := readForwardingOptionsSamplingInstanceInput(confRead.familyMplsInput[0], strings.TrimPrefix(itemTrim, "family mpls input ")); err != nil { return confRead, err } @@ -1139,7 +1139,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "run_length": -1, }) } - if err := readForwardingoptionsSamplingInstanceInput(confRead.input[0], + if err := readForwardingOptionsSamplingInstanceInput(confRead.input[0], strings.TrimPrefix(itemTrim, "input ")); err != nil { return confRead, err } @@ -1156,7 +1156,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "interface": make([]map[string]interface{}, 0), }) } - if err := readForwardingoptionsSamplingInstanceOutput(confRead.familyInetOutput[0], + if err := readForwardingOptionsSamplingInstanceOutput(confRead.familyInetOutput[0], strings.TrimPrefix(itemTrim, "family inet output "), inetW); err != nil { return confRead, err } @@ -1173,7 +1173,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "interface": make([]map[string]interface{}, 0), }) } - if err := readForwardingoptionsSamplingInstanceOutput(confRead.familyInet6Output[0], + if err := readForwardingOptionsSamplingInstanceOutput(confRead.familyInet6Output[0], strings.TrimPrefix(itemTrim, "family inet6 output "), inet6W); err != nil { return confRead, err } @@ -1189,7 +1189,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju "interface": make([]map[string]interface{}, 0), }) } - if err := readForwardingoptionsSamplingInstanceOutput(confRead.familyMplsOutput[0], + if err := readForwardingOptionsSamplingInstanceOutput(confRead.familyMplsOutput[0], strings.TrimPrefix(itemTrim, "family mpls output "), mplsW); err != nil { return confRead, err } @@ -1200,7 +1200,7 @@ func readForwardingoptionsSamplingInstance(name string, clt *Client, junSess *ju return confRead, nil } -func readForwardingoptionsSamplingInstanceInput(inputRead map[string]interface{}, itemTrim string) error { +func readForwardingOptionsSamplingInstanceInput(inputRead map[string]interface{}, itemTrim string) error { switch { case strings.HasPrefix(itemTrim, "max-packets-per-second "): var err error @@ -1231,7 +1231,7 @@ func readForwardingoptionsSamplingInstanceInput(inputRead map[string]interface{} return nil } -func readForwardingoptionsSamplingInstanceOutput(outputRead map[string]interface{}, itemTrim, family string) error { +func readForwardingOptionsSamplingInstanceOutput(outputRead map[string]interface{}, itemTrim, family string) error { switch { case strings.HasPrefix(itemTrim, "aggregate-export-interval "): var err error @@ -1375,13 +1375,13 @@ func readForwardingoptionsSamplingInstanceOutput(outputRead map[string]interface return nil } -func delForwardingoptionsSamplingInstance(samplingInstance string, clt *Client, junSess *junosSession) error { +func delForwardingOptionsSamplingInstance(samplingInstance string, clt *Client, junSess *junosSession) error { configSet := []string{"delete forwarding-options sampling instance \"" + samplingInstance + "\""} return clt.configSet(configSet, junSess) } -func fillForwardingoptionsSamplingInstanceData( +func fillForwardingOptionsSamplingInstanceData( d *schema.ResourceData, samplingInstanceOptions samplingInstanceOptions, ) { if tfErr := d.Set("name", samplingInstanceOptions.name); tfErr != nil { From cf415d0dd4e04fb13b04cb7b4ad969dd9a55e88d Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Sat, 24 Sep 2022 18:50:05 +0200 Subject: [PATCH 05/17] r/system_services_dhcp_localserver_group: fix crash with empty overrides_v4 or overrides_v6 block and enhance error messages --- .golangci.yml | 2 +- CHANGELOG.md | 2 + ..._system_services_dhcp_localserver_group.go | 41 ++++++++++++------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3f703bad..3adc6eda 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -40,7 +40,7 @@ linters-settings: ignore-tests: true gocyclo: # minimal code complexity to report, 30 by default - min-complexity: 90 + min-complexity: 100 gofumpt: # Choose whether to use the extra rules. extra-rules: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 23fd991b..a557037b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ENHANCEMENTS: BUG FIXES: +* resource/`junos_system_services_dhcp_localserver_group`: fix crash with empty `overrides_v4` or `overrides_v6` block + ## 1.30.1 (September 09, 2022) BUG FIXES: diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index 7fd385d7..3f83b65b 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -1107,13 +1107,13 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, } if authenticationUsernameInclude["option_60"].(bool) { if d.Get("version").(string) == "v6" { - return fmt.Errorf("option_60 not compatible when version = v6") + return fmt.Errorf("authentication_username_include.0.option_60 not compatible when version = v6") } configSet = append(configSet, setPrefix+"authentication username-include option-60") } if authenticationUsernameInclude["option_82"].(bool) { if d.Get("version").(string) == "v6" { - return fmt.Errorf("option_82 not compatible when version = v6") + return fmt.Errorf("authentication_username_include.0.option_82 not compatible when version = v6") } configSet = append(configSet, setPrefix+"authentication username-include option-82") if authenticationUsernameInclude["option_82_circuit_id"].(bool) { @@ -1129,19 +1129,19 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, } if authenticationUsernameInclude["relay_agent_interface_id"].(bool) { if d.Get("version").(string) == "v4" { - return fmt.Errorf("relay_agent_interface_id not compatible when version = v4") + return fmt.Errorf("authentication_username_include.0.relay_agent_interface_id not compatible when version = v4") } configSet = append(configSet, setPrefix+"authentication username-include relay-agent-interface-id") } if authenticationUsernameInclude["relay_agent_remote_id"].(bool) { if d.Get("version").(string) == "v4" { - return fmt.Errorf("relay_agent_remote_id not compatible when version = v4") + return fmt.Errorf("authentication_username_include.0.relay_agent_remote_id not compatible when version = v4") } configSet = append(configSet, setPrefix+"authentication username-include relay-agent-remote-id") } if authenticationUsernameInclude["relay_agent_subscriber_id"].(bool) { if d.Get("version").(string) == "v4" { - return fmt.Errorf("relay_agent_subscriber_id not compatible when version = v4") + return fmt.Errorf("authentication_username_include.0.relay_agent_subscriber_id not compatible when version = v4") } configSet = append(configSet, setPrefix+"authentication username-include relay-agent-subscriber-id") } @@ -1244,23 +1244,25 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, } } for _, ldmLayer2 := range d.Get("liveness_detection_method_layer2").([]interface{}) { - if ldmLayer2 != nil { - liveDetectMethLayer2 := ldmLayer2.(map[string]interface{}) - setPrefixLDMLayer2 := setPrefix + "liveness-detection method layer2-liveness-detection " - if v := liveDetectMethLayer2["max_consecutive_retries"].(int); v != 0 { - configSet = append(configSet, setPrefixLDMLayer2+"max-consecutive-retries "+strconv.Itoa(v)) - } - if v := liveDetectMethLayer2["transmit_interval"].(int); v != 0 { - configSet = append(configSet, setPrefixLDMLayer2+"transmit-interval "+strconv.Itoa(v)) - } - } else { + if ldmLayer2 == nil { return fmt.Errorf("liveness_detection_method_layer2 block is empty") } + liveDetectMethLayer2 := ldmLayer2.(map[string]interface{}) + setPrefixLDMLayer2 := setPrefix + "liveness-detection method layer2-liveness-detection " + if v := liveDetectMethLayer2["max_consecutive_retries"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"max-consecutive-retries "+strconv.Itoa(v)) + } + if v := liveDetectMethLayer2["transmit_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"transmit-interval "+strconv.Itoa(v)) + } } for _, v := range d.Get("overrides_v4").([]interface{}) { if d.Get("version").(string) == "v6" { return fmt.Errorf("overrides_v4 not compatible if version = v6") } + if v == nil { + return fmt.Errorf("overrides_v4 block is empty") + } configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV4( v.(map[string]interface{}), setPrefix) if err != nil { @@ -1272,6 +1274,9 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, clt *Client, if d.Get("version").(string) == "v4" { return fmt.Errorf("overrides_v6 not compatible if version = v4") } + if v == nil { + return fmt.Errorf("overrides_v6 block is empty") + } configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV6( v.(map[string]interface{}), setPrefix) if err != nil { @@ -1378,6 +1383,9 @@ func setSystemServicesDhcpLocalServerGroupInterface( if version == "v6" { return configSet, fmt.Errorf("overrides_v4 not compatible if version = v6") } + if v == nil { + return configSet, fmt.Errorf("overrides_v4 block in interface %s is empty", interFace["name"].(string)) + } configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV4( v.(map[string]interface{}), setPrefix) if err != nil { @@ -1389,6 +1397,9 @@ func setSystemServicesDhcpLocalServerGroupInterface( if version == "v4" { return configSet, fmt.Errorf("overrides_v6 not compatible if version = v4") } + if v == nil { + return configSet, fmt.Errorf("overrides_v6 block in interface %s is empty", interFace["name"].(string)) + } configSetOverrides, err := setSystemServicesDhcpLocalServerGroupOverridesV6( v.(map[string]interface{}), setPrefix) if err != nil { From 81e00e9c3772a25feb33d374b7755d392fc8eeb2 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 26 Sep 2022 08:48:50 +0200 Subject: [PATCH 06/17] r/system_services_dhcp_localserver_group: accept all in interface.*.name --- CHANGELOG.md | 2 +- docs/resources/system_services_dhcp_localserver_group.md | 2 +- junos/resource_system_services_dhcp_localserver_group.go | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a557037b..a9a9d718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ ENHANCEMENTS: BUG FIXES: -* resource/`junos_system_services_dhcp_localserver_group`: fix crash with empty `overrides_v4` or `overrides_v6` block +* resource/`junos_system_services_dhcp_localserver_group`: fix crash with empty `overrides_v4` or `overrides_v6` block and accept `all` in `interface.*.name` ## 1.30.1 (September 09, 2022) diff --git a/docs/resources/system_services_dhcp_localserver_group.md b/docs/resources/system_services_dhcp_localserver_group.md index d7a12a90..62988409 100644 --- a/docs/resources/system_services_dhcp_localserver_group.md +++ b/docs/resources/system_services_dhcp_localserver_group.md @@ -204,7 +204,7 @@ The following arguments are supported: - **name** (Required, String) Interface name. - Need to be a logical interface. + Need to be a logical interface or `all`. - **access_profile** (Optional, String) Access profile to use for AAA services. - **dynamic_profile** (Optional, String) diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index 3f83b65b..f4bce6f1 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -243,9 +243,12 @@ func resourceSystemServicesDhcpLocalServerGroup() *schema.Resource { Required: true, ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { value := v.(string) + if value == "all" { + return + } if strings.Count(value, ".") != 1 { errors = append(errors, fmt.Errorf( - "%q in %q need to have 1 dot", value, k)) + "%q in %q need to have 1 dot or be 'all'", value, k)) } return From 684a9391cb40117ab710b1cc5e39aff597f4a956 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 26 Sep 2022 08:51:18 +0200 Subject: [PATCH 07/17] r/rip-neighbor: accept all in name --- CHANGELOG.md | 1 + junos/resource_rip_neighbor.go | 5 ++++- junos/resource_rip_neighbor_test.go | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a9d718..70e9eaa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ENHANCEMENTS: BUG FIXES: * resource/`junos_system_services_dhcp_localserver_group`: fix crash with empty `overrides_v4` or `overrides_v6` block and accept `all` in `interface.*.name` +* resource/`junos_rip_neighbor`: accept `all` in `name` ## 1.30.1 (September 09, 2022) diff --git a/junos/resource_rip_neighbor.go b/junos/resource_rip_neighbor.go index 9e574f90..3d84dda3 100644 --- a/junos/resource_rip_neighbor.go +++ b/junos/resource_rip_neighbor.go @@ -56,9 +56,12 @@ func resourceRipNeighbor() *schema.Resource { ForceNew: true, ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { value := v.(string) + if value == "all" { + return + } if strings.Count(value, ".") != 1 { errors = append(errors, fmt.Errorf( - "%q in %q need to have 1 dot", value, k)) + "%q in %q need to have 1 dot or be 'all'", value, k)) } return diff --git a/junos/resource_rip_neighbor_test.go b/junos/resource_rip_neighbor_test.go index 65deda27..28e9f46b 100644 --- a/junos/resource_rip_neighbor_test.go +++ b/junos/resource_rip_neighbor_test.go @@ -84,6 +84,10 @@ resource "junos_rip_neighbor" "testacc_ripngneigh" { ng = true group = junos_rip_group.testacc_ripngneigh.name } +resource "junos_rip_neighbor" "testacc_ripneigh_all" { + name = "all" + group = junos_rip_group.testacc_ripneigh.name +} resource "junos_interface_physical" "testacc_ripneigh2" { name = "%s" } From 9863c2f6d3b3104105632dfbe16bffa8ea9a1cd7 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 29 Sep 2022 13:51:38 +0200 Subject: [PATCH 08/17] r/system_services_dhcp_localserver_group: move min arguments to version argument --- ..._system_services_dhcp_localserver_group.go | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index f4bce6f1..07dc329e 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -71,6 +71,28 @@ func resourceSystemServicesDhcpLocalServerGroup() *schema.Resource { ForceNew: true, Default: "v4", ValidateFunc: validation.StringInSlice([]string{"v4", "v6"}, false), + AtLeastOneOf: []string{ + "access_profile", + "authentication_password", + "authentication_username_include", + "dynamic_profile", + "interface", + "lease_time_validation", + "liveness_detection_failure_action", + "liveness_detection_method_bfd", + "liveness_detection_method_layer2", + "overrides_v4", + "overrides_v6", + "reauthenticate_lease_renewal", + "reauthenticate_remote_id_mismatch", + "reconfigure", + "remote_id_mismatch_disconnect", + "route_suppression_access", + "route_suppression_access_internal", + "route_suppression_destination", + "service_profile", + "short_cycle_protection_lockout_max_time", + }, }, "access_profile": { Type: schema.TypeString, @@ -214,28 +236,7 @@ func resourceSystemServicesDhcpLocalServerGroup() *schema.Resource { "interface": { Type: schema.TypeSet, Optional: true, - AtLeastOneOf: []string{ - "access_profile", - "authentication_password", - "authentication_username_include", - "dynamic_profile", - "interface", - "lease_time_validation", - "liveness_detection_failure_action", - "liveness_detection_method_bfd", - "liveness_detection_method_layer2", - "overrides_v4", - "overrides_v6", - "reauthenticate_lease_renewal", - "reauthenticate_remote_id_mismatch", - "reconfigure", - "remote_id_mismatch_disconnect", - "route_suppression_access", - "route_suppression_access_internal", - "route_suppression_destination", - "service_profile", - "short_cycle_protection_lockout_max_time", - }, + Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { From 428e44339aa96814275974a98278f14e1fffaf4a Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Sat, 1 Oct 2022 19:02:02 +0200 Subject: [PATCH 09/17] r/system_services_dhcp_localserver_group: fix mismatch words in doc --- docs/resources/system_services_dhcp_localserver_group.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/resources/system_services_dhcp_localserver_group.md b/docs/resources/system_services_dhcp_localserver_group.md index 62988409..c3c11085 100644 --- a/docs/resources/system_services_dhcp_localserver_group.md +++ b/docs/resources/system_services_dhcp_localserver_group.md @@ -36,7 +36,7 @@ The following arguments are supported: - **name** (Required, String, Forces new resource) Group name. - **routing_instance** (Optional, String, Forces new resource) - Routing instance for pool. + Routing instance for group. Need to be `default` or name of routing instance. Defaults to `default` - **version** (Optional, String, Forces new resource) @@ -345,9 +345,9 @@ The following attributes are exported: ## Import -Junos aggregate route can be imported using an id made up of +Junos system DHCP local server group can be imported using an id made up of `_-__-_`, e.g. ```shell -$ terraform import junos_system_services_dhcp_localserver_group.demo_dhcp_pool demo_dhcp_group_-_default_-_v4 +$ terraform import junos_system_services_dhcp_localserver_group.demo_dhcp_group demo_dhcp_group_-_default_-_v4 ``` From b3a0882520a98f4064c82d285031bdaceb3540e5 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 3 Oct 2022 08:58:34 +0200 Subject: [PATCH 10/17] r/access_address_asignment_pool: fix mismatch words in doc --- docs/resources/access_address_assignment_pool.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/resources/access_address_assignment_pool.md b/docs/resources/access_address_assignment_pool.md index 2480c9bb..17b31a6a 100644 --- a/docs/resources/access_address_assignment_pool.md +++ b/docs/resources/access_address_assignment_pool.md @@ -233,7 +233,8 @@ The following attributes are exported: ## Import -Junos aggregate route can be imported using an id made up of `_-_`, e.g. +Junos access address-assignment pool can be imported using an id made up of +`_-_`, e.g. ```shell $ terraform import junos_access_address_assignment_pool.demo_dhcp_pool demo_dhcp_pool_-_default From 57ed3a28b817bb663c22f728391e1526d6d327a2 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 4 Oct 2022 13:37:04 +0200 Subject: [PATCH 11/17] add forwardingoptions_dhcprelay_group resource Fix #423 --- CHANGELOG.md | 1 + .../forwardingoptions_dhcprelay_group.md | 569 +++++ ...nc_resource_forwardingoptions_dhcprelay.go | 1842 +++++++++++++++ junos/provider.go | 1 + ...ource_forwardingoptions_dhcprelay_group.go | 1983 +++++++++++++++++ ..._forwardingoptions_dhcprelay_group_test.go | 531 +++++ 6 files changed, 4927 insertions(+) create mode 100644 docs/resources/forwardingoptions_dhcprelay_group.md create mode 100644 junos/func_resource_forwardingoptions_dhcprelay.go create mode 100644 junos/resource_forwardingoptions_dhcprelay_group.go create mode 100644 junos/resource_forwardingoptions_dhcprelay_group_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e9eaa0..25d2d896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: +* add `junos_forwardingoptions_dhcprelay_group` resource (Fixes #423) * add `junos_forwardingoptions_dhcprelay_servergroup` resource (Fixes #423) ENHANCEMENTS: diff --git a/docs/resources/forwardingoptions_dhcprelay_group.md b/docs/resources/forwardingoptions_dhcprelay_group.md new file mode 100644 index 00000000..4f2c4383 --- /dev/null +++ b/docs/resources/forwardingoptions_dhcprelay_group.md @@ -0,0 +1,569 @@ +--- +page_title: "Junos: junos_forwardingoptions_dhcprelay_group" +--- + +# junos_forwardingoptions_dhcprelay_group + +Provides a DHCP (or DHCPv6) relay group. + +## Example Usage + +```hcl +# Add a dhcp-relay group +resource "junos_forwardingoptions_dhcprelay_group" "demo" { + name = "demo" + active_server_group = junos_forwardingoptions_dhcprelay_servergroup.demo.name +} + +resource "junos_forwardingoptions_dhcprelay_servergroup" "demo" { + name = "demo" + ip_address = [ + "192.0.2.8", + ] +} +``` + +## Argument Reference + +-> **Note:** At least one of arguments need to be set +(in addition to `name`, `routing_instance` and `version`). + +The following arguments are supported: + +- **name** (Required, String, Forces new resource) + Group name. +- **routing_instance** (Optional, String, Forces new resource) + Routing instance for group. + Need to be `default` or name of routing instance. + Defaults to `default` +- **version** (Optional, String, Forces new resource) + Version for DHCP or DHCPv6. + Need to be `v4` or `v6`. +- **access_profile** (Optional, String) + Access profile to use for AAA services. +- **active_server_group** (Optional, String) + Name of DHCP server group. +- **active_server_group_allow_server_change** (Optional, Boolean) + Accept DHCP-ACK from any server in this group. + `version` need to be `v4`. +- **authentication_password** (Optional, String) + DHCP authentication, username password to use. +- **authentication_username_include** (Optional, Block) + DHCP authentication, add username options. + At least one of arguments of block need to be set. + - **circuit_type** (Optional, Boolean) + Include circuit type. + - **client_id** (Optional, Boolean) + Include client ID. + - **client_id_exclude_headers** (Optional, Boolean) + Exclude all the headers. + `client_id` need to be true. + - **client_id_use_automatic_ascii_hex_encoding** (Optional, Boolean) + Use automatic ascii hex username encoding. + `client_id` need to be true. + - **delimiter** (Optional, String) + Change delimiter/separator character. + One character maximum. + - **domain_name** (Optional, String) + Add domain name. + - **interface_description** (Optional, String) + Include interface description. + Need to be `device` or `logical`. + - **interface_name** (Optional, Boolean) + Include interface name. + - **mac_address** (Optional, Boolean) + Include MAC address. + - **option_60** (Optional, Boolean) + Include option 60. + `version` need to be `v4`. + - **option_82** (Optional, Boolean) + Include option 82. + `version` need to be `v4`. + - **option_82_circuit_id** (Optional, Boolean) + Include option 82 circuit-id (sub option 1). + `option_82` need to be true. + - **option_82_remote_id** (Optional, Boolean) + Include option 82 remote-id (sub option 2). + `option_82` need to be true. + - **relay_agent_interface_id** (Optional, Boolean) + Include the relay agent interface ID. + `version` need to be `v6`. + - **relay_agent_remote_id** (Optional, Boolean) + Include the relay agent remote ID. + `version` need to be `v6`. + - **relay_agent_subscriber_id** (Optional, Boolean) + Include the relay agent subscriber ID. + `version` need to be `v6`. + - **routing_instance_name** (Optional, Boolean) + Include routing instance name. + - **user_prefix** (Optional, String) + Add user defined prefix. + - **vlan_tags** (Optional, Boolean) + Include the vlan tag(s). +- **client_response_ttl** (Optional, Number) + IP time-to-live value to set in responses to client (1..255). + `version` need to be `v4`. +- **description** (Optional, String) + Description. +- **dynamic_profile** (Optional, String) + Dynamic profile to use. +- **dynamic_profile_aggregate_clients** (Optional, Boolean) + Aggregate client profiles. + `dynamic_profile` need to be set. +- **dynamic_profile_aggregate_clients_action** (Optional, String) + Merge or replace the client dynamic profiles. + Need to be `merge` or `replace`. + `dynamic_profile_aggregate_clients` need to be true. +- **dynamic_profile_use_primary** (Optional, String) + Dynamic profile to use on the primary interface. + `dynamic_profile` need to be set. + Conflict with `dynamic_profile_aggregate_clients`. +- **forward_only** (Optional, Boolean) + Forward DHCP packets without creating binding. +- **forward_only_routing_instance** (Optional, String) + Name of routing instance to forward-only. +- **interface** (Optional, Block Set) + For each name of interface to declare. + - **name** (Required, String) + Interface name. + - **access_profile** (Optional, String) + Access profile to use for AAA services. + - **dynamic_profile** (Optional, String) + Dynamic profile to use. + - **dynamic_profile_aggregate_clients** (Optional, String) + Aggregate client profiles. + `dynamic_profile` need to be set. + - **dynamic_profile_aggregate_clients_action** (Optional, String) + Merge or replace the client dynamic profiles. + Need to be `merge` or `replace`. + `dynamic_profile_aggregate_clients` need to be set. + - **dynamic_profile_use_primary** (Optional, String) + Dynamic profile to use on the primary interface. + `dynamic_profile` need to be set. + Conflict with `dynamic_profile_aggregate_clients`. + - **exclude** (Optional, Boolean) + Exclude this interface range. + - **overrides_v4** (Optional, Block) + DHCP override processing. + `version` need to be `v4`. + See [below for nested schema](#overrides_v4-arguments). + - **overrides_v6** (Optional, Block) + DHCPv6 override processing. + `version` need to be `v6`. + See [below for nested schema](#overrides_v6-arguments). + - **service_profile** (Optional, String) + Dynamic profile to use for default service activation. + - **short_cycle_protection_lockout_max_time** (Optional, Number) + Short cycle lockout max time in seconds (1..86400). + - **short_cycle_protection_lockout_min_time** (Optional, Number) + Short cycle lockout min time in seconds (1..86400). + - **trace** (Optional, Boolean) + Enable tracing for this interface. + - **upto** (Optional, String) + Interface up to. +- **lease_time_validation** (Optional, Block) + Configure lease time violation validation. + - **lease_time_threshold** (Optional, Number) + Threshold for lease time violation seconds (60..2147483647 seconds). + - **violation_action_drop** (Optional, Boolean) + Lease time validation violation action is drop. +- **liveness_detection_failure_action** (Optional, String) + Liveness detection failure action options. + Need to be `clear-binding`, `clear-binding-if-interface-up` or `log-only`. +- **liveness_detection_method_bfd** (Optional, Block) + Liveness detection method BFD options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_layer2`. + - **detection_time_threshold** (Optional, Number) + High detection-time triggering a trap (milliseconds). + - **holddown_interval** (Optional, Number) + Time to hold the session-UP notification to the client (0..255000 milliseconds). + - **minimum_interval** (Optional, Number) + Minimum transmit and receive interval (30000..255000 milliseconds). + - **minimum_receive_interval** (Optional, Number) + Minimum receive interval (30000..255000 milliseconds). + - **multiplier** (Optional, Number) + Detection time multiplier (1..255). + - **no_adaptation** (Optional, Boolean) + Disable adaptation. + - **session_mode** (Optional, String) + BFD single-hop or multihop session-mode. + Need to be `automatic`, `multihop` or `single-hop`. + - **transmit_interval_minimum** (Optional, Number) + Minimum transmit interval (30000..255000 milliseconds). + - **transmit_interval_threshold** (Optional, Number) + High transmit interval triggering a trap (milliseconds) + - **version** (Optional, String) + BFD protocol version number. + Need to be `0`, `1` or `automatic`. +- **liveness_detection_method_layer2** (Optional, Block) + Liveness detection method address resolution options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_bfd`. + - **max_consecutive_retries** (Optional, Number) + Retry attempts (3..6). + - **transmit_interval** (Optional, Number) + Transmit interval for address resolution (300..1800 seconds). +- **maximum_hop_count** (Optional, Number) + Maximum number of hops per packet (1..16) + `version` need to be `v4`. +- **minimum_wait_time** (Optional, Number) + Minimum number of seconds before requests are forwarded (0..30000). + `version` need to be `v4`. +- **overrides_v4** (Optional, Block) + DHCP override processing. + `version` need to be `v4`. + See [below for nested schema](#overrides_v4-arguments). +- **overrides_v6** (Optional, Block) + DHCPv6 override processing. + `version` need to be `v6`. + See [below for nested schema](#overrides_v6-arguments). +- **relay_agent_interface_id** (Optional, Block) + DHCPv6 interface-id option processing. + `version` need to be `v6`. + See [below for nested schema](#relay_agent_interface_id-or-relay_agent_remote_id-arguments). +- **relay_agent_option_79** (Optional, Boolean) + Add the client MAC address to the Relay Forward header. + `version` need to be `v6`. +- **relay_agent_remote_id** (Optional, Block) + DHCPv6 remote-id option processing. + `version` need to be `v6`. + See [below for nested schema](#relay_agent_interface_id-or-relay_agent_remote_id-arguments) + but without `keep_incoming_id_strict`. +- **relay_option** (Optional, Block) + DHCP option processing. + See [below for nested schema](#relay_option-arguments). +- **relay_option_82** (Optional, Block) + DHCP option-82 processing. + `version` need to be `v4`. + See [below for nested schema](#relay_option_82-arguments). +- **remote_id_mismatch_disconnect** (Optional, Boolean) + Disconnect session on remote-id mismatch. +- **route_suppression_access** (Optional, Boolean) + Suppress access route addition. + `version` need to be `v6`. +- **route_suppression_access_internal** (Optional, Boolean) + Suppress access-internal route addition. +- **route_suppression_destination** (Optional, Boolean) + Suppress destination route addition. + `version` need to be `v4`. +- **server_match_address** (Optional, Block Set) + For each `address`, server match processing. + - **address** (Required, String) + Server address. + - **action** (Required, String) + Action on address. + Need to be `create-relay-entry` or `forward-only`. +- **server_match_default_action** (Optional, String) + Server match default action. + Need to be `create-relay-entry` or `forward-only`. +- **server_match_duid** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, match duid processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `create-relay-entry` or `forward-only`. +- **service_profile** (Optional, String) + Dynamic profile to use for default service activation. +- **short_cycle_protection_lockout_max_time** (Optional, Number) + Short cycle lockout max time in seconds (1..86400). + `short_cycle_protection_lockout_min_time` need to be set. +- **short_cycle_protection_lockout_min_time** (Optional, Number) + Short cycle lockout min time in seconds (1..86400). + `short_cycle_protection_lockout_max_time` need to be set. +- **source_ip_change** (Optional, Boolean) + Use address of egress interface as source ip. + `version` need to be `v4`. +- **vendor_specific_information_host_name** (Optional, Boolean) + DHCPv6 option 17 vendor-specific processing, add router host name. + `version` need to be `v6`. +- **vendor_specific_information_location** (Optional, Boolean) + DHCPv6 option 17 vendor-specific processing, + add location information expressed as interface name format. + `version` need to be `v6`. + +--- + +### overrides_v4 arguments + +- **allow_no_end_option** (Optional, Boolean) + Allow packets without end-of-option. +- **allow_snooped_clients** (Optional, Boolean) + Allow client creation from snooped PDUs. +- **always_write_giaddr** (Optional, Boolean) + Overwrite existing 'giaddr' field, when present. +- **always_write_option_82** (Optional, Boolean) + Overwrite existing value of option 82, when present. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **bootp_support** (Optional, Boolean) + Allows relay of bootp req and reply. +- **client_discover_match** (Optional, String) + Use secondary match criteria for DISCOVER PDU. + Need to be `incoming-interface` or `option60-and-option82`. +- **delay_authentication** (Optional, Boolean) + Delay subscriber authentication in DHCP protocol processing until request packet. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on rengotiation. +- **disable_relay** (Optional, Boolean) + Disable DHCP relay processing. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **layer2_unicast_replies** (Optional, Boolean) + Do not broadcast client responses. +- **no_allow_snooped_clients** (Optional, Boolean) + Don't allow client creation from snooped PDUs. +- **no_bind_on_request** (Optional, Boolean) + Do not bind if stray DHCP request is received. +- **no_unicast_replies** (Optional, Boolean) + Overwrite unicast bit in incoming packet, when present. +- **proxy_mode** Optional, Boolean) + Put the relay in proxy mode. +- **relay_source** (Optional, String) + Interface for relay source. +- **replace_ip_source_with_giaddr** (Optional, Boolean) + Replace IP source address in request and release packets. +- **send_release_on_delete** (Optional, Boolean) + Always send RELEASE to the server when a binding is deleted. +- **trust_option_82** (Optional, Boolean) + Trust options-82 option. +- **user_defined_option_82** (Optional, String) + Set user defined description for option-82. + +### overrides_v6 arguments + +- **allow_snooped_clients** (Optional, Boolean) + Allow client creation from snooped PDUs. +- **always_process_option_request_option** (Optional, Boolean) + Always process option even after address allocation failure. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **asymmetric_prefix_lease_time** (Optional, Number) + Use a reduced prefix lease time for the client. In seconds (600..86400 seconds). +- **client_negotiation_match_incoming_interface** (Optional, Boolean) + Use incoming interface match criteria for SOLICIT PDU. +- **delay_authentication** (Optional, Boolean) + Delay subscriber authentication in DHCP protocol processing until request packet. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on rengotiation. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **no_allow_snooped_clients** (Optional, Boolean) + Don't allow client creation from snooped PDUs. +- **no_bind_on_request** (Optional, Boolean) + Do not bind if stray DHCPv6 RENEW, REBIND is received. +- **relay_source** (Optional, String) + Interface for relay source. +- **send_release_on_delete** (Optional, Boolean) + Always send RELEASE to the server when a binding is deleted. + +### relay_agent_interface_id or relay_agent_remote_id arguments + +- **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. +- **keep_incoming_id** (Optional, Boolean) + Keep incoming interface identifier. +- **keep_incoming_id_strict** (Optional, Boolean) + Drop packet if interface identifier not present. + Only on `relay_agent_interface_id` block. +- **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. +- **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. +- **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. +- **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. +- **use_option_82** (Optional, Boolean) + Use option-82 circuit-id for interface-id or remote-id. +- **use_option_82_strict** (Optional, Boolean) + Drop packet if option-82 circuit-id not present. +- **use_vlan_id** (Optional, Boolean) + Use VLAN id instead of name. + +### relay_option arguments + +- **option_15** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 15 processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_15_default_action** (Optional, Block) + Generic option 15 default action. + `version` need to be `v6`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_16** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 16 processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_16_default_action** (Optional, Block) + Generic option 16 default action. + `version` need to be `v6`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_60** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 60 processing. + `version` need to be `v4`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_60_default_action** (Optional, Block) + Generic option 60 default action. + `version` need to be `v4`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_77** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 77 processing. + `version` need to be `v4`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_77_default_action** (Optional, Block) + Generic option 77 default action. + `version` need to be `v4`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_order** (Optional, List of String) + Options precedence order. + Need to be `60` or `77` with `version` = `v4`. + Need to be `15` or `16` with `version` = `v6`. + +### relay_option_82 arguments + +- **circuit_id** (Optional, Block) + Add circuit identifier. + - **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. + - **keep_incoming_circuit_id** (Optional, Boolean) + Keep incoming circuit identifier. + - **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. + - **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. + - **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. + - **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. + - **use_vlan_id** (Optional, Boolean) + Use VLAN id instead of name. + - **user_defined** (Optional, Boolean) + Include user defined string. + - **vlan_id_only** (Optional, Boolean) + Use only VLAN id. +- **exclude_relay_agent_identifier** (Optional, Boolean) + Exclude relay agent identifier from packets to server. +- **link_selection** (Optional, Boolean) + Add link-selection sub-option on packets to server. +- **remote_id** (Optional, Block) + Add remote identifier. + - **hostname_only** (Optional, Boolean) + Include hostname only. + - **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. + - **keep_incoming_remote_id** (Optional, Boolean) + Keep incoming remote identifier. + - **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. + - **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. + - **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. + - **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. + - **use_string** (Optional, String) + Use raw string instead of the default remote id. + - **use_vlan_id** Optional, String) + Use VLAN id instead of name. +- **server_id_override** (Optional, Boolean) + Add link-selection and server-id sub-options on packets to server. +- **vendor_specific_host_name** (Optional, Boolean) + Add vendor-specific information, add router host name. +- **vendor_specific_location** (Optional, Boolean) + Add vendor-specific information, add location information expressed as interface name format. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format `_-__-_`. + +## Import + +Junos forwarding-options dhcp-relay group can be imported using an id made up of +`_-__-_`, e.g. + +```shell +$ terraform import junos_forwardingoptions_dhcprelay_group.demo demo_-_default_-_v4 +``` diff --git a/junos/func_resource_forwardingoptions_dhcprelay.go b/junos/func_resource_forwardingoptions_dhcprelay.go new file mode 100644 index 00000000..c1731b8f --- /dev/null +++ b/junos/func_resource_forwardingoptions_dhcprelay.go @@ -0,0 +1,1842 @@ +package junos + +import ( + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func schemaForwardingOptionsDhcpRelayAuthUsernameInclude() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "circuit_type": { + Type: schema.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "authentication_username_include.0.circuit_type", + "authentication_username_include.0.client_id", + "authentication_username_include.0.delimiter", + "authentication_username_include.0.domain_name", + "authentication_username_include.0.interface_description", + "authentication_username_include.0.interface_name", + "authentication_username_include.0.mac_address", + "authentication_username_include.0.option_60", + "authentication_username_include.0.option_82", + "authentication_username_include.0.relay_agent_interface_id", + "authentication_username_include.0.relay_agent_remote_id", + "authentication_username_include.0.relay_agent_subscriber_id", + "authentication_username_include.0.routing_instance_name", + "authentication_username_include.0.user_prefix", + "authentication_username_include.0.vlan_tags", + }, + }, + "client_id": { + Type: schema.TypeBool, + Optional: true, + }, + "client_id_exclude_headers": { + Type: schema.TypeBool, + Optional: true, + }, + "client_id_use_automatic_ascii_hex_encoding": { + Type: schema.TypeBool, + Optional: true, + }, + "delimiter": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1), + }, + "domain_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "interface_name": { + Type: schema.TypeBool, + Optional: true, + }, + "mac_address": { + Type: schema.TypeBool, + Optional: true, + }, + "option_60": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "option_82": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "option_82_circuit_id": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.option_82"}, + }, + "option_82_remote_id": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.option_82"}, + }, + "relay_agent_interface_id": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_remote_id": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_subscriber_id": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "user_prefix": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "vlan_tags": { + Type: schema.TypeBool, + Optional: true, + }, + } +} + +func schemaForwardingOptionsDhcpRelayOverridesV4() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "allow_no_end_option": { + Type: schema.TypeBool, + Optional: true, + }, + "allow_snooped_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "always_write_giaddr": { + Type: schema.TypeBool, + Optional: true, + }, + "always_write_option_82": { + Type: schema.TypeBool, + Optional: true, + }, + "asymmetric_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "bootp_support": { + Type: schema.TypeBool, + Optional: true, + }, + "client_discover_match": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"incoming-interface", "option60-and-option82"}, false), + }, + "delay_authentication": { + Type: schema.TypeBool, + Optional: true, + }, + "delete_binding_on_renegotiation": { + Type: schema.TypeBool, + Optional: true, + }, + "disable_relay": { + Type: schema.TypeBool, + Optional: true, + }, + "dual_stack": { + Type: schema.TypeString, + Optional: true, + }, + "interface_client_limit": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 500000), + }, + "layer2_unicast_replies": { + Type: schema.TypeBool, + Optional: true, + }, + "no_allow_snooped_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "no_bind_on_request": { + Type: schema.TypeBool, + Optional: true, + }, + "no_unicast_replies": { + Type: schema.TypeBool, + Optional: true, + }, + "proxy_mode": { + Type: schema.TypeBool, + Optional: true, + }, + "relay_source": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot", value, k)) + } + + return + }, + }, + "replace_ip_source_with_giaddr": { + Type: schema.TypeBool, + Optional: true, + }, + "send_release_on_delete": { + Type: schema.TypeBool, + Optional: true, + }, + "trust_option_82": { + Type: schema.TypeBool, + Optional: true, + }, + "user_defined_option_82": { + Type: schema.TypeString, + Optional: true, + }, + } +} + +func schemaForwardingOptionsDhcpRelayOverridesV6() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "allow_snooped_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "always_process_option_request_option": { + Type: schema.TypeBool, + Optional: true, + }, + "asymmetric_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "asymmetric_prefix_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "client_negotiation_match_incoming_interface": { + Type: schema.TypeBool, + Optional: true, + }, + "delay_authentication": { + Type: schema.TypeBool, + Optional: true, + }, + "delete_binding_on_renegotiation": { + Type: schema.TypeBool, + Optional: true, + }, + "dual_stack": { + Type: schema.TypeString, + Optional: true, + }, + "interface_client_limit": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 500000), + }, + "no_allow_snooped_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "no_bind_on_request": { + Type: schema.TypeBool, + Optional: true, + }, + "relay_source": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot", value, k)) + } + + return + }, + }, + "send_release_on_delete": { + Type: schema.TypeBool, + Optional: true, + }, + } +} + +func schemaForwardingOptionsDhcpRelayAgentID(keepIncomingIDStrict bool) map[string]*schema.Schema { + r := map[string]*schema.Schema{ + "include_irb_and_l2": { + Type: schema.TypeBool, + Optional: true, + }, + "keep_incoming_id": { + Type: schema.TypeBool, + Optional: true, + }, + "no_vlan_interface_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_host_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "use_interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "use_option_82": { + Type: schema.TypeBool, + Optional: true, + }, + "use_option_82_strict": { + Type: schema.TypeBool, + Optional: true, + }, + "use_vlan_id": { + Type: schema.TypeBool, + Optional: true, + }, + } + if keepIncomingIDStrict { + r["keep_incoming_id_strict"] = &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + } + } + + return r +} + +func schemaForwardingOptionsDhcpRelayOption() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "option_15": { // only dhcpv6 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"drop", "forward-only", "relay-server-group"}, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_15_default_action": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"drop", "forward-only", "relay-server-group"}, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_16": { // only dhcpv6 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"drop", "forward-only", "relay-server-group"}, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_16_default_action": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"drop", "forward-only", "relay-server-group"}, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_60": { // only dhcpv4 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "drop", + "forward-only", + "local-server-group", + "relay-server-group", + }, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_60_default_action": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "drop", + "forward-only", + "local-server-group", + "relay-server-group", + }, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_77": { // only dhcpv4 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "drop", + "forward-only", + "local-server-group", + "relay-server-group", + }, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_77_default_action": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "drop", + "forward-only", + "local-server-group", + "relay-server-group", + }, false), + }, + "group": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "option_order": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"15", "16", "60", "77"}, false), + }, + }, + } +} + +func schemaForwardingOptionsDhcpRelayOption82() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "circuit_id": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "include_irb_and_l2": { + Type: schema.TypeBool, + Optional: true, + }, + "keep_incoming_circuit_id": { + Type: schema.TypeBool, + Optional: true, + }, + "no_vlan_interface_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_host_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "use_interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "use_vlan_id": { + Type: schema.TypeBool, + Optional: true, + }, + "user_defined": { + Type: schema.TypeBool, + Optional: true, + }, + "vlan_id_only": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "exclude_relay_agent_identifier": { + Type: schema.TypeBool, + Optional: true, + }, + "link_selection": { + Type: schema.TypeBool, + Optional: true, + }, + "remote_id": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hostname_only": { + Type: schema.TypeBool, + Optional: true, + }, + "include_irb_and_l2": { + Type: schema.TypeBool, + Optional: true, + }, + "keep_incoming_remote_id": { + Type: schema.TypeBool, + Optional: true, + }, + "no_vlan_interface_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_host_name": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "use_interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "use_string": { + Type: schema.TypeString, + Optional: true, + }, + "use_vlan_id": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "server_id_override": { + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_host_name": { + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_location": { + Type: schema.TypeBool, + Optional: true, + }, + } +} + +func setForwardingOptionsDhcpRelayAuthUsernameInclude( + authenticationUsernameInclude map[string]interface{}, setPrefixSrc, version string, +) ([]string, error) { + configSet := make([]string, 0) + + setPrefix := setPrefixSrc + "authentication username-include " + if authenticationUsernameInclude["circuit_type"].(bool) { + configSet = append(configSet, setPrefix+"circuit-type") + } + if authenticationUsernameInclude["client_id"].(bool) { + configSet = append(configSet, setPrefix+"client-id") + if authenticationUsernameInclude["client_id_exclude_headers"].(bool) { + configSet = append(configSet, setPrefix+"client-id exclude-headers") + } + if authenticationUsernameInclude["client_id_use_automatic_ascii_hex_encoding"].(bool) { + configSet = append(configSet, setPrefix+"client-id use-automatic-ascii-hex-encoding") + } + } else if authenticationUsernameInclude["client_id_exclude_headers"].(bool) || + authenticationUsernameInclude["client_id_use_automatic_ascii_hex_encoding"].(bool) { + return configSet, fmt.Errorf("authentication_username_include.0.client_id need to be true with " + + "client_id_exclude_headers or client_id_use_automatic_ascii_hex_encoding") + } + if v := authenticationUsernameInclude["delimiter"].(string); v != "" { + configSet = append(configSet, setPrefix+"delimiter \""+v+"\"") + } + if v := authenticationUsernameInclude["domain_name"].(string); v != "" { + configSet = append(configSet, setPrefix+"domain-name \""+v+"\"") + } + if v := authenticationUsernameInclude["interface_description"].(string); v != "" { + configSet = append(configSet, setPrefix+"interface-description "+v) + } + if authenticationUsernameInclude["interface_name"].(bool) { + configSet = append(configSet, setPrefix+"interface-name") + } + if authenticationUsernameInclude["mac_address"].(bool) { + configSet = append(configSet, setPrefix+"mac-address") + } + if authenticationUsernameInclude["option_60"].(bool) { + if version == "v6" { + return configSet, fmt.Errorf("authentication_username_include.0.option_60 not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"option-60") + } + if authenticationUsernameInclude["option_82"].(bool) { + if version == "v6" { + return configSet, fmt.Errorf("authentication_username_include.0.option_82 not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"option-82") + if authenticationUsernameInclude["option_82_circuit_id"].(bool) { + configSet = append(configSet, setPrefix+"option-82 circuit-id") + } + if authenticationUsernameInclude["option_82_remote_id"].(bool) { + configSet = append(configSet, setPrefix+"option-82 remote-id") + } + } else if authenticationUsernameInclude["option_82_circuit_id"].(bool) || + authenticationUsernameInclude["option_82_remote_id"].(bool) { + return configSet, fmt.Errorf("authentication_username_include.0.option_82 need to be true with " + + "option_82_circuit_id or option_82_remote_id") + } + if authenticationUsernameInclude["relay_agent_interface_id"].(bool) { + if version == "v4" { + return configSet, fmt.Errorf("authentication_username_include.0.relay_agent_interface_id" + + " not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-interface-id") + } + if authenticationUsernameInclude["relay_agent_remote_id"].(bool) { + if version == "v4" { + return configSet, fmt.Errorf("authentication_username_include.0.relay_agent_remote_id" + + " not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-remote-id") + } + if authenticationUsernameInclude["relay_agent_subscriber_id"].(bool) { + if version == "v4" { + return configSet, fmt.Errorf("authentication_username_include.0.relay_agent_subscriber_id" + + " not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-subscriber-id") + } + if authenticationUsernameInclude["routing_instance_name"].(bool) { + configSet = append(configSet, setPrefix+"routing-instance-name") + } + if v := authenticationUsernameInclude["user_prefix"].(string); v != "" { + configSet = append(configSet, setPrefix+"user-prefix \""+v+"\"") + } + if authenticationUsernameInclude["vlan_tags"].(bool) { + configSet = append(configSet, setPrefix+"vlan-tags") + } + + return configSet, nil +} + +func setForwardingOptionsDhcpRelayOverridesV4(overrides map[string]interface{}, setPrefix string, +) ([]string, error) { + configSet := make([]string, 0) + setPrefix += "overrides " + + if overrides["allow_no_end_option"].(bool) { + configSet = append(configSet, setPrefix+"allow-no-end-option") + } + if overrides["allow_snooped_clients"].(bool) { + configSet = append(configSet, setPrefix+"allow-snooped-clients") + } + if overrides["always_write_giaddr"].(bool) { + configSet = append(configSet, setPrefix+"always-write-giaddr") + } + if overrides["always_write_option_82"].(bool) { + configSet = append(configSet, setPrefix+"always-write-option-82") + } + if v := overrides["asymmetric_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-lease-time "+strconv.Itoa(v)) + } + if overrides["bootp_support"].(bool) { + configSet = append(configSet, setPrefix+"bootp-support") + } + if v := overrides["client_discover_match"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-discover-match "+v) + } + if overrides["delay_authentication"].(bool) { + configSet = append(configSet, setPrefix+"delay-authentication") + } + if overrides["delete_binding_on_renegotiation"].(bool) { + configSet = append(configSet, setPrefix+"delete-binding-on-renegotiation") + } + if overrides["disable_relay"].(bool) { + configSet = append(configSet, setPrefix+"disable-relay") + } + if v := overrides["dual_stack"].(string); v != "" { + configSet = append(configSet, setPrefix+"dual-stack \""+v+"\"") + } + if v := overrides["interface_client_limit"].(int); v != 0 { + configSet = append(configSet, setPrefix+"interface-client-limit "+strconv.Itoa(v)) + } + if overrides["layer2_unicast_replies"].(bool) { + configSet = append(configSet, setPrefix+"layer2-unicast-replies") + } + if overrides["no_allow_snooped_clients"].(bool) { + if overrides["allow_snooped_clients"].(bool) { + return configSet, fmt.Errorf("allow_snooped_clients and no_allow_snooped_clients can't be true in same time") + } + configSet = append(configSet, setPrefix+"no-allow-snooped-clients") + } + if overrides["no_bind_on_request"].(bool) { + configSet = append(configSet, setPrefix+"no-bind-on-request") + } + if overrides["no_unicast_replies"].(bool) { + if overrides["layer2_unicast_replies"].(bool) { + return configSet, fmt.Errorf("layer2_unicast_replies and no_unicast_replies can't be true in same time") + } + configSet = append(configSet, setPrefix+"no-unicast-replies") + } + if overrides["proxy_mode"].(bool) { + configSet = append(configSet, setPrefix+"proxy-mode") + } + if v := overrides["relay_source"].(string); v != "" { + configSet = append(configSet, setPrefix+"relay-source "+v) + } + if overrides["replace_ip_source_with_giaddr"].(bool) { + configSet = append(configSet, setPrefix+"replace-ip-source-with giaddr") + } + if overrides["send_release_on_delete"].(bool) { + configSet = append(configSet, setPrefix+"send-release-on-delete") + } + if overrides["trust_option_82"].(bool) { + configSet = append(configSet, setPrefix+"trust-option-82") + } + if v := overrides["user_defined_option_82"].(string); v != "" { + configSet = append(configSet, setPrefix+"user-defined-option-82 \""+v+"\"") + } + + if len(configSet) == 0 { + return configSet, fmt.Errorf("an overrides_v4 block is empty") + } + + return configSet, nil +} + +func setForwardingOptionsDhcpRelayOverridesV6(overrides map[string]interface{}, setPrefix string, +) ([]string, error) { + configSet := make([]string, 0) + setPrefix += "overrides " + + if overrides["allow_snooped_clients"].(bool) { + configSet = append(configSet, setPrefix+"allow-snooped-clients") + } + if overrides["always_process_option_request_option"].(bool) { + configSet = append(configSet, setPrefix+"always-process-option-request-option") + } + if v := overrides["asymmetric_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-lease-time "+strconv.Itoa(v)) + } + if v := overrides["asymmetric_prefix_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-prefix-lease-time "+strconv.Itoa(v)) + } + if overrides["client_negotiation_match_incoming_interface"].(bool) { + configSet = append(configSet, setPrefix+"client-negotiation-match incoming-interface") + } + if overrides["delay_authentication"].(bool) { + configSet = append(configSet, setPrefix+"delay-authentication") + } + if overrides["delete_binding_on_renegotiation"].(bool) { + configSet = append(configSet, setPrefix+"delete-binding-on-renegotiation") + } + if v := overrides["dual_stack"].(string); v != "" { + configSet = append(configSet, setPrefix+"dual-stack \""+v+"\"") + } + if v := overrides["interface_client_limit"].(int); v != 0 { + configSet = append(configSet, setPrefix+"interface-client-limit "+strconv.Itoa(v)) + } + if overrides["no_allow_snooped_clients"].(bool) { + if overrides["allow_snooped_clients"].(bool) { + return configSet, fmt.Errorf("allow_snooped_clients and no_allow_snooped_clients can't be true in same time") + } + configSet = append(configSet, setPrefix+"no-allow-snooped-clients") + } + if overrides["no_bind_on_request"].(bool) { + configSet = append(configSet, setPrefix+"no-bind-on-request") + } + if v := overrides["relay_source"].(string); v != "" { + configSet = append(configSet, setPrefix+"relay-source "+v) + } + if overrides["send_release_on_delete"].(bool) { + configSet = append(configSet, setPrefix+"send-release-on-delete") + } + + if len(configSet) == 0 { + return configSet, fmt.Errorf("an overrides_v6 block is empty") + } + + return configSet, nil +} + +func setForwardingOptionsDhcpRelayAgentID(relayAgentID map[string]interface{}, setPrefix, blockName string, +) ([]string, error) { + configSet := make([]string, 0) + + if relayAgentID["include_irb_and_l2"].(bool) { + configSet = append(configSet, setPrefix+"include-irb-and-l2") + } + if relayAgentID["keep_incoming_id"].(bool) { + switch blockName { + case "relay_agent_interface_id": + configSet = append(configSet, setPrefix+"keep-incoming-interface-id") + if relayAgentID["keep_incoming_id_strict"].(bool) { + configSet = append(configSet, setPrefix+"keep-incoming-interface-id strict") + } + case "relay_agent_remote_id": + configSet = append(configSet, setPrefix+"keep-incoming-remote-id") + } + } else if blockName == "relay_agent_interface_id" && relayAgentID["keep_incoming_id_strict"].(bool) { + return configSet, fmt.Errorf("keep_incoming_id need to be true with keep_incoming_id_strict") + } + if relayAgentID["no_vlan_interface_name"].(bool) { + configSet = append(configSet, setPrefix+"no-vlan-interface-name") + } + if relayAgentID["prefix_host_name"].(bool) { + configSet = append(configSet, setPrefix+"prefix host-name") + } + if relayAgentID["prefix_routing_instance_name"].(bool) { + configSet = append(configSet, setPrefix+"prefix routing-instance-name") + } + if v := relayAgentID["use_interface_description"].(string); v != "" { + configSet = append(configSet, setPrefix+"use-interface-description "+v) + } + if relayAgentID["use_option_82"].(bool) { + configSet = append(configSet, setPrefix+"use-option-82") + if relayAgentID["use_option_82_strict"].(bool) { + configSet = append(configSet, setPrefix+"use-option-82 strict") + } + } else if relayAgentID["use_option_82_strict"].(bool) { + return configSet, fmt.Errorf("use_option_82 need to be true with use_option_82_strict") + } + if relayAgentID["use_vlan_id"].(bool) { + configSet = append(configSet, setPrefix+"use-vlan-id") + } + + if len(configSet) == 0 { + return configSet, fmt.Errorf("an %s block is empty", blockName) + } + + return configSet, nil +} + +func setForwardingOptionsDhcpRelayOption(relayOption map[string]interface{}, setPrefixSrc, version string, +) ([]string, error) { + configSet := make([]string, 0) + setPrefix := setPrefixSrc + "relay-option " + + for _, block := range relayOption["option_15"].(*schema.Set).List() { + if version == "v4" { + return configSet, fmt.Errorf("relay_option.0.option_15 not compatible when version = v4") + } + option15 := block.(map[string]interface{}) + if action := option15["action"].(string); action == "relay-server-group" { + if option15["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = relay-server-group in option_15 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-15 "+ + option15["compare"].(string)+" "+ + option15["value_type"].(string)+" "+ + "\""+option15["value"].(string)+"\" "+ + action+" "+ + "\""+option15["group"].(string)+"\"") + } else { + if option15["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = relay-server-group in option_15 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-15 "+ + option15["compare"].(string)+" "+ + option15["value_type"].(string)+" "+ + "\""+option15["value"].(string)+"\" "+ + action) + } + } + for _, block := range relayOption["option_15_default_action"].([]interface{}) { + if version == "v4" { + return configSet, fmt.Errorf("relay_option.0.option_15_default_action not compatible when version = v4") + } + option15DefAction := block.(map[string]interface{}) + if action := option15DefAction["action"].(string); action == "relay-server-group" { + if option15DefAction["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = relay-server-group in option_15_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-15 default-action "+action+ + " \""+option15DefAction["group"].(string)+"\"") + } else { + if option15DefAction["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = relay-server-group in option_15_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-15 default-action "+action) + } + } + for _, block := range relayOption["option_16"].(*schema.Set).List() { + if version == "v4" { + return configSet, fmt.Errorf("relay_option.0.option_16 not compatible when version = v4") + } + option16 := block.(map[string]interface{}) + if action := option16["action"].(string); action == "relay-server-group" { + if option16["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = relay-server-group in option_16 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-16 "+ + option16["compare"].(string)+" "+ + option16["value_type"].(string)+" "+ + "\""+option16["value"].(string)+"\" "+ + action+" "+ + "\""+option16["group"].(string)+"\"") + } else { + if option16["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = relay-server-group in option_16 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-16 "+ + option16["compare"].(string)+" "+ + option16["value_type"].(string)+" "+ + "\""+option16["value"].(string)+"\" "+ + action) + } + } + for _, block := range relayOption["option_16_default_action"].([]interface{}) { + if version == "v4" { + return configSet, fmt.Errorf("relay_option.0.option_16_default_action not compatible when version = v4") + } + option16DefAction := block.(map[string]interface{}) + if action := option16DefAction["action"].(string); action == "relay-server-group" { + if option16DefAction["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = relay-server-group in option_16_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-16 default-action "+action+ + " \""+option16DefAction["group"].(string)+"\"") + } else { + if option16DefAction["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = relay-server-group in option_16_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-16 default-action "+action) + } + } + for _, block := range relayOption["option_60"].(*schema.Set).List() { + if version == "v6" { + return configSet, fmt.Errorf("relay_option.0.option_60 not compatible when version = v6") + } + option60 := block.(map[string]interface{}) + if action := option60["action"].(string); action == "local-server-group" || action == "relay-server-group" { + if option60["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = local-server-group or relay-server-group in option_60 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-60 "+ + option60["compare"].(string)+" "+ + option60["value_type"].(string)+" "+ + "\""+option60["value"].(string)+"\" "+ + action+" "+ + "\""+option60["group"].(string)+"\"") + } else { + if option60["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = local-server-group or relay-server-group in option_60 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-60 "+ + option60["compare"].(string)+" "+ + option60["value_type"].(string)+" "+ + "\""+option60["value"].(string)+"\" "+ + action) + } + } + for _, block := range relayOption["option_60_default_action"].([]interface{}) { + if version == "v6" { + return configSet, fmt.Errorf("relay_option.0.option_60_default_action not compatible when version = v6") + } + option60DefAction := block.(map[string]interface{}) + if action := option60DefAction["action"].(string); action == "local-server-group" || action == "relay-server-group" { + if option60DefAction["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = local-server-group or relay-server-group in option_60_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-60 default-action "+action+ + " \""+option60DefAction["group"].(string)+"\"") + } else { + if option60DefAction["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = local-server-group or relay-server-group in option_60_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-60 default-action "+action) + } + } + for _, block := range relayOption["option_77"].(*schema.Set).List() { + if version == "v6" { + return configSet, fmt.Errorf("relay_option.0.option_77 not compatible when version = v6") + } + option77 := block.(map[string]interface{}) + if action := option77["action"].(string); action == "local-server-group" || action == "relay-server-group" { + if option77["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = local-server-group or relay-server-group in option_77 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-77 "+ + option77["compare"].(string)+" "+ + option77["value_type"].(string)+" "+ + "\""+option77["value"].(string)+"\" "+ + action+" "+ + "\""+option77["group"].(string)+"\"") + } else { + if option77["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = local-server-group or relay-server-group in option_77 block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-77 "+ + option77["compare"].(string)+" "+ + option77["value_type"].(string)+" "+ + "\""+option77["value"].(string)+"\" "+ + action) + } + } + for _, block := range relayOption["option_77_default_action"].([]interface{}) { + if version == "v6" { + return configSet, fmt.Errorf("relay_option.0.option_77_default_action not compatible when version = v6") + } + option77DefAction := block.(map[string]interface{}) + if action := option77DefAction["action"].(string); action == "local-server-group" || action == "relay-server-group" { + if option77DefAction["group"].(string) == "" { + return configSet, fmt.Errorf("group must be set when " + + "action = local-server-group or relay-server-group in option_77_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-77 default-action "+action+ + " \""+option77DefAction["group"].(string)+"\"") + } else { + if option77DefAction["group"].(string) != "" { + return configSet, fmt.Errorf("group must be set only with " + + "action = local-server-group or relay-server-group in option_77_default_action block in relay_option block") + } + configSet = append(configSet, setPrefix+"option-77 default-action "+action) + } + } + for _, elem := range relayOption["option_order"].([]interface{}) { + opt := elem.(string) + if version == "v4" && (opt == "15" || opt == "16") { + return configSet, fmt.Errorf("15 & 16 for value in relay_option.0.option_order not compatible when version = v4") + } + if version == "v6" && (opt == "60" || opt == "77") { + return configSet, fmt.Errorf("60 & 77 for value in relay_option.0.option_order not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"option-order "+opt) + } + + return configSet, nil +} + +func setForwardingOptionsDhcpRelayOption82(relayOption map[string]interface{}, setPrefixSrc string) []string { + configSet := make([]string, 0) + setPrefix := setPrefixSrc + "relay-option-82 " + + for _, block := range relayOption["circuit_id"].([]interface{}) { + setPrefixCircuitID := setPrefix + "circuit-id " + configSet = append(configSet, setPrefixCircuitID) + if block != nil { + circuitID := block.(map[string]interface{}) + if circuitID["include_irb_and_l2"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"include-irb-and-l2") + } + if circuitID["keep_incoming_circuit_id"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"keep-incoming-circuit-id") + } + if circuitID["no_vlan_interface_name"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"no-vlan-interface-name") + } + if circuitID["prefix_host_name"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"prefix host-name") + } + if circuitID["prefix_routing_instance_name"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"prefix routing-instance-name") + } + if v := circuitID["use_interface_description"].(string); v != "" { + configSet = append(configSet, setPrefixCircuitID+"use-interface-description "+v) + } + if circuitID["use_vlan_id"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"use-vlan-id") + } + if circuitID["user_defined"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"user-defined") + } + if circuitID["vlan_id_only"].(bool) { + configSet = append(configSet, setPrefixCircuitID+"vlan-id-only") + } + } + } + if relayOption["exclude_relay_agent_identifier"].(bool) { + configSet = append(configSet, setPrefix+"exclude-relay-agent-identifier") + } + if relayOption["link_selection"].(bool) { + configSet = append(configSet, setPrefix+"link-selection") + } + for _, block := range relayOption["remote_id"].([]interface{}) { + setPrefixRemoteID := setPrefix + "remote-id " + configSet = append(configSet, setPrefixRemoteID) + if block != nil { + remoteID := block.(map[string]interface{}) + if remoteID["hostname_only"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"hostname-only") + } + if remoteID["include_irb_and_l2"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"include-irb-and-l2") + } + if remoteID["keep_incoming_remote_id"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"keep-incoming-remote-id") + } + if remoteID["no_vlan_interface_name"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"no-vlan-interface-name") + } + if remoteID["prefix_host_name"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"prefix host-name") + } + if remoteID["prefix_routing_instance_name"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"prefix routing-instance-name") + } + if v := remoteID["use_interface_description"].(string); v != "" { + configSet = append(configSet, setPrefixRemoteID+"use-interface-description "+v) + } + if v := remoteID["use_string"].(string); v != "" { + configSet = append(configSet, setPrefixRemoteID+"use-string \""+v+"\"") + } + if remoteID["use_vlan_id"].(bool) { + configSet = append(configSet, setPrefixRemoteID+"use-vlan-id") + } + } + } + if relayOption["server_id_override"].(bool) { + configSet = append(configSet, setPrefix+"server-id-override") + } + if relayOption["vendor_specific_host_name"].(bool) { + configSet = append(configSet, setPrefix+"vendor-specific host-name") + } + if relayOption["vendor_specific_location"].(bool) { + configSet = append(configSet, setPrefix+"vendor-specific location") + } + + return configSet +} + +func genForwardingOptionsDhcpRelayAuthUsernameInclude() map[string]interface{} { + return map[string]interface{}{ + "circuit_type": false, + "client_id": false, + "client_id_exclude_headers": false, + "client_id_use_automatic_ascii_hex_encoding": false, + "delimiter": "", + "domain_name": "", + "interface_description": "", + "interface_name": false, + "mac_address": false, + "option_60": false, + "option_82": false, + "option_82_circuit_id": false, + "option_82_remote_id": false, + "relay_agent_interface_id": false, + "relay_agent_remote_id": false, + "relay_agent_subscriber_id": false, + "routing_instance_name": false, + "user_prefix": "", + "vlan_tags": false, + } +} + +func genForwardingOptionsDhcpRelayOverridesV4() map[string]interface{} { + return map[string]interface{}{ + "allow_no_end_option": false, + "allow_snooped_clients": false, + "always_write_giaddr": false, + "always_write_option_82": false, + "asymmetric_lease_time": 0, + "bootp_support": false, + "client_discover_match": "", + "delay_authentication": false, + "delete_binding_on_renegotiation": false, + "disable_relay": false, + "dual_stack": "", + "interface_client_limit": 0, + "layer2_unicast_replies": false, + "no_allow_snooped_clients": false, + "no_bind_on_request": false, + "no_unicast_replies": false, + "proxy_mode": false, + "relay_source": "", + "replace_ip_source_with_giaddr": false, + "send_release_on_delete": false, + "trust_option_82": false, + "user_defined_option_82": "", + } +} + +func genForwardingOptionsDhcpRelayOverridesV6() map[string]interface{} { + return map[string]interface{}{ + "allow_snooped_clients": false, + "always_process_option_request_option": false, + "asymmetric_lease_time": 0, + "asymmetric_prefix_lease_time": 0, + "client_negotiation_match_incoming_interface": false, + "delay_authentication": false, + "delete_binding_on_renegotiation": false, + "dual_stack": "", + "interface_client_limit": 0, + "no_allow_snooped_clients": false, + "no_bind_on_request": false, + "relay_source": "", + "send_release_on_delete": false, + } +} + +func genForwardingOptionsDhcpRelayAgentID(keepIncomingIDStrict bool) map[string]interface{} { + r := map[string]interface{}{ + "include_irb_and_l2": false, + "keep_incoming_id": false, + "no_vlan_interface_name": false, + "prefix_host_name": false, + "prefix_routing_instance_name": false, + "use_interface_description": "", + "use_option_82": false, + "use_option_82_strict": false, + "use_vlan_id": false, + } + if keepIncomingIDStrict { + r["keep_incoming_id_strict"] = false + } + + return r +} + +func genForwardingOptionsDhcpRelayOption() map[string]interface{} { + return map[string]interface{}{ + "option_15": make([]map[string]interface{}, 0), + "option_15_default_action": make([]map[string]interface{}, 0), + "option_16": make([]map[string]interface{}, 0), + "option_16_default_action": make([]map[string]interface{}, 0), + "option_60": make([]map[string]interface{}, 0), + "option_60_default_action": make([]map[string]interface{}, 0), + "option_77": make([]map[string]interface{}, 0), + "option_77_default_action": make([]map[string]interface{}, 0), + "option_order": make([]string, 0), + } +} + +func genForwardingOptionsDhcpRelayOption82() map[string]interface{} { + return map[string]interface{}{ + "circuit_id": make([]map[string]interface{}, 0), + "exclude_relay_agent_identifier": false, + "link_selection": false, + "remote_id": make([]map[string]interface{}, 0), + "server_id_override": false, + "vendor_specific_host_name": false, + "vendor_specific_location": false, + } +} + +func readForwardingOptionsDhcpRelayAuthUsernameInclude( + itemTrim string, authUsernameInclude map[string]interface{}, +) { + switch { + case itemTrim == "circuit-type": + authUsernameInclude["circuit_type"] = true + case strings.HasPrefix(itemTrim, "client-id"): + authUsernameInclude["client_id"] = true + switch { + case itemTrim == "client-id exclude-headers": + authUsernameInclude["client_id_exclude_headers"] = true + case itemTrim == "client-id use-automatic-ascii-hex-encoding": + authUsernameInclude["client_id_use_automatic_ascii_hex_encoding"] = true + } + case strings.HasPrefix(itemTrim, "delimiter "): + authUsernameInclude["delimiter"] = strings.Trim(strings.TrimPrefix(itemTrim, "delimiter "), "\"") + case strings.HasPrefix(itemTrim, "domain-name "): + authUsernameInclude["domain_name"] = strings.Trim(strings.TrimPrefix(itemTrim, "domain-name "), "\"") + case strings.HasPrefix(itemTrim, "interface-description "): + authUsernameInclude["interface_description"] = strings.TrimPrefix(itemTrim, "interface-description ") + case itemTrim == "interface-name": + authUsernameInclude["interface_name"] = true + case itemTrim == "mac-address": + authUsernameInclude["mac_address"] = true + case itemTrim == "option-60": + authUsernameInclude["option_60"] = true + case strings.HasPrefix(itemTrim, "option-82"): + authUsernameInclude["option_82"] = true + switch { + case itemTrim == "option-82 circuit-id": + authUsernameInclude["option_82_circuit_id"] = true + case itemTrim == "option-82 remote-id": + authUsernameInclude["option_82_remote_id"] = true + } + case itemTrim == "relay-agent-interface-id": + authUsernameInclude["relay_agent_interface_id"] = true + case itemTrim == "relay-agent-remote-id": + authUsernameInclude["relay_agent_remote_id"] = true + case itemTrim == "relay-agent-subscriber-id": + authUsernameInclude["relay_agent_subscriber_id"] = true + case itemTrim == "routing-instance-name": + authUsernameInclude["routing_instance_name"] = true + case strings.HasPrefix(itemTrim, "user-prefix "): + authUsernameInclude["user_prefix"] = strings.Trim(strings.TrimPrefix(itemTrim, "user-prefix "), "\"") + case itemTrim == "vlan-tags": + authUsernameInclude["vlan_tags"] = true + } +} + +func readForwardingOptionsDhcpRelayOverridesV4(itemTrim string, overrides map[string]interface{}) error { + var err error + switch { + case itemTrim == "allow-no-end-option": + overrides["allow_no_end_option"] = true + case itemTrim == "allow-snooped-clients": + overrides["allow_snooped_clients"] = true + case itemTrim == "always-write-giaddr": + overrides["always_write_giaddr"] = true + case itemTrim == "always-write-option-82": + overrides["always_write_option_82"] = true + case strings.HasPrefix(itemTrim, "asymmetric-lease-time "): + overrides["asymmetric_lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "asymmetric-lease-time ")) + case itemTrim == "bootp-support": + overrides["bootp_support"] = true + case strings.HasPrefix(itemTrim, "client-discover-match "): + overrides["client_discover_match"] = strings.TrimPrefix(itemTrim, "client-discover-match ") + case itemTrim == "delay-authentication": + overrides["delay_authentication"] = true + case itemTrim == "delete-binding-on-renegotiation": + overrides["delete_binding_on_renegotiation"] = true + case itemTrim == "disable-relay": + overrides["disable_relay"] = true + case strings.HasPrefix(itemTrim, "dual-stack "): + overrides["dual_stack"] = strings.Trim(strings.TrimPrefix(itemTrim, "dual-stack "), "\"") + case strings.HasPrefix(itemTrim, "interface-client-limit "): + overrides["interface_client_limit"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "interface-client-limit ")) + case itemTrim == "layer2-unicast-replies": + overrides["layer2_unicast_replies"] = true + case itemTrim == "no-allow-snooped-clients": + overrides["no_allow_snooped_clients"] = true + case itemTrim == "no-bind-on-request": + overrides["no_bind_on_request"] = true + case itemTrim == "no-unicast-replies": + overrides["no_unicast_replies"] = true + case itemTrim == "proxy-mode": + overrides["proxy_mode"] = true + case strings.HasPrefix(itemTrim, "relay-source "): + overrides["relay_source"] = strings.TrimPrefix(itemTrim, "relay-source ") + case itemTrim == "replace-ip-source-with giaddr": + overrides["replace_ip_source_with_giaddr"] = true + case itemTrim == "send-release-on-delete": + overrides["send_release_on_delete"] = true + case itemTrim == "trust-option-82": + overrides["trust_option_82"] = true + case strings.HasPrefix(itemTrim, "user-defined-option-82 "): + overrides["user_defined_option_82"] = strings.Trim(strings.TrimPrefix(itemTrim, "user-defined-option-82 "), "\"") + } + if err != nil { + return fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + + return nil +} + +func readForwardingOptionsDhcpRelayOverridesV6(itemTrim string, overrides map[string]interface{}) error { + var err error + switch { + case itemTrim == "allow-snooped-clients": + overrides["allow_snooped_clients"] = true + case itemTrim == "always-process-option-request-option": + overrides["always_process_option_request_option"] = true + case strings.HasPrefix(itemTrim, "asymmetric-lease-time "): + overrides["asymmetric_lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "asymmetric-lease-time ")) + case strings.HasPrefix(itemTrim, "asymmetric-prefix-lease-time "): + overrides["asymmetric_prefix_lease_time"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "asymmetric-prefix-lease-time ")) + case itemTrim == "client-negotiation-match incoming-interface": + overrides["client_negotiation_match_incoming_interface"] = true + case itemTrim == "delay-authentication": + overrides["delay_authentication"] = true + case itemTrim == "delete-binding-on-renegotiation": + overrides["delete_binding_on_renegotiation"] = true + case strings.HasPrefix(itemTrim, "dual-stack "): + overrides["dual_stack"] = strings.Trim(strings.TrimPrefix(itemTrim, "dual-stack "), "\"") + case strings.HasPrefix(itemTrim, "interface-client-limit "): + overrides["interface_client_limit"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "interface-client-limit ")) + case itemTrim == "no-allow-snooped-clients": + overrides["no_allow_snooped_clients"] = true + case itemTrim == "no-bind-on-request": + overrides["no_bind_on_request"] = true + case strings.HasPrefix(itemTrim, "relay-source "): + overrides["relay_source"] = strings.TrimPrefix(itemTrim, "relay-source ") + case itemTrim == "send-release-on-delete": + overrides["send_release_on_delete"] = true + } + if err != nil { + return fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + + return nil +} + +func readForwardingOptionsDhcpRelayAgentID(itemTrim string, agentID map[string]interface{}) { + switch { + case itemTrim == "include-irb-and-l2": + agentID["include_irb_and_l2"] = true + case itemTrim == "keep-incoming-interface-id": + agentID["keep_incoming_id"] = true + case itemTrim == "keep-incoming-remote-id": + agentID["keep_incoming_id"] = true + case itemTrim == "keep-incoming-interface-id strict": + agentID["keep_incoming_id_strict"] = true + agentID["keep_incoming_id"] = true + case itemTrim == "no-vlan-interface-name": + agentID["no_vlan_interface_name"] = true + case itemTrim == "prefix host-name": + agentID["prefix_host_name"] = true + case itemTrim == "prefix routing-instance-name": + agentID["prefix_routing_instance_name"] = true + case strings.HasPrefix(itemTrim, "use-interface-description "): + agentID["use_interface_description"] = strings.TrimPrefix(itemTrim, "use-interface-description ") + case itemTrim == "use-option-82": + agentID["use_option_82"] = true + case itemTrim == "use-option-82 strict": + agentID["use_option_82_strict"] = true + agentID["use_option_82"] = true + case itemTrim == "use-vlan-id": + agentID["use_vlan_id"] = true + } +} + +func readForwardingOptionsDhcpRelayOption(itemTrim string, relayOption map[string]interface{}) error { + switch { + case strings.HasPrefix(itemTrim, "option-15 default-action "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-15 default-action "), " ") + defAction := map[string]interface{}{ + "action": itemTrimSplit[0], + "group": "", + } + if len(itemTrimSplit) > 1 { + defAction["group"] = strings.Trim(strings.Join(itemTrimSplit[1:], " "), "\"") + } + relayOption["option_15_default_action"] = append( + relayOption["option_15_default_action"].([]map[string]interface{}), + defAction, + ) + case strings.HasPrefix(itemTrim, "option-15 "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-15 "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read values for option_15 in '%s'", itemTrim) + } + value := itemTrimSplit[2] + actionIndex := 3 + if strings.Contains(itemTrimSplit[2], "\"") { + for k, v := range itemTrimSplit[3:] { + value += " " + v + if strings.Contains(v, "\"") { + actionIndex = 3 + k + 1 + + break + } + } + } + value = strings.Trim(value, "\"") + action := itemTrimSplit[actionIndex] + option15 := map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": value, + "action": action, + "group": "", + } + if len(itemTrimSplit) > actionIndex { + option15["group"] = strings.Trim(strings.Join(itemTrimSplit[actionIndex+1:], " "), "\"") + } + relayOption["option_15"] = append( + relayOption["option_15"].([]map[string]interface{}), + option15, + ) + case strings.HasPrefix(itemTrim, "option-16 default-action "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-16 default-action "), " ") + defAction := map[string]interface{}{ + "action": itemTrimSplit[0], + "group": "", + } + if len(itemTrimSplit) > 1 { + defAction["group"] = strings.Trim(strings.Join(itemTrimSplit[1:], " "), "\"") + } + relayOption["option_16_default_action"] = append( + relayOption["option_16_default_action"].([]map[string]interface{}), + defAction, + ) + case strings.HasPrefix(itemTrim, "option-16 "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-16 "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read values for option_16 in '%s'", itemTrim) + } + value := itemTrimSplit[2] + actionIndex := 3 + if strings.Contains(itemTrimSplit[2], "\"") { + for k, v := range itemTrimSplit[3:] { + value += " " + v + if strings.Contains(v, "\"") { + actionIndex = 3 + k + 1 + + break + } + } + } + value = strings.Trim(value, "\"") + action := itemTrimSplit[actionIndex] + option16 := map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": value, + "action": action, + "group": "", + } + if len(itemTrimSplit) > actionIndex { + option16["group"] = strings.Trim(strings.Join(itemTrimSplit[actionIndex+1:], " "), "\"") + } + relayOption["option_16"] = append( + relayOption["option_16"].([]map[string]interface{}), + option16, + ) + case strings.HasPrefix(itemTrim, "option-60 default-action "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-60 default-action "), " ") + defAction := map[string]interface{}{ + "action": itemTrimSplit[0], + "group": "", + } + if len(itemTrimSplit) > 1 { + defAction["group"] = strings.Trim(strings.Join(itemTrimSplit[1:], " "), "\"") + } + relayOption["option_60_default_action"] = append( + relayOption["option_60_default_action"].([]map[string]interface{}), + defAction, + ) + case strings.HasPrefix(itemTrim, "option-60 "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-60 "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read values for option_60 in '%s'", itemTrim) + } + value := itemTrimSplit[2] + actionIndex := 3 + if strings.Contains(itemTrimSplit[2], "\"") { + for k, v := range itemTrimSplit[3:] { + value += " " + v + if strings.Contains(v, "\"") { + actionIndex = 3 + k + 1 + + break + } + } + } + value = strings.Trim(value, "\"") + action := itemTrimSplit[actionIndex] + option60 := map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": value, + "action": action, + "group": "", + } + if len(itemTrimSplit) > actionIndex { + option60["group"] = strings.Trim(strings.Join(itemTrimSplit[actionIndex+1:], " "), "\"") + } + relayOption["option_60"] = append( + relayOption["option_60"].([]map[string]interface{}), + option60, + ) + case strings.HasPrefix(itemTrim, "option-77 default-action "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-77 default-action "), " ") + defAction := map[string]interface{}{ + "action": itemTrimSplit[0], + "group": "", + } + if len(itemTrimSplit) > 1 { + defAction["group"] = strings.Trim(strings.Join(itemTrimSplit[1:], " "), "\"") + } + relayOption["option_77_default_action"] = append( + relayOption["option_77_default_action"].([]map[string]interface{}), + defAction, + ) + case strings.HasPrefix(itemTrim, "option-77 "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "option-77 "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read values for option_77 in '%s'", itemTrim) + } + value := itemTrimSplit[2] + actionIndex := 3 + if strings.Contains(itemTrimSplit[2], "\"") { + for k, v := range itemTrimSplit[3:] { + value += " " + v + if strings.Contains(v, "\"") { + actionIndex = 3 + k + 1 + + break + } + } + } + value = strings.Trim(value, "\"") + action := itemTrimSplit[actionIndex] + option77 := map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": value, + "action": action, + "group": "", + } + if len(itemTrimSplit) > actionIndex { + option77["group"] = strings.Trim(strings.Join(itemTrimSplit[actionIndex+1:], " "), "\"") + } + relayOption["option_77"] = append( + relayOption["option_77"].([]map[string]interface{}), + option77, + ) + case strings.HasPrefix(itemTrim, "option-order "): + relayOption["option_order"] = append( + relayOption["option_order"].([]string), + strings.TrimPrefix(itemTrim, "option-order "), + ) + } + + return nil +} + +func readForwardingOptionsDhcpRelayOption82(itemTrim string, relayOption82 map[string]interface{}) { + switch { + case strings.HasPrefix(itemTrim, "circuit-id"): + if len(relayOption82["circuit_id"].([]map[string]interface{})) == 0 { + relayOption82["circuit_id"] = append(relayOption82["circuit_id"].([]map[string]interface{}), map[string]interface{}{ + "include_irb_and_l2": false, + "keep_incoming_circuit_id": false, + "no_vlan_interface_name": false, + "prefix_host_name": false, + "prefix_routing_instance_name": false, + "use_interface_description": "", + "use_vlan_id": false, + "user_defined": false, + "vlan_id_only": false, + }) + } + if strings.HasPrefix(itemTrim, "circuit-id ") { + circuitID := relayOption82["circuit_id"].([]map[string]interface{})[0] + itemTrimCircuitID := strings.TrimPrefix(itemTrim, "circuit-id ") + switch { + case itemTrimCircuitID == "include-irb-and-l2": + circuitID["include_irb_and_l2"] = true + case itemTrimCircuitID == "keep-incoming-circuit-id": + circuitID["keep_incoming_circuit_id"] = true + case itemTrimCircuitID == "no-vlan-interface-name": + circuitID["no_vlan_interface_name"] = true + case itemTrimCircuitID == "prefix host-name": + circuitID["prefix_host_name"] = true + case itemTrimCircuitID == "prefix routing-instance-name": + circuitID["prefix_routing_instance_name"] = true + case strings.HasPrefix(itemTrimCircuitID, "use-interface-description "): + circuitID["use_interface_description"] = strings.TrimPrefix(itemTrimCircuitID, "use-interface-description ") + case itemTrimCircuitID == "use-vlan-id": + circuitID["use_vlan_id"] = true + case itemTrimCircuitID == "user-defined": + circuitID["user_defined"] = true + case itemTrimCircuitID == "vlan-id-only": + circuitID["vlan_id_only"] = true + } + } + case itemTrim == "exclude-relay-agent-identifier": + relayOption82["exclude_relay_agent_identifier"] = true + case itemTrim == "link-selection": + relayOption82["link_selection"] = true + case strings.HasPrefix(itemTrim, "remote-id"): + if len(relayOption82["remote_id"].([]map[string]interface{})) == 0 { + relayOption82["remote_id"] = append(relayOption82["remote_id"].([]map[string]interface{}), map[string]interface{}{ + "hostname_only": false, + "include_irb_and_l2": false, + "keep_incoming_remote_id": false, + "no_vlan_interface_name": false, + "prefix_host_name": false, + "prefix_routing_instance_name": false, + "use_interface_description": "", + "use_string": "", + "use_vlan_id": false, + }) + } + if strings.HasPrefix(itemTrim, "remote-id ") { + remoteID := relayOption82["remote_id"].([]map[string]interface{})[0] + itemTrimRemoteID := strings.TrimPrefix(itemTrim, "remote-id ") + switch { + case itemTrimRemoteID == "hostname-only": + remoteID["hostname_only"] = true + case itemTrimRemoteID == "include-irb-and-l2": + remoteID["include_irb_and_l2"] = true + case itemTrimRemoteID == "keep-incoming-remote-id": + remoteID["keep_incoming_remote_id"] = true + case itemTrimRemoteID == "no-vlan-interface-name": + remoteID["no_vlan_interface_name"] = true + case itemTrimRemoteID == "prefix host-name": + remoteID["prefix_host_name"] = true + case itemTrimRemoteID == "prefix routing-instance-name": + remoteID["prefix_routing_instance_name"] = true + case strings.HasPrefix(itemTrimRemoteID, "use-interface-description "): + remoteID["use_interface_description"] = strings.TrimPrefix(itemTrimRemoteID, "use-interface-description ") + case strings.HasPrefix(itemTrimRemoteID, "use-string "): + remoteID["use_string"] = strings.Trim(strings.TrimPrefix(itemTrimRemoteID, "use-string "), "\"") + case itemTrimRemoteID == "use-vlan-id": + remoteID["use_vlan_id"] = true + } + } + case itemTrim == "server-id-override": + relayOption82["server_id_override"] = true + case itemTrim == "vendor-specific host-name": + relayOption82["vendor_specific_host_name"] = true + case itemTrim == "vendor-specific location": + relayOption82["vendor_specific_location"] = true + } +} diff --git a/junos/provider.go b/junos/provider.go index 1c1731f1..24d402b4 100644 --- a/junos/provider.go +++ b/junos/provider.go @@ -126,6 +126,7 @@ func Provider() *schema.Provider { "junos_evpn": resourceEvpn(), "junos_firewall_filter": resourceFirewallFilter(), "junos_firewall_policer": resourceFirewallPolicer(), + "junos_forwardingoptions_dhcprelay_group": resourceForwardingOptionsDhcpRelayGroup(), "junos_forwardingoptions_dhcprelay_servergroup": resourceForwardingOptionsDhcpRelayServerGroup(), "junos_forwardingoptions_sampling_instance": resourceForwardingOptionsSamplingInstance(), "junos_generate_route": resourceGenerateRoute(), diff --git a/junos/resource_forwardingoptions_dhcprelay_group.go b/junos/resource_forwardingoptions_dhcprelay_group.go new file mode 100644 index 00000000..18c39045 --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay_group.go @@ -0,0 +1,1983 @@ +package junos + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + bchk "github.com/jeremmfr/go-utils/basiccheck" +) + +type fwdOptsDhcpRelGroupOptions struct { + activeServerGroupAllowServerChange bool + dynamicProfileAggregateClients bool + forwardOnly bool + relayAgentOption79 bool + remoteIDMismatchDisconnect bool + routeSuppressionAccess bool + routeSuppressionAccessInternal bool + routeSuppressionDestination bool + sourceIPChange bool + vendorSpecificInformationHostName bool + vendorSpecificInformationLocation bool + clientResponseTTL int + maximumHopCount int + minimumWaitTime int + shortCycleProtectionLockoutMaxTime int + shortCycleProtectionLockoutMinTime int + accessProfile string + activeServerGroup string + authenticationPassword string + description string + dynamicProfile string + dynamicProfileAggregateClientsAction string + dynamicProfileUsePrimary string + forwardOnlyRoutingInstance string + livenessDetectionFailureAction string + name string + routingInstance string + serverMatchDefaultAction string + serviceProfile string + version string + authenticationUsernameInclude []map[string]interface{} + interFace []map[string]interface{} + leaseTimeValidation []map[string]interface{} + livenessDetectionMethodBfd []map[string]interface{} + livenessDetectionMethodLayer2 []map[string]interface{} + overridesV4 []map[string]interface{} + overridesV6 []map[string]interface{} + relayAgentInterfaceID []map[string]interface{} + relayAgentRemoteID []map[string]interface{} + relayOption []map[string]interface{} + relayOption82 []map[string]interface{} + serverMatchAddress []map[string]interface{} + serverMatchDuid []map[string]interface{} +} + +func resourceForwardingOptionsDhcpRelayGroup() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceForwardingOptionsDhcpRelayGroupCreate, + ReadWithoutTimeout: resourceForwardingOptionsDhcpRelayGroupRead, + UpdateWithoutTimeout: resourceForwardingOptionsDhcpRelayGroupUpdate, + DeleteWithoutTimeout: resourceForwardingOptionsDhcpRelayGroupDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceForwardingOptionsDhcpRelayGroupImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "routing_instance": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: defaultW, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "v4", + ValidateFunc: validation.StringInSlice([]string{"v4", "v6"}, false), + AtLeastOneOf: []string{ + "access_profile", + "active_server_group", + "active_server_group_allow_server_change", + "authentication_password", + "authentication_username_include", + "client_response_ttl", + "description", + "dynamic_profile", + "forward_only", + "interface", + "lease_time_validation", + "liveness_detection_failure_action", + "liveness_detection_method_bfd", + "liveness_detection_method_layer2", + "maximum_hop_count", + "minimum_wait_time", + "overrides_v4", + "overrides_v6", + "relay_agent_interface_id", + "relay_agent_option_79", + "relay_agent_remote_id", + "relay_option", + "relay_option_82", + "remote_id_mismatch_disconnect", + "route_suppression_access", + "route_suppression_access_internal", + "route_suppression_destination", + "server_match_address", + "server_match_default_action", + "server_match_duid", + "service_profile", + "short_cycle_protection_lockout_max_time", + "source_ip_change", + "vendor_specific_information_host_name", + "vendor_specific_information_location", + }, + }, + "access_profile": { + Type: schema.TypeString, + Optional: true, + }, + "active_server_group": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "active_server_group_allow_server_change": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "authentication_password": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_username_include": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAuthUsernameInclude(), + }, + }, + "client_response_ttl": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_aggregate_clients": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + }, + "dynamic_profile_aggregate_clients_action": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile_aggregate_clients"}, + ValidateFunc: validation.StringInSlice([]string{"merge", "replace"}, false), + }, + "dynamic_profile_use_primary": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + }, + "forward_only": { + Type: schema.TypeBool, + Optional: true, + }, + "forward_only_routing_instance": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"forward_only"}, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "interface": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value == "all" { + return + } + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot or be 'all'", value, k)) + } + + return + }, + }, + "access_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_aggregate_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "dynamic_profile_aggregate_clients_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"merge", "replace"}, false), + }, + "dynamic_profile_use_primary": { + Type: schema.TypeString, + Optional: true, + }, + "exclude": { + Type: schema.TypeBool, + Optional: true, + }, + "overrides_v4": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV4(), + }, + }, + "overrides_v6": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV6(), + }, + }, + "service_profile": { + Type: schema.TypeString, + Optional: true, + }, + "short_cycle_protection_lockout_max_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "short_cycle_protection_lockout_min_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "trace": { + Type: schema.TypeBool, + Optional: true, + }, + "upto": { + Type: schema.TypeString, + Optional: true, + + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot", value, k)) + } + + return + }, + }, + }, + }, + }, + "lease_time_validation": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lease_time_threshold": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(60, 2147483647), + }, + "violation_action_drop": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "liveness_detection_failure_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "clear-binding", + "clear-binding-if-interface-up", + "log-only", + }, false), + }, + "liveness_detection_method_bfd": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_layer2"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "detection_time_threshold": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_bfd.0.detection_time_threshold", + "liveness_detection_method_bfd.0.holddown_interval", + "liveness_detection_method_bfd.0.minimum_interval", + "liveness_detection_method_bfd.0.minimum_receive_interval", + "liveness_detection_method_bfd.0.multiplier", + "liveness_detection_method_bfd.0.no_adaptation", + "liveness_detection_method_bfd.0.session_mode", + "liveness_detection_method_bfd.0.transmit_interval_minimum", + "liveness_detection_method_bfd.0.transmit_interval_threshold", + "liveness_detection_method_bfd.0.version", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "holddown_interval": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 255000), + }, + "minimum_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "minimum_receive_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "multiplier": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "no_adaptation": { + Type: schema.TypeBool, + Optional: true, + }, + "session_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"automatic", "multihop", "single-hop"}, false), + }, + "transmit_interval_minimum": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "transmit_interval_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"0", "1", "automatic"}, false), + }, + }, + }, + }, + "liveness_detection_method_layer2": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_bfd"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_consecutive_retries": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_layer2.0.max_consecutive_retries", + "liveness_detection_method_layer2.0.transmit_interval", + }, + ValidateFunc: validation.IntBetween(3, 6), + }, + "transmit_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(300, 1800), + }, + }, + }, + }, + "maximum_hop_count": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 16), + }, + "minimum_wait_time": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 30000), + }, + "overrides_v4": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV4(), + }, + }, + "overrides_v6": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV6(), + }, + }, + "relay_agent_interface_id": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAgentID(true), + }, + }, + "relay_agent_option_79": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_remote_id": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAgentID(false), + }, + }, + "relay_option": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOption(), + }, + }, + "relay_option_82": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOption82(), + }, + }, + "remote_id_mismatch_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access_internal": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_destination": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "server_match_address": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsCIDR, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + }, + }, + }, + "server_match_default_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + "server_match_duid": { // only dhcpv6 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + }, + }, + }, + "service_profile": { // only dhcpv6 + Type: schema.TypeString, + Optional: true, + }, + "short_cycle_protection_lockout_max_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_min_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "short_cycle_protection_lockout_min_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_max_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "source_ip_change": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_information_host_name": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_information_location": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + }, + } +} + +func resourceForwardingOptionsDhcpRelayGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeCreateSetFile != "" { + if err := setForwardingOptionsDhcpRelayGroup(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(nameArg + idSeparator + routingInstanceArg + idSeparator + versionArg) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if routingInstanceArg != defaultW { + instanceExists, err := checkRoutingInstanceExists(routingInstanceArg, clt, junSess) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !instanceExists { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, + diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", routingInstanceArg))...) + } + } + fwdOptsDhcpRelGroupExists, err := checkForwardingOptionsDhcpRelayGroupExists( + nameArg, + routingInstanceArg, + versionArg, + clt, junSess) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if fwdOptsDhcpRelGroupExists { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + if versionArg == "v6" { + return append(diagWarns, + diag.FromErr(fmt.Errorf("forwarding-options dhcp-relay dhcpv6 group %v"+ + " already exists in routing-instance %s", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, + diag.FromErr(fmt.Errorf("forwarding-options dhcp-relay group %v"+ + " already exists in routing-instance %s", nameArg, routingInstanceArg))...) + } + + if err := setForwardingOptionsDhcpRelayGroup(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("create resource junos_forwardingoptions_dhcprelay_group", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + fwdOptsDhcpRelGroupExists, err = checkForwardingOptionsDhcpRelayGroupExists( + nameArg, + routingInstanceArg, + versionArg, + clt, junSess) + if err != nil { + return append(diagWarns, diag.FromErr(err)...) + } + if fwdOptsDhcpRelGroupExists { + d.SetId(nameArg + idSeparator + routingInstanceArg + idSeparator + versionArg) + } else { + if versionArg == "v6" { + return append(diagWarns, diag.FromErr(fmt.Errorf( + "forwarding-options dhcp-relay dhcpv6 group %v not exists in routing_instance %s after commit "+ + "=> check your config", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, diag.FromErr(fmt.Errorf( + "forwarding-options dhcp-relay group %v not exists in routing_instance %s after commit "+ + "=> check your config", nameArg, routingInstanceArg))...) + } + + return append(diagWarns, resourceForwardingOptionsDhcpRelayGroupReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + + return resourceForwardingOptionsDhcpRelayGroupReadWJunSess(d, clt, junSess) +} + +func resourceForwardingOptionsDhcpRelayGroupReadWJunSess( + d *schema.ResourceData, clt *Client, junSess *junosSession, +) diag.Diagnostics { + mutex.Lock() + fwdOptsDhcpRelGroupOptions, err := readForwardingOptionsDhcpRelayGroup( + d.Get("name").(string), + d.Get("routing_instance").(string), + d.Get("version").(string), + clt, junSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if fwdOptsDhcpRelGroupOptions.name == "" { + d.SetId("") + } else { + fillForwardingOptionsDhcpRelayGroupData(d, fwdOptsDhcpRelGroupOptions) + } + + return nil +} + +func resourceForwardingOptionsDhcpRelayGroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + d.Partial(true) + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeUpdateAlso { + if err := delForwardingOptionsDhcpRelayGroup(nameArg, routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + if err := setForwardingOptionsDhcpRelayGroup(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.Partial(false) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelayGroup(nameArg, routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if err := setForwardingOptionsDhcpRelayGroup(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("update resource junos_forwardingoptions_dhcprelay_group", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.Partial(false) + + return append(diagWarns, resourceForwardingOptionsDhcpRelayGroupReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayGroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + nameArg := d.Get("name").(string) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeDeleteAlso { + if err := delForwardingOptionsDhcpRelayGroup(nameArg, routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelayGroup(nameArg, routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := clt.commitConf("delete resource junos_forwardingoptions_dhcprelay_group", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + return diagWarns +} + +func resourceForwardingOptionsDhcpRelayGroupImport(ctx context.Context, d *schema.ResourceData, m interface{}, +) ([]*schema.ResourceData, error) { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return nil, err + } + defer clt.closeSession(junSess) + result := make([]*schema.ResourceData, 1) + idSplit := strings.Split(d.Id(), idSeparator) + if len(idSplit) < 3 { + return nil, fmt.Errorf("missing element(s) in id with separator %v", idSeparator) + } + if idSplit[2] != "v4" && idSplit[2] != "v6" { + return nil, fmt.Errorf("bad version '%s' in id, need to be 'v4' or 'v6' (id must be "+ + ""+idSeparator+""+idSeparator+")", idSplit[2]) + } + fwdOptsDhcpRelGroupExists, err := checkForwardingOptionsDhcpRelayGroupExists( + idSplit[0], + idSplit[1], + idSplit[2], + clt, junSess) + if err != nil { + return nil, err + } + if !fwdOptsDhcpRelGroupExists { + if idSplit[2] == "v6" { + return nil, fmt.Errorf("don't find forwarding-options dhcp-relay dhcpv6 group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + + return nil, fmt.Errorf("don't find forwarding-options dhcp-relay group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + fwdOptsDhcpRelGroupOptions, err := readForwardingOptionsDhcpRelayGroup( + idSplit[0], + idSplit[1], + idSplit[2], + clt, junSess) + if err != nil { + return nil, err + } + fillForwardingOptionsDhcpRelayGroupData(d, fwdOptsDhcpRelGroupOptions) + + result[0] = d + + return result, nil +} + +func checkForwardingOptionsDhcpRelayGroupExists(name, instance, version string, clt *Client, junSess *junosSession, +) (bool, error) { + showCmd := cmdShowConfig + if instance != defaultW { + showCmd += routingInstancesWS + instance + " " + } + showCmd += "forwarding-options dhcp-relay " + if version == "v6" { + showCmd += "dhcpv6 group " + name + } else { + showCmd += "group " + name + } + showConfig, err := clt.command(showCmd+pipeDisplaySet, junSess) + if err != nil { + return false, err + } + if showConfig == emptyW { + return false, nil + } + + return true, nil +} + +func setForwardingOptionsDhcpRelayGroup(d *schema.ResourceData, clt *Client, junSess *junosSession) error { + configSet := make([]string, 0) + + setPrefix := setLS + if d.Get("routing_instance").(string) != defaultW { + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " + } + if d.Get("version").(string) == "v6" { + setPrefix += "forwarding-options dhcp-relay dhcpv6 group " + d.Get("name").(string) + " " + } else { + setPrefix += "forwarding-options dhcp-relay group " + d.Get("name").(string) + " " + } + + if v := d.Get("access_profile").(string); v != "" { + configSet = append(configSet, setPrefix+"access-profile \""+v+"\"") + } + if v := d.Get("active_server_group").(string); v != "" { + configSet = append(configSet, setPrefix+"active-server-group "+v) + } + if d.Get("active_server_group_allow_server_change").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("active_server_group_allow_server_change not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"active-server-group allow-server-change") + } + if v := d.Get("authentication_password").(string); v != "" { + configSet = append(configSet, setPrefix+"authentication password \""+v+"\"") + } + for _, vBlock := range d.Get("authentication_username_include").([]interface{}) { + authenticationUsernameInclude := vBlock.(map[string]interface{}) + configSetAuthUsernameInclude, err := setForwardingOptionsDhcpRelayAuthUsernameInclude( + authenticationUsernameInclude, + setPrefix, + d.Get("version").(string), + ) + if err != nil { + return err + } + configSet = append(configSet, configSetAuthUsernameInclude...) + } + if v := d.Get("client_response_ttl").(int); v != 0 { + if d.Get("version").(string) != "v4" { + return fmt.Errorf("client_response_ttl only compatible when version = v4") + } + configSet = append(configSet, setPrefix+"client-response-ttl "+strconv.Itoa(v)) + } + if v := d.Get("description").(string); v != "" { + configSet = append(configSet, setPrefix+"description \""+v+"\"") + } + if dynProfile := d.Get("dynamic_profile").(string); dynProfile != "" { + configSet = append(configSet, setPrefix+"dynamic-profile \""+dynProfile+"\"") + if d.Get("dynamic_profile_aggregate_clients").(bool) { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients") + if v := d.Get("dynamic_profile_aggregate_clients_action").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients "+v) + } + } else if d.Get("dynamic_profile_aggregate_clients_action").(string) != "" { + return fmt.Errorf("dynamic_profile_aggregate_clients need to be true with " + + "dynamic_profile_aggregate_clients_action") + } + if v := d.Get("dynamic_profile_use_primary").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile use-primary \""+v+"\"") + } + } else if d.Get("dynamic_profile_aggregate_clients").(bool) || + d.Get("dynamic_profile_aggregate_clients_action").(string) != "" || + d.Get("dynamic_profile_use_primary").(string) != "" { + return fmt.Errorf("dynamic_profile need to be set with " + + "dynamic_profile_use_primary, dynamic_profile_aggregate_clients " + + "and dynamic_profile_aggregate_clients_action") + } + if d.Get("forward_only").(bool) { + configSet = append(configSet, setPrefix+"forward-only") + if v := d.Get("forward_only_routing_instance").(string); v != "" { + configSet = append(configSet, setPrefix+"forward-only routing-instance "+v) + } + } else if d.Get("forward_only_routing_instance").(string) != "" { + return fmt.Errorf("forward_only need to be true with forward_only_routing_instance") + } + interfaceNameList := make([]string, 0) + for _, v := range d.Get("interface").(*schema.Set).List() { + interFace := v.(map[string]interface{}) + if bchk.StringInSlice(interFace["name"].(string), interfaceNameList) { + return fmt.Errorf("multiple blocks interface with the same name %s", interFace["name"].(string)) + } + interfaceNameList = append(interfaceNameList, interFace["name"].(string)) + configSetInterface, err := setForwardingOptionsDhcpRelayGroupInterface( + interFace, setPrefix, d.Get("version").(string)) + if err != nil { + return err + } + configSet = append(configSet, configSetInterface...) + } + for _, vBlock := range d.Get("lease_time_validation").([]interface{}) { + configSet = append(configSet, setPrefix+"lease-time-validation") + if vBlock != nil { + leaseTimeValidation := vBlock.(map[string]interface{}) + if v := leaseTimeValidation["lease_time_threshold"].(int); v != 0 { + configSet = append(configSet, setPrefix+"lease-time-validation lease-time-threshold "+strconv.Itoa(v)) + } + if leaseTimeValidation["violation_action_drop"].(bool) { + configSet = append(configSet, setPrefix+"lease-time-validation violation-action drop") + } + } + } + if v := d.Get("liveness_detection_failure_action").(string); v != "" { + configSet = append(configSet, setPrefix+"liveness-detection failure-action "+v) + } + for _, ldmBfd := range d.Get("liveness_detection_method_bfd").([]interface{}) { + liveDetectMethBfd := ldmBfd.(map[string]interface{}) + setPrefixLDMBfd := setPrefix + "liveness-detection method bfd " + if v := liveDetectMethBfd["detection_time_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"detection-time threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["holddown_interval"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"holddown-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_receive_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-receive-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["multiplier"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"multiplier "+strconv.Itoa(v)) + } + if liveDetectMethBfd["no_adaptation"].(bool) { + configSet = append(configSet, setPrefixLDMBfd+"no-adaptation") + } + if v := liveDetectMethBfd["session_mode"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"session-mode "+v) + } + if v := liveDetectMethBfd["transmit_interval_minimum"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["transmit_interval_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["version"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"version "+v) + } + + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefixLDMBfd) { + return fmt.Errorf("liveness_detection_method_bfd block is empty") + } + } + for _, ldmLayer2 := range d.Get("liveness_detection_method_layer2").([]interface{}) { + if ldmLayer2 == nil { + return fmt.Errorf("liveness_detection_method_layer2 block is empty") + } + liveDetectMethLayer2 := ldmLayer2.(map[string]interface{}) + setPrefixLDMLayer2 := setPrefix + "liveness-detection method layer2-liveness-detection " + if v := liveDetectMethLayer2["max_consecutive_retries"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"max-consecutive-retries "+strconv.Itoa(v)) + } + if v := liveDetectMethLayer2["transmit_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"transmit-interval "+strconv.Itoa(v)) + } + } + if v := d.Get("maximum_hop_count").(int); v != 0 { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("maximum_hop_count not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"maximum-hop-count "+strconv.Itoa(v)) + } + if v := d.Get("minimum_wait_time").(int); v != -1 { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("minimum_wait_time not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"minimum-wait-time "+strconv.Itoa(v)) + } + for _, v := range d.Get("overrides_v4").([]interface{}) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("overrides_v4 not compatible if version = v6") + } + if v == nil { + return fmt.Errorf("overrides_v4 block is empty") + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV4( + v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range d.Get("overrides_v6").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("overrides_v6 not compatible if version = v4") + } + if v == nil { + return fmt.Errorf("overrides_v6 block is empty") + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV6( + v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range d.Get("relay_agent_interface_id").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-interface-id") + if v != nil { + configSetRelayAgentID, err := setForwardingOptionsDhcpRelayAgentID( + v.(map[string]interface{}), setPrefix+"relay-agent-interface-id ", "relay_agent_interface_id") + if err != nil { + return err + } + configSet = append(configSet, configSetRelayAgentID...) + } + } + if d.Get("relay_agent_option_79").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-option-79") + } + for _, v := range d.Get("relay_agent_remote_id").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-remote-id") + if v != nil { + configSetRelayAgentID, err := setForwardingOptionsDhcpRelayAgentID( + v.(map[string]interface{}), setPrefix+"relay-agent-remote-id ", "relay_agent_remote_id") + if err != nil { + return err + } + configSet = append(configSet, configSetRelayAgentID...) + } + } + for _, v := range d.Get("relay_option").([]interface{}) { + configSet = append(configSet, setPrefix+"relay-option") + if v != nil { + configSetRelayOption, err := setForwardingOptionsDhcpRelayOption( + v.(map[string]interface{}), setPrefix, d.Get("version").(string)) + if err != nil { + return err + } + configSet = append(configSet, configSetRelayOption...) + } + } + for _, v := range d.Get("relay_option_82").([]interface{}) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("relay_option_82 not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"relay-option-82") + if v != nil { + configSet = append(configSet, setForwardingOptionsDhcpRelayOption82(v.(map[string]interface{}), setPrefix)...) + } + } + if d.Get("remote_id_mismatch_disconnect").(bool) { + configSet = append(configSet, setPrefix+"remote-id-mismatch disconnect") + } + if d.Get("route_suppression_access").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("route_suppression_access not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"route-suppression access") + } + if d.Get("route_suppression_access_internal").(bool) { + configSet = append(configSet, setPrefix+"route-suppression access-internal") + } + if d.Get("route_suppression_destination").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("route_suppression_destination not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"route-suppression destination") + } + serverMatchAddressList := make([]string, 0) + for _, v := range d.Get("server_match_address").(*schema.Set).List() { + serverMatchAddress := v.(map[string]interface{}) + if bchk.StringInSlice(serverMatchAddress["address"].(string), serverMatchAddressList) { + return fmt.Errorf("multiple blocks server_match_address with the same address %s", + serverMatchAddress["address"].(string)) + } + serverMatchAddressList = append(serverMatchAddressList, serverMatchAddress["address"].(string)) + configSet = append(configSet, setPrefix+"server-match address "+ + serverMatchAddress["address"].(string)+" "+serverMatchAddress["action"].(string)) + } + if v := d.Get("server_match_default_action").(string); v != "" { + configSet = append(configSet, setPrefix+"server-match default-action "+v) + } + serverMatchDuidList := make([]string, 0) + for _, v := range d.Get("server_match_duid").(*schema.Set).List() { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("server_match_duid not compatible if version = v4") + } + serverMatchDuid := v.(map[string]interface{}) + serverMatchDuidCompare := serverMatchDuid["compare"].(string) + serverMatchDuidValueType := serverMatchDuid["value_type"].(string) + serverMatchDuidValue := serverMatchDuid["value"].(string) + if bchk.StringInSlice( + serverMatchDuidCompare+idSeparator+serverMatchDuidValueType+idSeparator+serverMatchDuidValue, + serverMatchDuidList, + ) { + return fmt.Errorf("multiple blocks server_match_duid with the same compare %s, value_type %s, value %s", + serverMatchDuidCompare, serverMatchDuidValueType, serverMatchDuidValue) + } + serverMatchDuidList = append( + serverMatchDuidList, + serverMatchDuidCompare+idSeparator+serverMatchDuidValueType+idSeparator+serverMatchDuidValue, + ) + configSet = append(configSet, setPrefix+"server-match duid "+ + serverMatchDuidCompare+" "+ + serverMatchDuidValueType+" "+ + "\""+serverMatchDuidValue+"\" "+ + serverMatchDuid["action"].(string)) + } + if v := d.Get("service_profile").(string); v != "" { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("service_profile not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"service-profile \""+v+"\"") + } + if v := d.Get("short_cycle_protection_lockout_max_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-max-time "+strconv.Itoa(v)) + } + if v := d.Get("short_cycle_protection_lockout_min_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-min-time "+strconv.Itoa(v)) + } + if d.Get("source_ip_change").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("source_ip_change not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"source-ip-change") + } + if d.Get("vendor_specific_information_host_name").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("vendor_specific_information_host_name not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"vendor-specific-information host-name") + } + if d.Get("vendor_specific_information_location").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("vendor_specific_information_location not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"vendor-specific-information location") + } + + return clt.configSet(configSet, junSess) +} + +func setForwardingOptionsDhcpRelayGroupInterface( + interFace map[string]interface{}, setPrefixInterface, version string, +) ([]string, error) { + configSet := make([]string, 0) + + setPrefix := setPrefixInterface + "interface " + interFace["name"].(string) + " " + + configSet = append(configSet, setPrefix) + if v := interFace["access_profile"].(string); v != "" { + configSet = append(configSet, setPrefix+"access-profile \""+v+"\"") + } + if dynProfile := interFace["dynamic_profile"].(string); dynProfile != "" { + configSet = append(configSet, setPrefix+"dynamic-profile \""+dynProfile+"\"") + if interFace["dynamic_profile_use_primary"].(string) != "" && + interFace["dynamic_profile_aggregate_clients"].(bool) { + return configSet, fmt.Errorf("conflict between "+ + "dynamic_profile_use_primary and dynamic_profile_aggregate_clients in interface %s", interFace["name"].(string)) + } + if interFace["dynamic_profile_aggregate_clients"].(bool) { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients") + if v := interFace["dynamic_profile_aggregate_clients_action"].(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients "+v) + } + } else if interFace["dynamic_profile_aggregate_clients_action"].(string) != "" { + return configSet, fmt.Errorf("dynamic_profile_aggregate_clients need to be true with "+ + "dynamic_profile_aggregate_clients_action in interface %s", interFace["name"].(string)) + } + if v := interFace["dynamic_profile_use_primary"].(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile use-primary \""+v+"\"") + } + } else if interFace["dynamic_profile_use_primary"].(string) != "" || + interFace["dynamic_profile_aggregate_clients"].(bool) || + interFace["dynamic_profile_aggregate_clients_action"].(string) != "" { + return configSet, fmt.Errorf("dynamic_profile need to be set with "+ + "dynamic_profile_use_primary, dynamic_profile_aggregate_clients "+ + "or dynamic_profile_aggregate_clients_action in interface %s", interFace["name"].(string)) + } + if interFace["exclude"].(bool) { + configSet = append(configSet, setPrefix+"exclude") + } + for _, v := range interFace["overrides_v4"].([]interface{}) { + if version == "v6" { + return configSet, fmt.Errorf("overrides_v4 not compatible if version = v6") + } + if v == nil { + return configSet, fmt.Errorf("overrides_v4 block in interface %s is empty", interFace["name"].(string)) + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV4( + v.(map[string]interface{}), setPrefix) + if err != nil { + return configSet, err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range interFace["overrides_v6"].([]interface{}) { + if version == "v4" { + return configSet, fmt.Errorf("overrides_v6 not compatible if version = v4") + } + if v == nil { + return configSet, fmt.Errorf("overrides_v6 block in interface %s is empty", interFace["name"].(string)) + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV6( + v.(map[string]interface{}), setPrefix) + if err != nil { + return configSet, err + } + configSet = append(configSet, configSetOverrides...) + } + if v := interFace["service_profile"].(string); v != "" { + configSet = append(configSet, setPrefix+"service-profile \""+v+"\"") + } + if v := interFace["short_cycle_protection_lockout_max_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-max-time "+strconv.Itoa(v)) + } + if v := interFace["short_cycle_protection_lockout_min_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-min-time "+strconv.Itoa(v)) + } + if interFace["trace"].(bool) { + configSet = append(configSet, setPrefix+"trace") + } + if v := interFace["upto"].(string); v != "" { + configSet = append(configSet, setPrefix+"upto "+v) + } + + return configSet, nil +} + +func readForwardingOptionsDhcpRelayGroup(name, instance, version string, clt *Client, junSess *junosSession, +) (fwdOptsDhcpRelGroupOptions, error) { + var confRead fwdOptsDhcpRelGroupOptions + confRead.minimumWaitTime = -1 // default = -1 + + showCmd := cmdShowConfig + if instance != defaultW { + showCmd += routingInstancesWS + instance + " " + } + showCmd += "forwarding-options dhcp-relay " + if version == "v6" { + showCmd += "dhcpv6 group " + name + } else { + showCmd += "group " + name + } + showConfig, err := clt.command(showCmd+pipeDisplaySetRelative, junSess) + if err != nil { + return confRead, err + } + if showConfig != emptyW { + confRead.name = name + confRead.routingInstance = instance + confRead.version = version + for _, item := range strings.Split(showConfig, "\n") { + itemTrim := strings.TrimPrefix(item, setLS) + if strings.Contains(item, xmlStartTagConfigOut) { + continue + } + if strings.Contains(item, xmlEndTagConfigOut) { + break + } + switch { + case strings.HasPrefix(itemTrim, "access-profile "): + confRead.accessProfile = strings.Trim(strings.TrimPrefix(itemTrim, "access-profile "), "\"") + case itemTrim == "active-server-group allow-server-change": + confRead.activeServerGroupAllowServerChange = true + case strings.HasPrefix(itemTrim, "active-server-group "): + confRead.activeServerGroup = strings.TrimPrefix(itemTrim, "active-server-group ") + case strings.HasPrefix(itemTrim, "authentication password "): + confRead.authenticationPassword = strings.Trim(strings.TrimPrefix(itemTrim, "authentication password "), "\"") + case strings.HasPrefix(itemTrim, "authentication username-include "): + if len(confRead.authenticationUsernameInclude) == 0 { + confRead.authenticationUsernameInclude = append(confRead.authenticationUsernameInclude, + genForwardingOptionsDhcpRelayAuthUsernameInclude()) + } + readForwardingOptionsDhcpRelayAuthUsernameInclude( + strings.TrimPrefix(itemTrim, "authentication username-include "), + confRead.authenticationUsernameInclude[0], + ) + case strings.HasPrefix(itemTrim, "client-response-ttl "): + var err error + confRead.clientResponseTTL, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "client-response-ttl ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "description "): + confRead.description = strings.Trim(strings.TrimPrefix(itemTrim, "description "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients"): + confRead.dynamicProfileAggregateClients = true + if strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients ") { + confRead.dynamicProfileAggregateClientsAction = strings.TrimPrefix(itemTrim, "dynamic-profile aggregate-clients ") + } + case strings.HasPrefix(itemTrim, "dynamic-profile use-primary "): + confRead.dynamicProfileUsePrimary = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile use-primary "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile "): + confRead.dynamicProfile = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile "), "\"") + case strings.HasPrefix(itemTrim, "forward-only"): + confRead.forwardOnly = true + if strings.HasPrefix(itemTrim, "forward-only routing-instance ") { + confRead.forwardOnlyRoutingInstance = strings.Trim(strings.TrimPrefix( + itemTrim, "forward-only routing-instance "), "\"") + } + case strings.HasPrefix(itemTrim, "interface "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "interface "), " ") + interFace := map[string]interface{}{ + "name": itemTrimSplit[0], + "access_profile": "", + "dynamic_profile": "", + "dynamic_profile_aggregate_clients": false, + "dynamic_profile_aggregate_clients_action": "", + "dynamic_profile_use_primary": "", + "exclude": false, + "overrides_v4": make([]map[string]interface{}, 0), + "overrides_v6": make([]map[string]interface{}, 0), + "service_profile": "", + "short_cycle_protection_lockout_max_time": 0, + "short_cycle_protection_lockout_min_time": 0, + "trace": false, + "upto": "", + } + confRead.interFace = copyAndRemoveItemMapList("name", interFace, confRead.interFace) + itemTrimInterface := strings.TrimPrefix(itemTrim, "interface "+itemTrimSplit[0]) + if strings.HasPrefix(itemTrimInterface, " ") { + if err := readForwardingOptionsDhcpRelayGroupInterface( + strings.TrimPrefix(itemTrimInterface, " "), version, interFace); err != nil { + return confRead, err + } + } + confRead.interFace = append(confRead.interFace, interFace) + case strings.HasPrefix(itemTrim, "lease-time-validation"): + if len(confRead.leaseTimeValidation) == 0 { + confRead.leaseTimeValidation = append(confRead.leaseTimeValidation, map[string]interface{}{ + "lease_time_threshold": 0, + "violation_action_drop": false, + }) + } + switch { + case strings.HasPrefix(itemTrim, "lease-time-validation lease-time-threshold "): + var err error + confRead.leaseTimeValidation[0]["lease_time_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "lease-time-validation lease-time-threshold ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "lease-time-validation violation-action drop": + confRead.leaseTimeValidation[0]["violation_action_drop"] = true + } + case strings.HasPrefix(itemTrim, "liveness-detection failure-action "): + confRead.livenessDetectionFailureAction = strings.TrimPrefix(itemTrim, "liveness-detection failure-action ") + case strings.HasPrefix(itemTrim, "liveness-detection method bfd "): + if len(confRead.livenessDetectionMethodBfd) == 0 { + confRead.livenessDetectionMethodBfd = append(confRead.livenessDetectionMethodBfd, map[string]interface{}{ + "detection_time_threshold": -1, + "holddown_interval": -1, + "minimum_interval": 0, + "minimum_receive_interval": 0, + "multiplier": 0, + "no_adaptation": false, + "session_mode": "", + "transmit_interval_minimum": 0, + "transmit_interval_threshold": -1, + "version": "", + }) + } + itemTrimLiveDetMethBfd := strings.TrimPrefix(itemTrim, "liveness-detection method bfd ") + var err error + switch { + case strings.HasPrefix(itemTrimLiveDetMethBfd, "detection-time threshold "): + confRead.livenessDetectionMethodBfd[0]["detection_time_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "detection-time threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "holddown-interval "): + confRead.livenessDetectionMethodBfd[0]["holddown_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "holddown-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-receive-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_receive_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "minimum-receive-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "multiplier "): + confRead.livenessDetectionMethodBfd[0]["multiplier"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "multiplier ")) + case itemTrimLiveDetMethBfd == "no-adaptation": + confRead.livenessDetectionMethodBfd[0]["no_adaptation"] = true + case strings.HasPrefix(itemTrimLiveDetMethBfd, "session-mode "): + confRead.livenessDetectionMethodBfd[0]["session_mode"] = strings.TrimPrefix( + itemTrimLiveDetMethBfd, "session-mode ") + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_minimum"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "transmit-interval minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval threshold "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "transmit-interval threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "version "): + confRead.livenessDetectionMethodBfd[0]["version"] = strings.TrimPrefix(itemTrimLiveDetMethBfd, "version ") + } + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection "): + if len(confRead.livenessDetectionMethodLayer2) == 0 { + confRead.livenessDetectionMethodLayer2 = append(confRead.livenessDetectionMethodLayer2, map[string]interface{}{ + "max_consecutive_retries": 0, + "transmit_interval": 0, + }) + } + var err error + switch { + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection max-consecutive-retries "): + confRead.livenessDetectionMethodLayer2[0]["max_consecutive_retries"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "liveness-detection method layer2-liveness-detection max-consecutive-retries ")) + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection transmit-interval "): + confRead.livenessDetectionMethodLayer2[0]["transmit_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "liveness-detection method layer2-liveness-detection transmit-interval ")) + } + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "maximum-hop-count "): + var err error + confRead.maximumHopCount, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "maximum-hop-count ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "minimum-wait-time "): + var err error + confRead.minimumWaitTime, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "minimum-wait-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "overrides "): + if version == "v4" { + if len(confRead.overridesV4) == 0 { + confRead.overridesV4 = append(confRead.overridesV4, + genForwardingOptionsDhcpRelayOverridesV4()) + } + if err := readForwardingOptionsDhcpRelayOverridesV4( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV4[0], + ); err != nil { + return confRead, err + } + } else if version == "v6" { + if len(confRead.overridesV6) == 0 { + confRead.overridesV6 = append(confRead.overridesV6, + genForwardingOptionsDhcpRelayOverridesV6()) + } + if err := readForwardingOptionsDhcpRelayOverridesV6( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV6[0], + ); err != nil { + return confRead, err + } + } + case strings.HasPrefix(itemTrim, "relay-agent-interface-id"): + if len(confRead.relayAgentInterfaceID) == 0 { + confRead.relayAgentInterfaceID = append(confRead.relayAgentInterfaceID, + genForwardingOptionsDhcpRelayAgentID(true)) + } + if strings.HasPrefix(itemTrim, "relay-agent-interface-id ") { + readForwardingOptionsDhcpRelayAgentID( + strings.TrimPrefix(itemTrim, "relay-agent-interface-id "), + confRead.relayAgentInterfaceID[0], + ) + } + case itemTrim == "relay-agent-option-79": + confRead.relayAgentOption79 = true + case strings.HasPrefix(itemTrim, "relay-agent-remote-id"): + if len(confRead.relayAgentRemoteID) == 0 { + confRead.relayAgentRemoteID = append(confRead.relayAgentRemoteID, + genForwardingOptionsDhcpRelayAgentID(false)) + } + if strings.HasPrefix(itemTrim, "relay-agent-remote-id ") { + readForwardingOptionsDhcpRelayAgentID( + strings.TrimPrefix(itemTrim, "relay-agent-remote-id "), + confRead.relayAgentRemoteID[0], + ) + } + case strings.HasPrefix(itemTrim, "relay-option-82"): + if len(confRead.relayOption82) == 0 { + confRead.relayOption82 = append(confRead.relayOption82, + genForwardingOptionsDhcpRelayOption82()) + } + if strings.HasPrefix(itemTrim, "relay-option-82 ") { + readForwardingOptionsDhcpRelayOption82( + strings.TrimPrefix(itemTrim, "relay-option-82 "), + confRead.relayOption82[0], + ) + } + case strings.HasPrefix(itemTrim, "relay-option"): + if len(confRead.relayOption) == 0 { + confRead.relayOption = append(confRead.relayOption, + genForwardingOptionsDhcpRelayOption()) + } + if strings.HasPrefix(itemTrim, "relay-option ") { + if err := readForwardingOptionsDhcpRelayOption( + strings.TrimPrefix(itemTrim, "relay-option "), + confRead.relayOption[0], + ); err != nil { + return confRead, err + } + } + case itemTrim == "remote-id-mismatch disconnect": + confRead.remoteIDMismatchDisconnect = true + case itemTrim == "route-suppression access": + confRead.routeSuppressionAccess = true + case itemTrim == "route-suppression access-internal": + confRead.routeSuppressionAccessInternal = true + case itemTrim == "route-suppression destination": + confRead.routeSuppressionDestination = true + case strings.HasPrefix(itemTrim, "server-match address "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "server-match address "), " ") + if len(itemTrimSplit) < 2 { + return confRead, fmt.Errorf("can't read values for server_match_address in '%s'", itemTrim) + } + confRead.serverMatchAddress = append(confRead.serverMatchAddress, map[string]interface{}{ + "address": itemTrimSplit[0], + "action": itemTrimSplit[1], + }) + case strings.HasPrefix(itemTrim, "server-match default-action "): + confRead.serverMatchDefaultAction = strings.TrimPrefix(itemTrim, "server-match default-action ") + case strings.HasPrefix(itemTrim, "server-match duid "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "server-match duid "), " ") + if len(itemTrimSplit) < 4 { + return confRead, fmt.Errorf("can't read values for server_match_duid in '%s'", itemTrim) + } + if strings.Contains(itemTrimSplit[2], "\"") { + action := itemTrimSplit[len(itemTrimSplit)-1] + value := strings.Trim(strings.Join(itemTrimSplit[2:len(itemTrimSplit)-1], " "), "\"") + itemTrimSplit[2] = value + itemTrimSplit[3] = action + } + confRead.serverMatchDuid = append(confRead.serverMatchDuid, map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": itemTrimSplit[2], + "action": itemTrimSplit[3], + }) + case strings.HasPrefix(itemTrim, "service-profile "): + confRead.serviceProfile = strings.Trim(strings.TrimPrefix(itemTrim, "service-profile "), "\"") + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-max-time "): + var err error + confRead.shortCycleProtectionLockoutMaxTime, err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-max-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-min-time "): + var err error + confRead.shortCycleProtectionLockoutMinTime, err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-min-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "source-ip-change": + confRead.sourceIPChange = true + case itemTrim == "vendor-specific-information host-name": + confRead.vendorSpecificInformationHostName = true + case itemTrim == "vendor-specific-information location": + confRead.vendorSpecificInformationLocation = true + } + } + } + + return confRead, nil +} + +func readForwardingOptionsDhcpRelayGroupInterface(itemTrim, version string, interFace map[string]interface{}) error { + var err error + switch { + case strings.HasPrefix(itemTrim, "access-profile "): + interFace["access_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "access-profile "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile "): + switch { + case strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients"): + interFace["dynamic_profile_aggregate_clients"] = true + if strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients ") { + interFace["dynamic_profile_aggregate_clients_action"] = strings.TrimPrefix( + itemTrim, "dynamic-profile aggregate-clients ") + } + case strings.HasPrefix(itemTrim, "dynamic-profile use-primary "): + interFace["dynamic_profile_use_primary"] = strings.Trim(strings.TrimPrefix( + itemTrim, "dynamic-profile use-primary "), "\"") + default: + interFace["dynamic_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile "), "\"") + } + case itemTrim == "exclude": + interFace["exclude"] = true + case strings.HasPrefix(itemTrim, "overrides "): + if version == "v4" { + if len(interFace["overrides_v4"].([]map[string]interface{})) == 0 { + interFace["overrides_v4"] = append( + interFace["overrides_v4"].([]map[string]interface{}), + genForwardingOptionsDhcpRelayOverridesV4(), + ) + } + if err := readForwardingOptionsDhcpRelayOverridesV4( + strings.TrimPrefix(itemTrim, "overrides "), + interFace["overrides_v4"].([]map[string]interface{})[0], + ); err != nil { + return err + } + } else if version == "v6" { + if len(interFace["overrides_v6"].([]map[string]interface{})) == 0 { + interFace["overrides_v6"] = append( + interFace["overrides_v6"].([]map[string]interface{}), + genForwardingOptionsDhcpRelayOverridesV6(), + ) + } + if err := readForwardingOptionsDhcpRelayOverridesV6( + strings.TrimPrefix(itemTrim, "overrides "), + interFace["overrides_v6"].([]map[string]interface{})[0], + ); err != nil { + return err + } + } + case strings.HasPrefix(itemTrim, "service-profile "): + interFace["service_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "service-profile "), "\"") + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-max-time "): + interFace["short_cycle_protection_lockout_max_time"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-max-time ")) + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-min-time "): + interFace["short_cycle_protection_lockout_min_time"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-min-time ")) + case itemTrim == "trace": + interFace["trace"] = true + case strings.HasPrefix(itemTrim, "upto "): + interFace["upto"] = strings.TrimPrefix(itemTrim, "upto ") + } + if err != nil { + return fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + + return nil +} + +func delForwardingOptionsDhcpRelayGroup(name, instance, version string, clt *Client, junSess *junosSession) error { + configSet := make([]string, 0, 1) + switch { + case instance == defaultW && version == "v6": + configSet = append(configSet, deleteLS+"forwarding-options dhcp-relay dhcpv6 group "+name) + case instance == defaultW && version == "v4": + configSet = append(configSet, deleteLS+"forwarding-options dhcp-relay group "+name) + case instance != defaultW && version == "v6": + configSet = append(configSet, delRoutingInstances+instance+" "+ + "forwarding-options dhcp-relay dhcpv6 group "+name) + case instance != defaultW && version == "v4": + configSet = append(configSet, delRoutingInstances+instance+" "+ + "forwarding-options dhcp-relay group "+name) + } + + return clt.configSet(configSet, junSess) +} + +func fillForwardingOptionsDhcpRelayGroupData( + d *schema.ResourceData, fwdOptsDhcpRelGroupOptions fwdOptsDhcpRelGroupOptions, +) { + if tfErr := d.Set( + "name", + fwdOptsDhcpRelGroupOptions.name, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "routing_instance", + fwdOptsDhcpRelGroupOptions.routingInstance, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "version", + fwdOptsDhcpRelGroupOptions.version, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "access_profile", + fwdOptsDhcpRelGroupOptions.accessProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "active_server_group", + fwdOptsDhcpRelGroupOptions.activeServerGroup, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "active_server_group_allow_server_change", + fwdOptsDhcpRelGroupOptions.activeServerGroupAllowServerChange, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "authentication_password", + fwdOptsDhcpRelGroupOptions.authenticationPassword, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "authentication_username_include", + fwdOptsDhcpRelGroupOptions.authenticationUsernameInclude, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "client_response_ttl", + fwdOptsDhcpRelGroupOptions.clientResponseTTL, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "description", + fwdOptsDhcpRelGroupOptions.description, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile", + fwdOptsDhcpRelGroupOptions.dynamicProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_aggregate_clients", + fwdOptsDhcpRelGroupOptions.dynamicProfileAggregateClients, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_aggregate_clients_action", + fwdOptsDhcpRelGroupOptions.dynamicProfileAggregateClientsAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_use_primary", + fwdOptsDhcpRelGroupOptions.dynamicProfileUsePrimary, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_only", + fwdOptsDhcpRelGroupOptions.forwardOnly, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_only_routing_instance", + fwdOptsDhcpRelGroupOptions.forwardOnlyRoutingInstance, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "interface", + fwdOptsDhcpRelGroupOptions.interFace, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "lease_time_validation", + fwdOptsDhcpRelGroupOptions.leaseTimeValidation, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_failure_action", + fwdOptsDhcpRelGroupOptions.livenessDetectionFailureAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_method_bfd", + fwdOptsDhcpRelGroupOptions.livenessDetectionMethodBfd, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_method_layer2", + fwdOptsDhcpRelGroupOptions.livenessDetectionMethodLayer2, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "maximum_hop_count", + fwdOptsDhcpRelGroupOptions.maximumHopCount, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "minimum_wait_time", + fwdOptsDhcpRelGroupOptions.minimumWaitTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "overrides_v4", + fwdOptsDhcpRelGroupOptions.overridesV4, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "overrides_v6", + fwdOptsDhcpRelGroupOptions.overridesV6, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_interface_id", + fwdOptsDhcpRelGroupOptions.relayAgentInterfaceID, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_option_79", + fwdOptsDhcpRelGroupOptions.relayAgentOption79, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_remote_id", + fwdOptsDhcpRelGroupOptions.relayAgentRemoteID, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_option", + fwdOptsDhcpRelGroupOptions.relayOption, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_option_82", + fwdOptsDhcpRelGroupOptions.relayOption82, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "remote_id_mismatch_disconnect", + fwdOptsDhcpRelGroupOptions.remoteIDMismatchDisconnect, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_access", + fwdOptsDhcpRelGroupOptions.routeSuppressionAccess, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_access_internal", + fwdOptsDhcpRelGroupOptions.routeSuppressionAccessInternal, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_destination", + fwdOptsDhcpRelGroupOptions.routeSuppressionDestination, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_address", + fwdOptsDhcpRelGroupOptions.serverMatchAddress, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_default_action", + fwdOptsDhcpRelGroupOptions.serverMatchDefaultAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_duid", + fwdOptsDhcpRelGroupOptions.serverMatchDuid, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "service_profile", + fwdOptsDhcpRelGroupOptions.serviceProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "short_cycle_protection_lockout_max_time", + fwdOptsDhcpRelGroupOptions.shortCycleProtectionLockoutMaxTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "short_cycle_protection_lockout_min_time", + fwdOptsDhcpRelGroupOptions.shortCycleProtectionLockoutMinTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "source_ip_change", + fwdOptsDhcpRelGroupOptions.sourceIPChange, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "vendor_specific_information_host_name", + fwdOptsDhcpRelGroupOptions.vendorSpecificInformationHostName, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "vendor_specific_information_location", + fwdOptsDhcpRelGroupOptions.vendorSpecificInformationLocation, + ); tfErr != nil { + panic(tfErr) + } +} diff --git a/junos/resource_forwardingoptions_dhcprelay_group_test.go b/junos/resource_forwardingoptions_dhcprelay_group_test.go new file mode 100644 index 00000000..d41d0482 --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay_group_test.go @@ -0,0 +1,531 @@ +package junos_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccJunosForwardingOptionsDhcpRelayGroup_basic(t *testing.T) { + if os.Getenv("TESTACC_ROUTER") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosForwardingOptionsDhcpRelayGroupConfigCreate(), + }, + { + Config: testAccJunosForwardingOptionsDhcpRelayGroupConfigUpdate(), + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_group.testacc_dhcprelaygroup_v4_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_group.testacc_dhcprelaygroup_v6_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_group.testacc_dhcprelaygroup_v4_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay_group.testacc_dhcprelaygroup_v6_ri", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +func testAccJunosForwardingOptionsDhcpRelayGroupConfigCreate() string { + return ` +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_default" { + name = "testacc_dhcprelaygroup_v4_default" + + dynamic_profile = "junos-default-profile" +} + +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v6_default" { + name = "testacc_dhcprelaygroup_v6_default" + version = "v6" + + interface { + name = "ge-0/0/3.1" + upto = "ge-0/0/3.3" + exclude = true + } +} + +resource "junos_routing_instance" "testacc_dhcprelaygroup" { + name = "testacc_dhcprelaygroup" +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_ri" { + name = "testacc_dhcprelaygroup_v4_ri" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + + interface { + name = "ge-0/0/3.0" + dynamic_profile = "junos-default-profile" + trace = true + } + interface { + name = "ge-0/0/3.1" + dynamic_profile = "junos-default-profile" + } +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v6_ri" { + name = "testacc_dhcprelaygroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + version = "v6" + + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} + +// nolint: lll,nolintlint +func testAccJunosForwardingOptionsDhcpRelayGroupConfigUpdate() string { + return ` +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_default" { + name = "testacc_dhcprelaygroup_v4_default" + + interface { + name = "ge-0/0/3.1" + upto = "ge-0/0/3.3" + exclude = true + } + active_server_group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v4_default.name + active_server_group_allow_server_change = true + dynamic_profile = "junos-default-profile" + authentication_password = "test1#1" + authentication_username_include { + circuit_type = true + client_id = true + client_id_exclude_headers = true + client_id_use_automatic_ascii_hex_encoding = true + delimiter = "#" + domain_name = "a domain" + interface_description = "logical" + interface_name = true + mac_address = true + option_60 = true + option_82 = true + option_82_circuit_id = true + option_82_remote_id = true + routing_instance_name = true + user_prefix = "user_#1_" + vlan_tags = true + } + client_response_ttl = 60 + description = "testacc v4 default" + forward_only = true + forward_only_routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + maximum_hop_count = 8 + minimum_wait_time = 0 + + overrides_v4 { + allow_snooped_clients = true + always_write_giaddr = true + always_write_option_82 = true + delay_authentication = true + disable_relay = true + layer2_unicast_replies = true + no_bind_on_request = true + proxy_mode = true + relay_source = "lo0.1" + replace_ip_source_with_giaddr = true + send_release_on_delete = true + trust_option_82 = true + user_defined_option_82 = "#test" + } + relay_option { + option_60 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "local-server-group" + group = junos_system_services_dhcp_localserver_group.testacc_dhcprelaygroup_v4_default.name + } + option_60_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v4_default.name + } + option_77 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v4_default.name + } + option_77 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v4_default.name + } + option_77_default_action { + action = "local-server-group" + group = junos_system_services_dhcp_localserver_group.testacc_dhcprelaygroup_v4_default.name + } + option_order = ["77", "60"] + } + relay_option_82 { + circuit_id { + include_irb_and_l2 = true + keep_incoming_circuit_id = true + no_vlan_interface_name = true + prefix_host_name = true + prefix_routing_instance_name = true + use_interface_description = "logical" + } + exclude_relay_agent_identifier = true + link_selection = true + remote_id { + include_irb_and_l2 = true + keep_incoming_remote_id = true + no_vlan_interface_name = true + prefix_routing_instance_name = true + use_interface_description = "device" + } + vendor_specific_host_name = true + vendor_specific_location = true + } + route_suppression_destination = true + server_match_address { + address = "192.0.2.1/30" + action = "create-relay-entry" + } + server_match_default_action = "forward-only" + source_ip_change = true +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelaygroup_v4_default" { + name = "testacc_dhcprelaygroup_v4_default" +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcprelaygroup_v4_default" { + name = "testacc_dhcprelaygroup_v4_default" + + dynamic_profile = "junos-default-profile" +} + +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_default2" { + name = "testacc_dhcprelaygroup_v4_default2" + + relay_option_82 { + circuit_id { + vlan_id_only = true + } + } +} + +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v6_default" { + name = "testacc_dhcprelaygroup_v6_default" + version = "v6" + + authentication_username_include { + client_id = true + relay_agent_interface_id = true + relay_agent_remote_id = true + relay_agent_subscriber_id = true + } + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-dhcp-profile" + interface { + name = "ge-0/0/3.1" + upto = "ge-0/0/3.3" + overrides_v6 { + always_process_option_request_option = true + asymmetric_lease_time = 900 + asymmetric_prefix_lease_time = 1000 + client_negotiation_match_incoming_interface = true + delay_authentication = true + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + no_allow_snooped_clients = true + no_bind_on_request = true + relay_source = "lo0.1" + send_release_on_delete = true + } + } + lease_time_validation {} + liveness_detection_method_layer2 { + max_consecutive_retries = 4 + transmit_interval = 305 + } + overrides_v6 { + always_process_option_request_option = true + asymmetric_lease_time = 900 + asymmetric_prefix_lease_time = 1000 + client_negotiation_match_incoming_interface = true + delay_authentication = true + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + no_allow_snooped_clients = true + no_bind_on_request = true + relay_source = "lo0.1" + send_release_on_delete = true + } + relay_agent_interface_id {} + relay_agent_option_79 = true + relay_agent_remote_id {} + relay_option { + option_15 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "drop" + } + option_15 { + compare = "equals" + value_type = "hexadecimal" + value = "AABBff" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v6_default.name + } + option_15_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v6_default.name + } + option_16 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v6_default.name + } + option_16 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v6_default.name + } + option_16_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelaygroup_v6_default.name + } + option_order = ["16", "15"] + } + route_suppression_access = true + route_suppression_access_internal = true + server_match_duid { + compare = "equals" + value_type = "ascii" + value = " test_space " + action = "forward-only" + } + vendor_specific_information_host_name = true + vendor_specific_information_location = true +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelaygroup_v6_default" { + name = "testacc_dhcprelaygroup_v6_default" + version = "v6" +} + +resource "junos_routing_instance" "testacc_dhcprelaygroup" { + name = "testacc_dhcprelaygroup" +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_ri" { + name = "testacc_dhcprelaygroup_v4_ri" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + + authentication_username_include { + client_id = true + client_id_exclude_headers = true + client_id_use_automatic_ascii_hex_encoding = true + option_82 = true + } + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + interface { + name = "ge-0/0/3.0" + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-profile" + trace = true + overrides_v4 { + no_unicast_replies = true + } + } + interface { + name = "ge-0/0/3.1" + dynamic_profile = "junos-default-profile" + } + interface { + name = "ge-0/0/3.4" + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + dynamic_profile_aggregate_clients_action = "merge" + overrides_v4 { + allow_no_end_option = true + asymmetric_lease_time = 900 + bootp_support = true + client_discover_match = "option60-and-option82" + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + } + service_profile = "a_service#1" + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 + } + lease_time_validation { + lease_time_threshold = 60099 + violation_action_drop = true + } + liveness_detection_method_bfd { + detection_time_threshold = 200000 + holddown_interval = 2 + minimum_interval = 30003 + minimum_receive_interval = 30004 + multiplier = 5 + no_adaptation = true + session_mode = "multihop" + transmit_interval_minimum = 30006 + transmit_interval_threshold = 30066 + version = "automatic" + } + liveness_detection_failure_action = "log-only" + overrides_v4 { + client_discover_match = "incoming-interface" + } + relay_option_82 { + circuit_id { + use_vlan_id = true + } + remote_id { + use_vlan_id = true + } + } + route_suppression_destination = true + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_ri2" { + name = "testacc_dhcprelaygroup_v4_ri2" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + overrides_v4 { + no_allow_snooped_clients = true + } + relay_option { + option_60 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "forward-only" + } + option_60_default_action { + action = "drop" + } + option_77 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "forward-only" + } + option_77 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "drop" + } + option_77_default_action { + action = "forward-only" + } + option_order = ["77", "60"] + } + relay_option_82 { + circuit_id { + user_defined = true + } + remote_id { + hostname_only = true + } + } +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v4_ri3" { + name = "testacc_dhcprelaygroup_v4_ri3" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + + relay_option_82 { + remote_id { + prefix_host_name = true + use_string = " a string" + } + server_id_override = true + } + +} +resource "junos_forwardingoptions_dhcprelay_group" "testacc_dhcprelaygroup_v6_ri" { + name = "testacc_dhcprelaygroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcprelaygroup.name + version = "v6" + + + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + dynamic_profile_aggregate_clients_action = "merge" + + overrides_v6 { + allow_snooped_clients = true + } + relay_agent_interface_id { + include_irb_and_l2 = true + keep_incoming_id = true + keep_incoming_id_strict = true + no_vlan_interface_name = true + prefix_host_name = true + prefix_routing_instance_name = true + use_interface_description = "logical" + use_option_82 = true + } + relay_agent_remote_id { + keep_incoming_id = true + use_option_82 = true + use_option_82_strict = true + use_vlan_id = true + } + relay_option { + option_15 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "forward-only" + } + option_15 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "drop" + } + option_15_default_action { + action = "drop" + } + option_16 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "forward-only" + } + option_16_default_action { + action = "forward-only" + } + option_order = ["15", "16"] + } + remote_id_mismatch_disconnect = true + route_suppression_access = true + service_profile = "service-pro#2" + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} From 3422b7b920dbdd1a9bfccee58832599fba293e13 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 4 Oct 2022 13:38:04 +0200 Subject: [PATCH 12/17] add forwardingoptions_dhcprelay resource Fix #423 --- CHANGELOG.md | 1 + docs/resources/forwardingoptions_dhcprelay.md | 578 +++++ junos/provider.go | 1 + junos/resource_forwardingoptions_dhcprelay.go | 1970 +++++++++++++++++ ...source_forwardingoptions_dhcprelay_test.go | 519 +++++ 5 files changed, 3069 insertions(+) create mode 100644 docs/resources/forwardingoptions_dhcprelay.md create mode 100644 junos/resource_forwardingoptions_dhcprelay.go create mode 100644 junos/resource_forwardingoptions_dhcprelay_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 25d2d896..0d9a012f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: +* add `junos_forwardingoptions_dhcprelay` resource (Fixes #423) * add `junos_forwardingoptions_dhcprelay_group` resource (Fixes #423) * add `junos_forwardingoptions_dhcprelay_servergroup` resource (Fixes #423) diff --git a/docs/resources/forwardingoptions_dhcprelay.md b/docs/resources/forwardingoptions_dhcprelay.md new file mode 100644 index 00000000..f097edf1 --- /dev/null +++ b/docs/resources/forwardingoptions_dhcprelay.md @@ -0,0 +1,578 @@ +--- +page_title: "Junos: junos_forwardingoptions_dhcprelay" +--- + +# junos_forwardingoptions_dhcprelay + +-> **Note:** This resource should only be created **once** for each version `v4` or `v6` in +root level or in each routing-instance. It's used to configure static (not object) options in +`forwarding-options dhcp-relay [dhcpv6]` block in +root or routing-instance level. + +Configure static configuration in `forwarding-options dhcp-relay [dhcpv6]` block for +root or routing-instance level. + +## Example Usage + +```hcl +# Configure default dhcp-relay options +resource "junos_forwardingoptions_dhcprelay" "demo" { + active_server_group = junos_forwardingoptions_dhcprelay_servergroup.demo.name +} + +resource "junos_forwardingoptions_dhcprelay_servergroup" "demo" { + name = "demo" + ip_address = [ + "192.0.2.8", + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +- **routing_instance** (Optional, String, Forces new resource) + Routing instance if not root level. + Need to be `default` or name of routing instance. + Defaults to `default` +- **version** (Optional, String, Forces new resource) + Version for DHCP or DHCPv6. + Need to be `v4` or `v6`. +- **access_profile** (Optional, String) + Access profile to use for AAA services. +- **active_leasequery** (Optional, Block) + DHCPv4 active leasequery configuration. + - **idle_timeout** (Optional, Number) + Idle timeout in seconds (10..3600 seconds). + - **peer_address** (Optional, String) + Server ip address. + - **timeout** (Optional, Number) + Read/write timeout in seconds (10..3600 seconds). + - **topology_discover** (Optional, Boolean) + Topology discovery. +- **active_server_group** (Optional, String) + Name of DHCP server group. +- **active_server_group_allow_server_change** (Optional, Boolean) + Accept DHCP-ACK from any server in this group. + `version` need to be `v4`. +- **arp_inspection** (Optional, Boolean) + Enable Dynamic ARP Inspection. + `version` need to be `v4`. +- **authentication_password** (Optional, String) + DHCP authentication, username password to use. +- **authentication_username_include** (Optional, Block) + DHCP authentication, add username options. + At least one of arguments of block need to be set. + - **circuit_type** (Optional, Boolean) + Include circuit type. + - **client_id** (Optional, Boolean) + Include client ID. + - **client_id_exclude_headers** (Optional, Boolean) + Exclude all the headers. + `client_id` need to be true. + - **client_id_use_automatic_ascii_hex_encoding** (Optional, Boolean) + Use automatic ascii hex username encoding. + `client_id` need to be true. + - **delimiter** (Optional, String) + Change delimiter/separator character. + One character maximum. + - **domain_name** (Optional, String) + Add domain name. + - **interface_description** (Optional, String) + Include interface description. + Need to be `device` or `logical`. + - **interface_name** (Optional, Boolean) + Include interface name. + - **mac_address** (Optional, Boolean) + Include MAC address. + - **option_60** (Optional, Boolean) + Include option 60. + `version` need to be `v4`. + - **option_82** (Optional, Boolean) + Include option 82. + `version` need to be `v4`. + - **option_82_circuit_id** (Optional, Boolean) + Include option 82 circuit-id (sub option 1). + `option_82` need to be true. + - **option_82_remote_id** (Optional, Boolean) + Include option 82 remote-id (sub option 2). + `option_82` need to be true. + - **relay_agent_interface_id** (Optional, Boolean) + Include the relay agent interface ID. + `version` need to be `v6`. + - **relay_agent_remote_id** (Optional, Boolean) + Include the relay agent remote ID. + `version` need to be `v6`. + - **relay_agent_subscriber_id** (Optional, Boolean) + Include the relay agent subscriber ID. + `version` need to be `v6`. + - **routing_instance_name** (Optional, Boolean) + Include routing instance name. + - **user_prefix** (Optional, String) + Add user defined prefix. + - **vlan_tags** (Optional, Boolean) + Include the vlan tag(s). +- **bulk_leasequery** (Optional, Block) + DHCP bulk leasequery configuration. + - **attempts** (Optional, Number) + Number of retry attempts (1..720 with version=v4) (1..10 with version=v6) + - **timeout** (Optional, Number) + Number of seconds (1..10 seconds). + - **trigger_automatic** (Optional, Boolean) + Trigger automatically for bulk leasequery. + `version` need to be `v6`. +- **client_response_ttl** (Optional, Number) + IP time-to-live value to set in responses to client (1..255). + `version` need to be `v4`. +- **duplicate_clients_in_subnet** Optional, String) + Allow duplicate clients in a subnet. + Need to be `incoming-interface` or `option-82`. + `version` need to be `v4`. +- **duplicate_clients_incoming_interface** Optional, Boolean) + Allow duplicate clients on different underlying interfaces. + `version` need to be `v6`. +- **dynamic_profile** (Optional, String) + Dynamic profile to use. +- **dynamic_profile_aggregate_clients** (Optional, Boolean) + Aggregate client profiles. + `dynamic_profile` need to be set. +- **dynamic_profile_aggregate_clients_action** (Optional, String) + Merge or replace the client dynamic profiles. + Need to be `merge` or `replace`. + `dynamic_profile_aggregate_clients` need to be true. +- **dynamic_profile_use_primary** (Optional, String) + Dynamic profile to use on the primary interface. + `dynamic_profile` need to be set. + Conflict with `dynamic_profile_aggregate_clients`. +- **exclude_relay_agent_identifier** (Optional, Boolean) + Exclude relay agent identifier from packets to server. + `version` need to be `v6`. +- **forward_only** (Optional, Boolean) + Forward DHCP packets without creating binding. +- **forward_only_replies** (Optional, Boolean) + Forward-only replies from server to appropriate routing-instance based on options +- **forward_only_routing_instance** (Optional, String) + Name of routing instance to forward-only. + `forward_only` need to be true. +- **forward_snooped_clients** (Optional, String) + Forward snooped (unicast) packets. + Need to be `all-interfaces`, `configured-interfaces` or `non-configured-interfaces`. +- **lease_time_validation** (Optional, Block) + Configure lease time violation validation. + - **lease_time_threshold** (Optional, Number) + Threshold for lease time violation seconds (60..2147483647 seconds). + - **violation_action_drop** (Optional, Boolean) + Lease time validation violation action is drop. +- **leasequery** (Optional, Block) + DHCP leasequery configuration. + - **attempts** (Optional, Number) + Number of retry attempts (1..10). + - **timeout** (Optional, Number) + Number of seconds (1..10 seconds). +- **liveness_detection_failure_action** (Optional, String) + Liveness detection failure action options. + Need to be `clear-binding`, `clear-binding-if-interface-up` or `log-only`. +- **liveness_detection_method_bfd** (Optional, Block) + Liveness detection method BFD options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_layer2`. + - **detection_time_threshold** (Optional, Number) + High detection-time triggering a trap (milliseconds). + - **holddown_interval** (Optional, Number) + Time to hold the session-UP notification to the client (0..255000 milliseconds). + - **minimum_interval** (Optional, Number) + Minimum transmit and receive interval (30000..255000 milliseconds). + - **minimum_receive_interval** (Optional, Number) + Minimum receive interval (30000..255000 milliseconds). + - **multiplier** (Optional, Number) + Detection time multiplier (1..255). + - **no_adaptation** (Optional, Boolean) + Disable adaptation. + - **session_mode** (Optional, String) + BFD single-hop or multihop session-mode. + Need to be `automatic`, `multihop` or `single-hop`. + - **transmit_interval_minimum** (Optional, Number) + Minimum transmit interval (30000..255000 milliseconds). + - **transmit_interval_threshold** (Optional, Number) + High transmit interval triggering a trap (milliseconds) + - **version** (Optional, String) + BFD protocol version number. + Need to be `0`, `1` or `automatic`. +- **liveness_detection_method_layer2** (Optional, Block) + Liveness detection method address resolution options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_bfd`. + - **max_consecutive_retries** (Optional, Number) + Retry attempts (3..6). + - **transmit_interval** (Optional, Number) + Transmit interval for address resolution (300..1800 seconds). +- **maximum_hop_count** (Optional, Number) + Maximum number of hops per packet (1..16) + `version` need to be `v4`. +- **minimum_wait_time** (Optional, Number) + Minimum number of seconds before requests are forwarded (0..30000). + `version` need to be `v4`. +- **no_snoop** (Optional, Boolean) + Do not snoop DHCP packets. +- **overrides_v4** (Optional, Block) + DHCP override processing. + `version` need to be `v4`. + See [below for nested schema](#overrides_v4-arguments). +- **overrides_v6** (Optional, Block) + DHCPv6 override processing. + `version` need to be `v6`. + See [below for nested schema](#overrides_v6-arguments). +- **persistent_storage_automatic** (Optional, Boolean) + Trigger to enable flat file storage. +- **relay_agent_interface_id** (Optional, Block) + DHCPv6 interface-id option processing. + `version` need to be `v6`. + See [below for nested schema](#relay_agent_interface_id-or-relay_agent_remote_id-arguments). +- **relay_agent_option_79** (Optional, Boolean) + Add the client MAC address to the Relay Forward header. + `version` need to be `v6`. +- **relay_agent_remote_id** (Optional, Block) + DHCPv6 remote-id option processing. + `version` need to be `v6`. + See [below for nested schema](#relay_agent_interface_id-or-relay_agent_remote_id-arguments) + but without `keep_incoming_id_strict`. +- **relay_option** (Optional, Block) + DHCP option processing. + See [below for nested schema](#relay_option-arguments). +- **relay_option_82** (Optional, Block) + DHCP option-82 processing. + `version` need to be `v4`. + See [below for nested schema](#relay_option_82-arguments). +- **remote_id_mismatch_disconnect** (Optional, Boolean) + Disconnect session on remote-id mismatch. +- **route_suppression_access** (Optional, Boolean) + Suppress access route addition. + `version` need to be `v6`. +- **route_suppression_access_internal** (Optional, Boolean) + Suppress access-internal route addition. +- **route_suppression_destination** (Optional, Boolean) + Suppress destination route addition. + `version` need to be `v4`. +- **server_match_address** (Optional, Block Set) + For each `address`, server match processing. + - **address** (Required, String) + Server address. + - **action** (Required, String) + Action on address. + Need to be `create-relay-entry` or `forward-only`. +- **server_match_default_action** (Optional, String) + Server match default action. + Need to be `create-relay-entry` or `forward-only`. +- **server_match_duid** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, match duid processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `create-relay-entry` or `forward-only`. +- **server_response_time** (Optional, Number) + Number of seconds in a period of activity between the last server response and an unaswered request. +- **service_profile** (Optional, String) + Dynamic profile to use for default service activation. +- **short_cycle_protection_lockout_max_time** (Optional, Number) + Short cycle lockout max time in seconds (1..86400). + `short_cycle_protection_lockout_min_time` need to be set. +- **short_cycle_protection_lockout_min_time** (Optional, Number) + Short cycle lockout min time in seconds (1..86400). + `short_cycle_protection_lockout_max_time` need to be set. +- **source_ip_change** (Optional, Boolean) + Use address of egress interface as source ip. + `version` need to be `v4`. +- **vendor_specific_information_host_name** (Optional, Boolean) + DHCPv6 option 17 vendor-specific processing, add router host name. + `version` need to be `v6`. +- **vendor_specific_information_location** (Optional, Boolean) + DHCPv6 option 17 vendor-specific processing, + add location information expressed as interface name format. + `version` need to be `v6`. + +--- + +### overrides_v4 arguments + +- **allow_no_end_option** (Optional, Boolean) + Allow packets without end-of-option. +- **allow_snooped_clients** (Optional, Boolean) + Allow client creation from snooped PDUs. +- **always_write_giaddr** (Optional, Boolean) + Overwrite existing 'giaddr' field, when present. +- **always_write_option_82** (Optional, Boolean) + Overwrite existing value of option 82, when present. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **bootp_support** (Optional, Boolean) + Allows relay of bootp req and reply. +- **client_discover_match** (Optional, String) + Use secondary match criteria for DISCOVER PDU. + Need to be `incoming-interface` or `option60-and-option82`. +- **delay_authentication** (Optional, Boolean) + Delay subscriber authentication in DHCP protocol processing until request packet. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on rengotiation. +- **disable_relay** (Optional, Boolean) + Disable DHCP relay processing. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **layer2_unicast_replies** (Optional, Boolean) + Do not broadcast client responses. +- **no_allow_snooped_clients** (Optional, Boolean) + Don't allow client creation from snooped PDUs. +- **no_bind_on_request** (Optional, Boolean) + Do not bind if stray DHCP request is received. +- **no_unicast_replies** (Optional, Boolean) + Overwrite unicast bit in incoming packet, when present. +- **proxy_mode** Optional, Boolean) + Put the relay in proxy mode. +- **relay_source** (Optional, String) + Interface for relay source. +- **replace_ip_source_with_giaddr** (Optional, Boolean) + Replace IP source address in request and release packets. +- **send_release_on_delete** (Optional, Boolean) + Always send RELEASE to the server when a binding is deleted. +- **trust_option_82** (Optional, Boolean) + Trust options-82 option. +- **user_defined_option_82** (Optional, String) + Set user defined description for option-82. + +### overrides_v6 arguments + +- **allow_snooped_clients** (Optional, Boolean) + Allow client creation from snooped PDUs. +- **always_process_option_request_option** (Optional, Boolean) + Always process option even after address allocation failure. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **asymmetric_prefix_lease_time** (Optional, Number) + Use a reduced prefix lease time for the client. In seconds (600..86400 seconds). +- **client_negotiation_match_incoming_interface** (Optional, Boolean) + Use incoming interface match criteria for SOLICIT PDU. +- **delay_authentication** (Optional, Boolean) + Delay subscriber authentication in DHCP protocol processing until request packet. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on rengotiation. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **no_allow_snooped_clients** (Optional, Boolean) + Don't allow client creation from snooped PDUs. +- **no_bind_on_request** (Optional, Boolean) + Do not bind if stray DHCPv6 RENEW, REBIND is received. +- **relay_source** (Optional, String) + Interface for relay source. +- **send_release_on_delete** (Optional, Boolean) + Always send RELEASE to the server when a binding is deleted. + +### relay_agent_interface_id or relay_agent_remote_id arguments + +- **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. +- **keep_incoming_id** (Optional, Boolean) + Keep incoming interface identifier. +- **keep_incoming_id_strict** (Optional, Boolean) + Drop packet if interface identifier not present. + Only on `relay_agent_interface_id` block. +- **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. +- **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. +- **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. +- **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. +- **use_option_82** (Optional, Boolean) + Use option-82 circuit-id for interface-id or remote-id. +- **use_option_82_strict** (Optional, Boolean) + Drop packet if option-82 circuit-id not present. +- **use_vlan_id** (Optional, Boolean) + Use VLAN id instead of name. + +### relay_option arguments + +- **option_15** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 15 processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_15_default_action** (Optional, Block) + Generic option 15 default action. + `version` need to be `v6`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_16** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 16 processing. + `version` need to be `v6`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_16_default_action** (Optional, Block) + Generic option 16 default action. + `version` need to be `v6`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only` or `relay-server-group`. + - **group** (Optional, String) + Group for action `relay-server-group`. +- **option_60** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 60 processing. + `version` need to be `v4`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_60_default_action** (Optional, Block) + Generic option 60 default action. + `version` need to be `v4`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_77** (Optional, Block Set) + For each combination of `compare`, `value_type` and `value` arguments, add option 77 processing. + `version` need to be `v4`. + - **compare** (Required, String) + How to compare. + Need to be `equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. + - **action** (Required, String) + Action on match. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_77_default_action** (Optional, Block) + Generic option 77 default action. + `version` need to be `v4`. + - **action** (Required, String) + Action. + Need to be `drop`, `forward-only`, `local-server-group` or `relay-server-group`. + - **group** (Optional, String) + Group for action `local-server-group` or `relay-server-group`. +- **option_order** (Optional, List of String) + Options precedence order. + Need to be `60` or `77` with `version` = `v4`. + Need to be `15` or `16` with `version` = `v6`. + +### relay_option_82 arguments + +- **circuit_id** (Optional, Block) + Add circuit identifier. + - **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. + - **keep_incoming_circuit_id** (Optional, Boolean) + Keep incoming circuit identifier. + - **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. + - **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. + - **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. + - **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. + - **use_vlan_id** (Optional, Boolean) + Use VLAN id instead of name. + - **user_defined** (Optional, Boolean) + Include user defined string. + - **vlan_id_only** (Optional, Boolean) + Use only VLAN id. +- **exclude_relay_agent_identifier** (Optional, Boolean) + Exclude relay agent identifier from packets to server. +- **link_selection** (Optional, Boolean) + Add link-selection sub-option on packets to server. +- **remote_id** (Optional, Block) + Add remote identifier. + - **hostname_only** (Optional, Boolean) + Include hostname only. + - **include_irb_and_l2** (Optional, Boolean) + Include IRB and L2 interface name. + - **keep_incoming_remote_id** (Optional, Boolean) + Keep incoming remote identifier. + - **no_vlan_interface_name** (Optional, Boolean) + Not include vlan or interface name. + - **prefix_host_name** (Optional, Boolean) + Add router host name to circuit / interface-id or remote-id. + - **prefix_routing_instance_name** (Optional, Boolean) + Add routing instance name to circuit / interface-id or remote-id. + - **use_interface_description** (Optional, String) + Use interface description instead of circuit identifier. + Need to be `device` or `logical`. + - **use_string** (Optional, String) + Use raw string instead of the default remote id. + - **use_vlan_id** Optional, String) + Use VLAN id instead of name. +- **server_id_override** (Optional, Boolean) + Add link-selection and server-id sub-options on packets to server. +- **vendor_specific_host_name** (Optional, Boolean) + Add vendor-specific information, add router host name. +- **vendor_specific_location** (Optional, Boolean) + Add vendor-specific information, add location information expressed as interface name format. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format `_-_`. + +## Import + +Junos forwarding-options dhcp-relay can be imported using an id made up of +`_-_`, e.g. + +```shell +$ terraform import junos_forwardingoptions_dhcprelay.demo default_-_v4 +``` diff --git a/junos/provider.go b/junos/provider.go index 24d402b4..5b08a832 100644 --- a/junos/provider.go +++ b/junos/provider.go @@ -126,6 +126,7 @@ func Provider() *schema.Provider { "junos_evpn": resourceEvpn(), "junos_firewall_filter": resourceFirewallFilter(), "junos_firewall_policer": resourceFirewallPolicer(), + "junos_forwardingoptions_dhcprelay": resourceForwardingOptionsDhcpRelay(), "junos_forwardingoptions_dhcprelay_group": resourceForwardingOptionsDhcpRelayGroup(), "junos_forwardingoptions_dhcprelay_servergroup": resourceForwardingOptionsDhcpRelayServerGroup(), "junos_forwardingoptions_sampling_instance": resourceForwardingOptionsSamplingInstance(), diff --git a/junos/resource_forwardingoptions_dhcprelay.go b/junos/resource_forwardingoptions_dhcprelay.go new file mode 100644 index 00000000..333d821d --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay.go @@ -0,0 +1,1970 @@ +package junos + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + bchk "github.com/jeremmfr/go-utils/basiccheck" +) + +type fwdOptsDhcpRelayOptions struct { + activeServerGroupAllowServerChange bool + arpInspection bool + duplicateClientsIncomingInterface bool + dynamicProfileAggregateClients bool + excludeRelayAgentIdentifier bool + forwardOnly bool + forwardOnlyReplies bool + noSnoop bool + persistentStorageAutomatic bool + relayAgentOption79 bool + remoteIDMismatchDisconnect bool + routeSuppressionAccess bool + routeSuppressionAccessInternal bool + routeSuppressionDestination bool + sourceIPChange bool + vendorSpecificInformationHostName bool + vendorSpecificInformationLocation bool + clientResponseTTL int + maximumHopCount int + minimumWaitTime int + serverResponseTime int + shortCycleProtectionLockoutMaxTime int + shortCycleProtectionLockoutMinTime int + accessProfile string + activeServerGroup string + authenticationPassword string + duplicateClientsInSubnet string + dynamicProfile string + dynamicProfileAggregateClientsAction string + dynamicProfileUsePrimary string + forwardOnlyRoutingInstance string + forwardSnoopedClients string + livenessDetectionFailureAction string + routingInstance string + serverMatchDefaultAction string + serviceProfile string + version string + activeLeasequery []map[string]interface{} + authenticationUsernameInclude []map[string]interface{} + bulkLeasequery []map[string]interface{} + leaseTimeValidation []map[string]interface{} + leasequery []map[string]interface{} + livenessDetectionMethodBfd []map[string]interface{} + livenessDetectionMethodLayer2 []map[string]interface{} + overridesV4 []map[string]interface{} + overridesV6 []map[string]interface{} + relayAgentInterfaceID []map[string]interface{} + relayAgentRemoteID []map[string]interface{} + relayOption []map[string]interface{} + relayOption82 []map[string]interface{} + serverMatchAddress []map[string]interface{} + serverMatchDuid []map[string]interface{} +} + +func resourceForwardingOptionsDhcpRelay() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceForwardingOptionsDhcpRelayCreate, + ReadWithoutTimeout: resourceForwardingOptionsDhcpRelayRead, + UpdateWithoutTimeout: resourceForwardingOptionsDhcpRelayUpdate, + DeleteWithoutTimeout: resourceForwardingOptionsDhcpRelayDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceForwardingOptionsDhcpRelayImport, + }, + Schema: map[string]*schema.Schema{ + "routing_instance": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: defaultW, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "v4", + ValidateFunc: validation.StringInSlice([]string{"v4", "v6"}, false), + }, + "access_profile": { + Type: schema.TypeString, + Optional: true, + }, + "active_leasequery": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "idle_timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(10, 3600), + }, + "peer_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPAddress, + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(10, 3600), + }, + "topology_discover": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "active_server_group": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "active_server_group_allow_server_change": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "arp_inspection": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "authentication_password": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_username_include": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAuthUsernameInclude(), + }, + }, + "bulk_leasequery": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attempts": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 720), + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "trigger_automatic": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "client_response_ttl": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "duplicate_clients_in_subnet": { // only dhcpv4 + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"incoming-interface", "option-82"}, false), + }, + "duplicate_clients_incoming_interface": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "dynamic_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_aggregate_clients": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + }, + "dynamic_profile_aggregate_clients_action": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile_aggregate_clients"}, + ValidateFunc: validation.StringInSlice([]string{"merge", "replace"}, false), + }, + "dynamic_profile_use_primary": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + }, + "exclude_relay_agent_identifier": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "forward_only": { + Type: schema.TypeBool, + Optional: true, + }, + "forward_only_replies": { + Type: schema.TypeBool, + Optional: true, + }, + "forward_only_routing_instance": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"forward_only"}, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "forward_snooped_clients": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "all-interfaces", + "configured-interfaces", + "non-configured-interfaces", + }, false), + }, + "lease_time_validation": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lease_time_threshold": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(60, 2147483647), + }, + "violation_action_drop": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "leasequery": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attempts": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + }, + }, + }, + "liveness_detection_failure_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "clear-binding", + "clear-binding-if-interface-up", + "log-only", + }, false), + }, + "liveness_detection_method_bfd": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_layer2"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "detection_time_threshold": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_bfd.0.detection_time_threshold", + "liveness_detection_method_bfd.0.holddown_interval", + "liveness_detection_method_bfd.0.minimum_interval", + "liveness_detection_method_bfd.0.minimum_receive_interval", + "liveness_detection_method_bfd.0.multiplier", + "liveness_detection_method_bfd.0.no_adaptation", + "liveness_detection_method_bfd.0.session_mode", + "liveness_detection_method_bfd.0.transmit_interval_minimum", + "liveness_detection_method_bfd.0.transmit_interval_threshold", + "liveness_detection_method_bfd.0.version", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "holddown_interval": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 255000), + }, + "minimum_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "minimum_receive_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "multiplier": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "no_adaptation": { + Type: schema.TypeBool, + Optional: true, + }, + "session_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"automatic", "multihop", "single-hop"}, false), + }, + "transmit_interval_minimum": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "transmit_interval_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"0", "1", "automatic"}, false), + }, + }, + }, + }, + "liveness_detection_method_layer2": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_bfd"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_consecutive_retries": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_layer2.0.max_consecutive_retries", + "liveness_detection_method_layer2.0.transmit_interval", + }, + ValidateFunc: validation.IntBetween(3, 6), + }, + "transmit_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(300, 1800), + }, + }, + }, + }, + "maximum_hop_count": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 16), + }, + "minimum_wait_time": { // only dhcpv4 + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 30000), + }, + "no_snoop": { + Type: schema.TypeBool, + Optional: true, + }, + "overrides_v4": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV4(), + }, + }, + "overrides_v6": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOverridesV6(), + }, + }, + "persistent_storage_automatic": { + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_interface_id": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAgentID(true), + }, + }, + "relay_agent_option_79": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_remote_id": { // only dhcpv6 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayAgentID(false), + }, + }, + "relay_option": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOption(), + }, + }, + "relay_option_82": { // only dhcpv4 + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaForwardingOptionsDhcpRelayOption82(), + }, + }, + "remote_id_mismatch_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access_internal": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_destination": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "server_match_address": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsCIDR, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + }, + }, + }, + "server_match_default_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + "server_match_duid": { // only dhcpv6 + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"create-relay-entry", "forward-only"}, false), + }, + }, + }, + }, + "server_response_time": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "service_profile": { // only dhcpv6 + Type: schema.TypeString, + Optional: true, + }, + "short_cycle_protection_lockout_max_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_min_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "short_cycle_protection_lockout_min_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_max_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "source_ip_change": { // only dhcpv4 + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_information_host_name": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + "vendor_specific_information_location": { // only dhcpv6 + Type: schema.TypeBool, + Optional: true, + }, + }, + } +} + +func resourceForwardingOptionsDhcpRelayCreate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeCreateSetFile != "" { + if err := setForwardingOptionsDhcpRelay(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(routingInstanceArg + idSeparator + versionArg) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if routingInstanceArg != defaultW { + instanceExists, err := checkRoutingInstanceExists(routingInstanceArg, clt, junSess) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !instanceExists { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, + diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", routingInstanceArg))...) + } + } + if err := setForwardingOptionsDhcpRelay(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("create resource junos_forwardingoptions_dhcprelay", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.SetId(routingInstanceArg + idSeparator + versionArg) + + return append(diagWarns, resourceForwardingOptionsDhcpRelayReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayRead(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + + return resourceForwardingOptionsDhcpRelayReadWJunSess(d, clt, junSess) +} + +func resourceForwardingOptionsDhcpRelayReadWJunSess( + d *schema.ResourceData, clt *Client, junSess *junosSession, +) diag.Diagnostics { + mutex.Lock() + if d.Get("routing_instance").(string) != defaultW { + instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), clt, junSess) + if err != nil { + mutex.Unlock() + + return diag.FromErr(err) + } + if !instanceExists { + mutex.Unlock() + d.SetId("") + + return nil + } + } + fwdOptsDhcpRelayOptions, err := readForwardingOptionsDhcpRelay( + d.Get("routing_instance").(string), + d.Get("version").(string), + clt, junSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + fillForwardingOptionsDhcpRelayData(d, fwdOptsDhcpRelayOptions) + + return nil +} + +func resourceForwardingOptionsDhcpRelayUpdate(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + d.Partial(true) + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeUpdateAlso { + if err := delForwardingOptionsDhcpRelay(routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + if err := setForwardingOptionsDhcpRelay(d, clt, nil); err != nil { + return diag.FromErr(err) + } + d.Partial(false) + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelay(routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if err := setForwardingOptionsDhcpRelay(d, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + warns, err := clt.commitConf("update resource junos_forwardingoptions_dhcprelay", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.Partial(false) + + return append(diagWarns, resourceForwardingOptionsDhcpRelayReadWJunSess(d, clt, junSess)...) +} + +func resourceForwardingOptionsDhcpRelayDelete(ctx context.Context, d *schema.ResourceData, m interface{}, +) diag.Diagnostics { + routingInstanceArg := d.Get("routing_instance").(string) + versionArg := d.Get("version").(string) + clt := m.(*Client) + if clt.fakeDeleteAlso { + if err := delForwardingOptionsDhcpRelay(routingInstanceArg, versionArg, clt, nil); err != nil { + return diag.FromErr(err) + } + + return nil + } + junSess, err := clt.startNewSession(ctx) + if err != nil { + return diag.FromErr(err) + } + defer clt.closeSession(junSess) + if err := clt.configLock(ctx, junSess); err != nil { + return diag.FromErr(err) + } + var diagWarns diag.Diagnostics + if err := delForwardingOptionsDhcpRelay(routingInstanceArg, versionArg, clt, junSess); err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := clt.commitConf("delete resource junos_forwardingoptions_dhcprelay", junSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, clt.configClear(junSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + return diagWarns +} + +func resourceForwardingOptionsDhcpRelayImport(ctx context.Context, d *schema.ResourceData, m interface{}, +) ([]*schema.ResourceData, error) { + clt := m.(*Client) + junSess, err := clt.startNewSession(ctx) + if err != nil { + return nil, err + } + defer clt.closeSession(junSess) + result := make([]*schema.ResourceData, 1) + idSplit := strings.Split(d.Id(), idSeparator) + if len(idSplit) < 2 { + return nil, fmt.Errorf("missing element(s) in id with separator %v", idSeparator) + } + if idSplit[1] != "v4" && idSplit[1] != "v6" { + return nil, fmt.Errorf("bad version '%s' in id, need to be 'v4' or 'v6' (id must be "+ + ""+idSeparator+")", idSplit[2]) + } + if idSplit[0] != defaultW { + routingInstanceExists, err := checkRoutingInstanceExists(idSplit[0], clt, junSess) + if err != nil { + return nil, err + } + if !routingInstanceExists { + return nil, fmt.Errorf("don't find routing_instance with id '%v' (id must be "+ + ""+idSeparator+")", d.Id()) + } + } + fwdOptsDhcpRelayOptions, err := readForwardingOptionsDhcpRelay(idSplit[0], idSplit[1], clt, junSess) + if err != nil { + return nil, err + } + fillForwardingOptionsDhcpRelayData(d, fwdOptsDhcpRelayOptions) + + result[0] = d + + return result, nil +} + +func setForwardingOptionsDhcpRelay( //nolint: gocognit, gocyclo + d *schema.ResourceData, clt *Client, junSess *junosSession, +) error { + configSet := make([]string, 0) + + setPrefix := setLS + if d.Get("routing_instance").(string) != defaultW { + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " + } + if d.Get("version").(string) == "v6" { + setPrefix += "forwarding-options dhcp-relay dhcpv6 " + } else { + setPrefix += "forwarding-options dhcp-relay " + } + + if v := d.Get("access_profile").(string); v != "" { + configSet = append(configSet, setPrefix+"access-profile \""+v+"\"") + } + for _, block := range d.Get("active_leasequery").([]interface{}) { + configSet = append(configSet, setPrefix+"active-leasequery") + if block != nil { + activeLeasequery := block.(map[string]interface{}) + if v := activeLeasequery["idle_timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"active-leasequery idle-timeout "+strconv.Itoa(v)) + } + if v := activeLeasequery["peer_address"].(string); v != "" { + configSet = append(configSet, setPrefix+"active-leasequery peer-address "+v) + } + if v := activeLeasequery["timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"active-leasequery timeout "+strconv.Itoa(v)) + } + if activeLeasequery["topology_discover"].(bool) { + configSet = append(configSet, setPrefix+"active-leasequery topology-discover") + } + } + } + if v := d.Get("active_server_group").(string); v != "" { + configSet = append(configSet, setPrefix+"active-server-group "+v) + } + if d.Get("active_server_group_allow_server_change").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("active_server_group_allow_server_change not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"active-server-group allow-server-change") + } + if d.Get("arp_inspection").(bool) { + configSet = append(configSet, setPrefix+"arp-inspection") + } + if v := d.Get("authentication_password").(string); v != "" { + configSet = append(configSet, setPrefix+"authentication password \""+v+"\"") + } + for _, vBlock := range d.Get("authentication_username_include").([]interface{}) { + authenticationUsernameInclude := vBlock.(map[string]interface{}) + configSetAuthUsernameInclude, err := setForwardingOptionsDhcpRelayAuthUsernameInclude( + authenticationUsernameInclude, + setPrefix, + d.Get("version").(string), + ) + if err != nil { + return err + } + configSet = append(configSet, configSetAuthUsernameInclude...) + } + for _, block := range d.Get("bulk_leasequery").([]interface{}) { + configSet = append(configSet, setPrefix+"bulk-leasequery") + if block != nil { + bulkLeasequery := block.(map[string]interface{}) + if v := bulkLeasequery["attempts"].(int); v != 0 { + if d.Get("version") == "v6" && v > 10 { + return fmt.Errorf("expected bulk_leasequery.0.attempts to be in the range (1 - 10) with version = v6, got %d", v) + } + configSet = append(configSet, setPrefix+"bulk-leasequery attempts "+strconv.Itoa(v)) + } + if v := bulkLeasequery["timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"bulk-leasequery timeout "+strconv.Itoa(v)) + } + if bulkLeasequery["trigger_automatic"].(bool) { + if d.Get("version").(string) != "v6" { + return fmt.Errorf("bulk_leasequery.0.trigger_automatic only compatible when version = v6") + } + configSet = append(configSet, setPrefix+"bulk-leasequery trigger automatic") + } + } + } + if v := d.Get("client_response_ttl").(int); v != 0 { + if d.Get("version").(string) != "v4" { + return fmt.Errorf("client_response_ttl only compatible when version = v4") + } + configSet = append(configSet, setPrefix+"client-response-ttl "+strconv.Itoa(v)) + } + if v := d.Get("duplicate_clients_in_subnet").(string); v != "" { + if d.Get("version").(string) != "v4" { + return fmt.Errorf("duplicate_clients_in_subnet only compatible when version = v4") + } + configSet = append(configSet, setPrefix+"duplicate-clients-in-subnet "+v) + } + if d.Get("duplicate_clients_incoming_interface").(bool) { + if d.Get("version").(string) != "v6" { + return fmt.Errorf("duplicate_clients_incoming_interface only compatible when version = v6") + } + configSet = append(configSet, setPrefix+"duplicate-clients incoming-interface") + } + if dynProfile := d.Get("dynamic_profile").(string); dynProfile != "" { + configSet = append(configSet, setPrefix+"dynamic-profile \""+dynProfile+"\"") + if d.Get("dynamic_profile_aggregate_clients").(bool) { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients") + if v := d.Get("dynamic_profile_aggregate_clients_action").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients "+v) + } + } else if d.Get("dynamic_profile_aggregate_clients_action").(string) != "" { + return fmt.Errorf("dynamic_profile_aggregate_clients need to be true with " + + "dynamic_profile_aggregate_clients_action") + } + if v := d.Get("dynamic_profile_use_primary").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile use-primary \""+v+"\"") + } + } else if d.Get("dynamic_profile_aggregate_clients").(bool) || + d.Get("dynamic_profile_aggregate_clients_action").(string) != "" || + d.Get("dynamic_profile_use_primary").(string) != "" { + return fmt.Errorf("dynamic_profile need to be set with " + + "dynamic_profile_use_primary, dynamic_profile_aggregate_clients " + + "and dynamic_profile_aggregate_clients_action") + } + if d.Get("exclude_relay_agent_identifier").(bool) { + if d.Get("version").(string) != "v6" { + return fmt.Errorf("exclude_relay_agent_identifier only compatible when version = v6") + } + configSet = append(configSet, setPrefix+"exclude-relay-agent-identifier") + } + if d.Get("forward_only").(bool) { + configSet = append(configSet, setPrefix+"forward-only") + if v := d.Get("forward_only_routing_instance").(string); v != "" { + configSet = append(configSet, setPrefix+"forward-only routing-instance "+v) + } + } else if d.Get("forward_only_routing_instance").(string) != "" { + return fmt.Errorf("forward_only need to be true with forward_only_routing_instance") + } + if d.Get("forward_only_replies").(bool) { + configSet = append(configSet, setPrefix+"forward-only-replies") + } + if v := d.Get("forward_snooped_clients").(string); v != "" { + configSet = append(configSet, setPrefix+"forward-snooped-clients "+v) + } + for _, vBlock := range d.Get("lease_time_validation").([]interface{}) { + configSet = append(configSet, setPrefix+"lease-time-validation") + if vBlock != nil { + leaseTimeValidation := vBlock.(map[string]interface{}) + if v := leaseTimeValidation["lease_time_threshold"].(int); v != 0 { + configSet = append(configSet, setPrefix+"lease-time-validation lease-time-threshold "+strconv.Itoa(v)) + } + if leaseTimeValidation["violation_action_drop"].(bool) { + configSet = append(configSet, setPrefix+"lease-time-validation violation-action drop") + } + } + } + for _, block := range d.Get("leasequery").([]interface{}) { + configSet = append(configSet, setPrefix+"leasequery") + if block != nil { + leasequery := block.(map[string]interface{}) + if v := leasequery["attempts"].(int); v != 0 { + configSet = append(configSet, setPrefix+"leasequery attempts "+strconv.Itoa(v)) + } + if v := leasequery["timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"leasequery timeout "+strconv.Itoa(v)) + } + } + } + if v := d.Get("liveness_detection_failure_action").(string); v != "" { + configSet = append(configSet, setPrefix+"liveness-detection failure-action "+v) + } + for _, ldmBfd := range d.Get("liveness_detection_method_bfd").([]interface{}) { + liveDetectMethBfd := ldmBfd.(map[string]interface{}) + setPrefixLDMBfd := setPrefix + "liveness-detection method bfd " + if v := liveDetectMethBfd["detection_time_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"detection-time threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["holddown_interval"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"holddown-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_receive_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-receive-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["multiplier"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"multiplier "+strconv.Itoa(v)) + } + if liveDetectMethBfd["no_adaptation"].(bool) { + configSet = append(configSet, setPrefixLDMBfd+"no-adaptation") + } + if v := liveDetectMethBfd["session_mode"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"session-mode "+v) + } + if v := liveDetectMethBfd["transmit_interval_minimum"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["transmit_interval_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["version"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"version "+v) + } + + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefixLDMBfd) { + return fmt.Errorf("liveness_detection_method_bfd block is empty") + } + } + for _, ldmLayer2 := range d.Get("liveness_detection_method_layer2").([]interface{}) { + if ldmLayer2 == nil { + return fmt.Errorf("liveness_detection_method_layer2 block is empty") + } + liveDetectMethLayer2 := ldmLayer2.(map[string]interface{}) + setPrefixLDMLayer2 := setPrefix + "liveness-detection method layer2-liveness-detection " + if v := liveDetectMethLayer2["max_consecutive_retries"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"max-consecutive-retries "+strconv.Itoa(v)) + } + if v := liveDetectMethLayer2["transmit_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"transmit-interval "+strconv.Itoa(v)) + } + } + if v := d.Get("maximum_hop_count").(int); v != 0 { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("maximum_hop_count not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"maximum-hop-count "+strconv.Itoa(v)) + } + if v := d.Get("minimum_wait_time").(int); v != -1 { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("minimum_wait_time not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"minimum-wait-time "+strconv.Itoa(v)) + } + if d.Get("no_snoop").(bool) { + configSet = append(configSet, setPrefix+"no-snoop") + } + for _, v := range d.Get("overrides_v4").([]interface{}) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("overrides_v4 not compatible if version = v6") + } + if v == nil { + return fmt.Errorf("overrides_v4 block is empty") + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV4( + v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range d.Get("overrides_v6").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("overrides_v6 not compatible if version = v4") + } + if v == nil { + return fmt.Errorf("overrides_v6 block is empty") + } + configSetOverrides, err := setForwardingOptionsDhcpRelayOverridesV6( + v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + if d.Get("persistent_storage_automatic").(bool) { + configSet = append(configSet, setPrefix+"persistent-storage automatic") + } + for _, v := range d.Get("relay_agent_interface_id").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-interface-id") + if v != nil { + configSetRelayAgentID, err := setForwardingOptionsDhcpRelayAgentID( + v.(map[string]interface{}), setPrefix+"relay-agent-interface-id ", "relay_agent_interface_id") + if err != nil { + return err + } + configSet = append(configSet, configSetRelayAgentID...) + } + } + if d.Get("relay_agent_option_79").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-option-79") + } + for _, v := range d.Get("relay_agent_remote_id").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"relay-agent-remote-id") + if v != nil { + configSetRelayAgentID, err := setForwardingOptionsDhcpRelayAgentID( + v.(map[string]interface{}), setPrefix+"relay-agent-remote-id ", "relay_agent_remote_id") + if err != nil { + return err + } + configSet = append(configSet, configSetRelayAgentID...) + } + } + for _, v := range d.Get("relay_option").([]interface{}) { + configSet = append(configSet, setPrefix+"relay-option") + if v != nil { + configSetRelayOption, err := setForwardingOptionsDhcpRelayOption( + v.(map[string]interface{}), setPrefix, d.Get("version").(string)) + if err != nil { + return err + } + configSet = append(configSet, configSetRelayOption...) + } + } + for _, v := range d.Get("relay_option_82").([]interface{}) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("relay_option_82 not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"relay-option-82") + if v != nil { + configSet = append(configSet, setForwardingOptionsDhcpRelayOption82(v.(map[string]interface{}), setPrefix)...) + } + } + if d.Get("remote_id_mismatch_disconnect").(bool) { + configSet = append(configSet, setPrefix+"remote-id-mismatch disconnect") + } + if d.Get("route_suppression_access").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("route_suppression_access not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"route-suppression access") + } + if d.Get("route_suppression_access_internal").(bool) { + configSet = append(configSet, setPrefix+"route-suppression access-internal") + } + if d.Get("route_suppression_destination").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("route_suppression_destination not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"route-suppression destination") + } + serverMatchAddressList := make([]string, 0) + for _, v := range d.Get("server_match_address").(*schema.Set).List() { + serverMatchAddress := v.(map[string]interface{}) + if bchk.StringInSlice(serverMatchAddress["address"].(string), serverMatchAddressList) { + return fmt.Errorf("multiple blocks server_match_address with the same address %s", + serverMatchAddress["address"].(string)) + } + serverMatchAddressList = append(serverMatchAddressList, serverMatchAddress["address"].(string)) + configSet = append(configSet, setPrefix+"server-match address "+ + serverMatchAddress["address"].(string)+" "+serverMatchAddress["action"].(string)) + } + if v := d.Get("server_match_default_action").(string); v != "" { + configSet = append(configSet, setPrefix+"server-match default-action "+v) + } + serverMatchDuidList := make([]string, 0) + for _, v := range d.Get("server_match_duid").(*schema.Set).List() { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("server_match_duid not compatible if version = v4") + } + serverMatchDuid := v.(map[string]interface{}) + serverMatchDuidCompare := serverMatchDuid["compare"].(string) + serverMatchDuidValueType := serverMatchDuid["value_type"].(string) + serverMatchDuidValue := serverMatchDuid["value"].(string) + if bchk.StringInSlice( + serverMatchDuidCompare+idSeparator+serverMatchDuidValueType+idSeparator+serverMatchDuidValue, + serverMatchDuidList, + ) { + return fmt.Errorf("multiple blocks server_match_duid with the same compare %s, value_type %s, value %s", + serverMatchDuidCompare, serverMatchDuidValueType, serverMatchDuidValue) + } + serverMatchDuidList = append( + serverMatchDuidList, + serverMatchDuidCompare+idSeparator+serverMatchDuidValueType+idSeparator+serverMatchDuidValue, + ) + configSet = append(configSet, setPrefix+"server-match duid "+ + serverMatchDuidCompare+" "+ + serverMatchDuidValueType+" "+ + "\""+serverMatchDuidValue+"\" "+ + serverMatchDuid["action"].(string)) + } + if v := d.Get("server_response_time").(int); v != -1 { + configSet = append(configSet, setPrefix+"server-response-time "+strconv.Itoa(v)) + } + if v := d.Get("service_profile").(string); v != "" { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("service_profile not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"service-profile \""+v+"\"") + } + if v := d.Get("short_cycle_protection_lockout_max_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-max-time "+strconv.Itoa(v)) + } + if v := d.Get("short_cycle_protection_lockout_min_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-min-time "+strconv.Itoa(v)) + } + if d.Get("source_ip_change").(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("source_ip_change not compatible if version = v6") + } + configSet = append(configSet, setPrefix+"source-ip-change") + } + if d.Get("vendor_specific_information_host_name").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("vendor_specific_information_host_name not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"vendor-specific-information host-name") + } + if d.Get("vendor_specific_information_location").(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("vendor_specific_information_location not compatible if version = v4") + } + configSet = append(configSet, setPrefix+"vendor-specific-information location") + } + + return clt.configSet(configSet, junSess) +} + +func readForwardingOptionsDhcpRelay( //nolint: gocognit, gocyclo + instance, version string, clt *Client, junSess *junosSession, +) (fwdOptsDhcpRelayOptions, error) { + var confRead fwdOptsDhcpRelayOptions + confRead.minimumWaitTime = -1 // default = -1 + confRead.serverResponseTime = -1 // default = -1 + + showCmd := cmdShowConfig + if instance != defaultW { + showCmd += routingInstancesWS + instance + " " + } + showCmd += "forwarding-options dhcp-relay " + if version == "v6" { + showCmd += "dhcpv6 " + } + + showConfig, err := clt.command(showCmd+pipeDisplaySetRelative, junSess) + if err != nil { + return confRead, err + } + confRead.routingInstance = instance + confRead.version = version + if showConfig != emptyW { + for _, item := range strings.Split(showConfig, "\n") { + itemTrim := strings.TrimPrefix(item, setLS) + if strings.Contains(item, xmlStartTagConfigOut) { + continue + } + if strings.Contains(item, xmlEndTagConfigOut) { + break + } + switch { + case strings.HasPrefix(itemTrim, "access-profile "): + confRead.accessProfile = strings.Trim(strings.TrimPrefix(itemTrim, "access-profile "), "\"") + case strings.HasPrefix(itemTrim, "active-leasequery"): + if len(confRead.activeLeasequery) == 0 { + confRead.activeLeasequery = append(confRead.activeLeasequery, map[string]interface{}{ + "idle_timeout": 0, + "peer_address": "", + "timeout": 0, + "topology_discover": false, + }) + } + switch { + case strings.HasPrefix(itemTrim, "active-leasequery idle-timeout "): + var err error + confRead.activeLeasequery[0]["idle_timeout"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "active-leasequery idle-timeout ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "active-leasequery peer-address "): + confRead.activeLeasequery[0]["peer_address"] = strings.TrimPrefix( + itemTrim, "active-leasequery peer-address ") + case strings.HasPrefix(itemTrim, "active-leasequery timeout "): + var err error + confRead.activeLeasequery[0]["timeout"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "active-leasequery timeout ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "active-leasequery topology-discover": + confRead.activeLeasequery[0]["topology_discover"] = true + } + case itemTrim == "active-server-group allow-server-change": + confRead.activeServerGroupAllowServerChange = true + case strings.HasPrefix(itemTrim, "active-server-group "): + confRead.activeServerGroup = strings.TrimPrefix(itemTrim, "active-server-group ") + case itemTrim == "arp-inspection": + confRead.arpInspection = true + case strings.HasPrefix(itemTrim, "authentication password "): + confRead.authenticationPassword = strings.Trim(strings.TrimPrefix(itemTrim, "authentication password "), "\"") + case strings.HasPrefix(itemTrim, "authentication username-include "): + if len(confRead.authenticationUsernameInclude) == 0 { + confRead.authenticationUsernameInclude = append(confRead.authenticationUsernameInclude, + genForwardingOptionsDhcpRelayAuthUsernameInclude()) + } + readForwardingOptionsDhcpRelayAuthUsernameInclude( + strings.TrimPrefix(itemTrim, "authentication username-include "), + confRead.authenticationUsernameInclude[0], + ) + case strings.HasPrefix(itemTrim, "bulk-leasequery"): + if len(confRead.bulkLeasequery) == 0 { + confRead.bulkLeasequery = append(confRead.bulkLeasequery, map[string]interface{}{ + "attempts": 0, + "timeout": 0, + "trigger_automatic": false, + }) + } + switch { + case strings.HasPrefix(itemTrim, "bulk-leasequery attempts "): + var err error + confRead.bulkLeasequery[0]["attempts"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "bulk-leasequery attempts ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "bulk-leasequery timeout "): + var err error + confRead.bulkLeasequery[0]["timeout"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "bulk-leasequery timeout ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "bulk-leasequery trigger automatic": + confRead.bulkLeasequery[0]["trigger_automatic"] = true + } + case strings.HasPrefix(itemTrim, "client-response-ttl "): + var err error + confRead.clientResponseTTL, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "client-response-ttl ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "duplicate-clients-in-subnet "): + confRead.duplicateClientsInSubnet = strings.TrimPrefix(itemTrim, "duplicate-clients-in-subnet ") + case itemTrim == "duplicate-clients incoming-interface": + confRead.duplicateClientsIncomingInterface = true + case strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients"): + confRead.dynamicProfileAggregateClients = true + if strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients ") { + confRead.dynamicProfileAggregateClientsAction = strings.TrimPrefix(itemTrim, "dynamic-profile aggregate-clients ") + } + case strings.HasPrefix(itemTrim, "dynamic-profile use-primary "): + confRead.dynamicProfileUsePrimary = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile use-primary "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile "): + confRead.dynamicProfile = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile "), "\"") + case itemTrim == "exclude-relay-agent-identifier": + confRead.excludeRelayAgentIdentifier = true + case itemTrim == "forward-only-replies": + confRead.forwardOnlyReplies = true + case strings.HasPrefix(itemTrim, "forward-only"): + confRead.forwardOnly = true + if strings.HasPrefix(itemTrim, "forward-only routing-instance ") { + confRead.forwardOnlyRoutingInstance = strings.Trim(strings.TrimPrefix( + itemTrim, "forward-only routing-instance "), "\"") + } + case strings.HasPrefix(itemTrim, "forward-snooped-clients "): + confRead.forwardSnoopedClients = strings.TrimPrefix(itemTrim, "forward-snooped-clients ") + case strings.HasPrefix(itemTrim, "lease-time-validation"): + if len(confRead.leaseTimeValidation) == 0 { + confRead.leaseTimeValidation = append(confRead.leaseTimeValidation, map[string]interface{}{ + "lease_time_threshold": 0, + "violation_action_drop": false, + }) + } + switch { + case strings.HasPrefix(itemTrim, "lease-time-validation lease-time-threshold "): + var err error + confRead.leaseTimeValidation[0]["lease_time_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "lease-time-validation lease-time-threshold ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "lease-time-validation violation-action drop": + confRead.leaseTimeValidation[0]["violation_action_drop"] = true + } + case strings.HasPrefix(itemTrim, "leasequery"): + if len(confRead.leasequery) == 0 { + confRead.leasequery = append(confRead.leasequery, map[string]interface{}{ + "attempts": 0, + "timeout": 0, + }) + } + switch { + case strings.HasPrefix(itemTrim, "leasequery attempts "): + var err error + confRead.leasequery[0]["attempts"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "leasequery attempts ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "leasequery timeout "): + var err error + confRead.leasequery[0]["timeout"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "leasequery timeout ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + } + case strings.HasPrefix(itemTrim, "liveness-detection failure-action "): + confRead.livenessDetectionFailureAction = strings.TrimPrefix(itemTrim, "liveness-detection failure-action ") + case strings.HasPrefix(itemTrim, "liveness-detection method bfd "): + if len(confRead.livenessDetectionMethodBfd) == 0 { + confRead.livenessDetectionMethodBfd = append(confRead.livenessDetectionMethodBfd, map[string]interface{}{ + "detection_time_threshold": -1, + "holddown_interval": -1, + "minimum_interval": 0, + "minimum_receive_interval": 0, + "multiplier": 0, + "no_adaptation": false, + "session_mode": "", + "transmit_interval_minimum": 0, + "transmit_interval_threshold": -1, + "version": "", + }) + } + itemTrimLiveDetMethBfd := strings.TrimPrefix(itemTrim, "liveness-detection method bfd ") + var err error + switch { + case strings.HasPrefix(itemTrimLiveDetMethBfd, "detection-time threshold "): + confRead.livenessDetectionMethodBfd[0]["detection_time_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "detection-time threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "holddown-interval "): + confRead.livenessDetectionMethodBfd[0]["holddown_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "holddown-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-receive-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_receive_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "minimum-receive-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "multiplier "): + confRead.livenessDetectionMethodBfd[0]["multiplier"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "multiplier ")) + case itemTrimLiveDetMethBfd == "no-adaptation": + confRead.livenessDetectionMethodBfd[0]["no_adaptation"] = true + case strings.HasPrefix(itemTrimLiveDetMethBfd, "session-mode "): + confRead.livenessDetectionMethodBfd[0]["session_mode"] = strings.TrimPrefix( + itemTrimLiveDetMethBfd, "session-mode ") + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_minimum"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "transmit-interval minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval threshold "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrimLiveDetMethBfd, "transmit-interval threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "version "): + confRead.livenessDetectionMethodBfd[0]["version"] = strings.TrimPrefix(itemTrimLiveDetMethBfd, "version ") + } + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection "): + if len(confRead.livenessDetectionMethodLayer2) == 0 { + confRead.livenessDetectionMethodLayer2 = append(confRead.livenessDetectionMethodLayer2, map[string]interface{}{ + "max_consecutive_retries": 0, + "transmit_interval": 0, + }) + } + var err error + switch { + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection max-consecutive-retries "): + confRead.livenessDetectionMethodLayer2[0]["max_consecutive_retries"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "liveness-detection method layer2-liveness-detection max-consecutive-retries ")) + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection transmit-interval "): + confRead.livenessDetectionMethodLayer2[0]["transmit_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "liveness-detection method layer2-liveness-detection transmit-interval ")) + } + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "maximum-hop-count "): + var err error + confRead.maximumHopCount, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "maximum-hop-count ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "minimum-wait-time "): + var err error + confRead.minimumWaitTime, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "minimum-wait-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "no-snoop": + confRead.noSnoop = true + case strings.HasPrefix(itemTrim, "overrides "): + if version == "v4" { + if len(confRead.overridesV4) == 0 { + confRead.overridesV4 = append(confRead.overridesV4, + genForwardingOptionsDhcpRelayOverridesV4()) + } + if err := readForwardingOptionsDhcpRelayOverridesV4( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV4[0], + ); err != nil { + return confRead, err + } + } else if version == "v6" { + if len(confRead.overridesV6) == 0 { + confRead.overridesV6 = append(confRead.overridesV6, + genForwardingOptionsDhcpRelayOverridesV6()) + } + if err := readForwardingOptionsDhcpRelayOverridesV6( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV6[0], + ); err != nil { + return confRead, err + } + } + case itemTrim == "persistent-storage automatic": + confRead.persistentStorageAutomatic = true + case strings.HasPrefix(itemTrim, "relay-agent-interface-id"): + if len(confRead.relayAgentInterfaceID) == 0 { + confRead.relayAgentInterfaceID = append(confRead.relayAgentInterfaceID, + genForwardingOptionsDhcpRelayAgentID(true)) + } + if strings.HasPrefix(itemTrim, "relay-agent-interface-id ") { + readForwardingOptionsDhcpRelayAgentID( + strings.TrimPrefix(itemTrim, "relay-agent-interface-id "), + confRead.relayAgentInterfaceID[0], + ) + } + case itemTrim == "relay-agent-option-79": + confRead.relayAgentOption79 = true + case strings.HasPrefix(itemTrim, "relay-agent-remote-id"): + if len(confRead.relayAgentRemoteID) == 0 { + confRead.relayAgentRemoteID = append(confRead.relayAgentRemoteID, + genForwardingOptionsDhcpRelayAgentID(false)) + } + if strings.HasPrefix(itemTrim, "relay-agent-remote-id ") { + readForwardingOptionsDhcpRelayAgentID( + strings.TrimPrefix(itemTrim, "relay-agent-remote-id "), + confRead.relayAgentRemoteID[0], + ) + } + case strings.HasPrefix(itemTrim, "relay-option-82"): + if len(confRead.relayOption82) == 0 { + confRead.relayOption82 = append(confRead.relayOption82, + genForwardingOptionsDhcpRelayOption82()) + } + if strings.HasPrefix(itemTrim, "relay-option-82 ") { + readForwardingOptionsDhcpRelayOption82( + strings.TrimPrefix(itemTrim, "relay-option-82 "), + confRead.relayOption82[0], + ) + } + case strings.HasPrefix(itemTrim, "relay-option"): + if len(confRead.relayOption) == 0 { + confRead.relayOption = append(confRead.relayOption, + genForwardingOptionsDhcpRelayOption()) + } + if strings.HasPrefix(itemTrim, "relay-option ") { + if err := readForwardingOptionsDhcpRelayOption( + strings.TrimPrefix(itemTrim, "relay-option "), + confRead.relayOption[0], + ); err != nil { + return confRead, err + } + } + case itemTrim == "remote-id-mismatch disconnect": + confRead.remoteIDMismatchDisconnect = true + case itemTrim == "route-suppression access": + confRead.routeSuppressionAccess = true + case itemTrim == "route-suppression access-internal": + confRead.routeSuppressionAccessInternal = true + case itemTrim == "route-suppression destination": + confRead.routeSuppressionDestination = true + case strings.HasPrefix(itemTrim, "server-match address "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "server-match address "), " ") + if len(itemTrimSplit) < 2 { + return confRead, fmt.Errorf("can't read values for server_match_address in '%s'", itemTrim) + } + confRead.serverMatchAddress = append(confRead.serverMatchAddress, map[string]interface{}{ + "address": itemTrimSplit[0], + "action": itemTrimSplit[1], + }) + case strings.HasPrefix(itemTrim, "server-match default-action "): + confRead.serverMatchDefaultAction = strings.TrimPrefix(itemTrim, "server-match default-action ") + case strings.HasPrefix(itemTrim, "server-match duid "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "server-match duid "), " ") + if len(itemTrimSplit) < 4 { + return confRead, fmt.Errorf("can't read values for server_match_duid in '%s'", itemTrim) + } + if strings.Contains(itemTrimSplit[2], "\"") { + action := itemTrimSplit[len(itemTrimSplit)-1] + value := strings.Trim(strings.Join(itemTrimSplit[2:len(itemTrimSplit)-1], " "), "\"") + itemTrimSplit[2] = value + itemTrimSplit[3] = action + } + confRead.serverMatchDuid = append(confRead.serverMatchDuid, map[string]interface{}{ + "compare": itemTrimSplit[0], + "value_type": itemTrimSplit[1], + "value": itemTrimSplit[2], + "action": itemTrimSplit[3], + }) + case strings.HasPrefix(itemTrim, "server-response-time "): + var err error + confRead.serverResponseTime, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "server-response-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "service-profile "): + confRead.serviceProfile = strings.Trim(strings.TrimPrefix(itemTrim, "service-profile "), "\"") + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-max-time "): + var err error + confRead.shortCycleProtectionLockoutMaxTime, err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-max-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-min-time "): + var err error + confRead.shortCycleProtectionLockoutMinTime, err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "short-cycle-protection lockout-min-time ")) + if err != nil { + return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) + } + case itemTrim == "source-ip-change": + confRead.sourceIPChange = true + case itemTrim == "vendor-specific-information host-name": + confRead.vendorSpecificInformationHostName = true + case itemTrim == "vendor-specific-information location": + confRead.vendorSpecificInformationLocation = true + } + } + } + + return confRead, nil +} + +func delForwardingOptionsDhcpRelay(instance, version string, clt *Client, junSess *junosSession) error { + var delPrefix string + switch { + case instance == defaultW && version == "v6": + delPrefix = deleteLS + "forwarding-options dhcp-relay dhcpv6 " + case instance == defaultW && version == "v4": + delPrefix = deleteLS + "forwarding-options dhcp-relay " + case instance != defaultW && version == "v6": + delPrefix = delRoutingInstances + instance + " forwarding-options dhcp-relay dhcpv6 " + case instance != defaultW && version == "v4": + delPrefix = delRoutingInstances + instance + " forwarding-options dhcp-relay " + } + listLinesToDelete := []string{ + "access-profile", + "active-leasequery", + "active-server-group", + "arp-inspection", + "authentication", + "bulk-leasequery", + "client-response-ttl", + "duplicate-clients-in-subnet", + "duplicate-clients", + "dynamic-profile", + "exclude-relay-agent-identifier", + "forward-only", + "forward-only-replies", + "forward-snooped-clients", + "lease-time-validation", + "leasequery", + "liveness-detection", + "maximum-hop-count", + "minimum-wait-time", + "no-snoop", + "overrides", + "persistent-storage", + "relay-agent-interface-id", + "relay-agent-option-79", + "relay-agent-remote-id", + "relay-option", + "relay-option-82", + "remote-id-mismatch", + "route-suppression", + "server-match", + "server-response-time", + "service-profile", + "short-cycle-protection", + "source-ip-change", + "vendor-specific-information", + } + configSet := make([]string, len(listLinesToDelete)) + for k, line := range listLinesToDelete { + configSet[k] = delPrefix + line + } + + return clt.configSet(configSet, junSess) +} + +func fillForwardingOptionsDhcpRelayData( + d *schema.ResourceData, fwdOptsDhcpRelayOptions fwdOptsDhcpRelayOptions, +) { + if tfErr := d.Set( + "routing_instance", + fwdOptsDhcpRelayOptions.routingInstance, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "version", + fwdOptsDhcpRelayOptions.version, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "access_profile", + fwdOptsDhcpRelayOptions.accessProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "active_leasequery", + fwdOptsDhcpRelayOptions.activeLeasequery, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "active_server_group", + fwdOptsDhcpRelayOptions.activeServerGroup, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "active_server_group_allow_server_change", + fwdOptsDhcpRelayOptions.activeServerGroupAllowServerChange, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "arp_inspection", + fwdOptsDhcpRelayOptions.arpInspection, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "authentication_password", + fwdOptsDhcpRelayOptions.authenticationPassword, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "authentication_username_include", + fwdOptsDhcpRelayOptions.authenticationUsernameInclude, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "bulk_leasequery", + fwdOptsDhcpRelayOptions.bulkLeasequery, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "client_response_ttl", + fwdOptsDhcpRelayOptions.clientResponseTTL, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "duplicate_clients_in_subnet", + fwdOptsDhcpRelayOptions.duplicateClientsInSubnet, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "duplicate_clients_incoming_interface", + fwdOptsDhcpRelayOptions.duplicateClientsIncomingInterface, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile", + fwdOptsDhcpRelayOptions.dynamicProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_aggregate_clients", + fwdOptsDhcpRelayOptions.dynamicProfileAggregateClients, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_aggregate_clients_action", + fwdOptsDhcpRelayOptions.dynamicProfileAggregateClientsAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "dynamic_profile_use_primary", + fwdOptsDhcpRelayOptions.dynamicProfileUsePrimary, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "exclude_relay_agent_identifier", + fwdOptsDhcpRelayOptions.excludeRelayAgentIdentifier, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_only", + fwdOptsDhcpRelayOptions.forwardOnly, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_only_replies", + fwdOptsDhcpRelayOptions.forwardOnlyReplies, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_only_routing_instance", + fwdOptsDhcpRelayOptions.forwardOnlyRoutingInstance, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "forward_snooped_clients", + fwdOptsDhcpRelayOptions.forwardSnoopedClients, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "lease_time_validation", + fwdOptsDhcpRelayOptions.leaseTimeValidation, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "leasequery", + fwdOptsDhcpRelayOptions.leasequery, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_failure_action", + fwdOptsDhcpRelayOptions.livenessDetectionFailureAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_method_bfd", + fwdOptsDhcpRelayOptions.livenessDetectionMethodBfd, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "liveness_detection_method_layer2", + fwdOptsDhcpRelayOptions.livenessDetectionMethodLayer2, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "maximum_hop_count", + fwdOptsDhcpRelayOptions.maximumHopCount, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "minimum_wait_time", + fwdOptsDhcpRelayOptions.minimumWaitTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "no_snoop", + fwdOptsDhcpRelayOptions.noSnoop, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "overrides_v4", + fwdOptsDhcpRelayOptions.overridesV4, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "overrides_v6", + fwdOptsDhcpRelayOptions.overridesV6, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "persistent_storage_automatic", + fwdOptsDhcpRelayOptions.persistentStorageAutomatic, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_interface_id", + fwdOptsDhcpRelayOptions.relayAgentInterfaceID, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_option_79", + fwdOptsDhcpRelayOptions.relayAgentOption79, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_agent_remote_id", + fwdOptsDhcpRelayOptions.relayAgentRemoteID, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_option", + fwdOptsDhcpRelayOptions.relayOption, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "relay_option_82", + fwdOptsDhcpRelayOptions.relayOption82, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "remote_id_mismatch_disconnect", + fwdOptsDhcpRelayOptions.remoteIDMismatchDisconnect, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_access", + fwdOptsDhcpRelayOptions.routeSuppressionAccess, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_access_internal", + fwdOptsDhcpRelayOptions.routeSuppressionAccessInternal, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "route_suppression_destination", + fwdOptsDhcpRelayOptions.routeSuppressionDestination, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_address", + fwdOptsDhcpRelayOptions.serverMatchAddress, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_default_action", + fwdOptsDhcpRelayOptions.serverMatchDefaultAction, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_match_duid", + fwdOptsDhcpRelayOptions.serverMatchDuid, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "server_response_time", + fwdOptsDhcpRelayOptions.serverResponseTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "service_profile", + fwdOptsDhcpRelayOptions.serviceProfile, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "short_cycle_protection_lockout_max_time", + fwdOptsDhcpRelayOptions.shortCycleProtectionLockoutMaxTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "short_cycle_protection_lockout_min_time", + fwdOptsDhcpRelayOptions.shortCycleProtectionLockoutMinTime, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "source_ip_change", + fwdOptsDhcpRelayOptions.sourceIPChange, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "vendor_specific_information_host_name", + fwdOptsDhcpRelayOptions.vendorSpecificInformationHostName, + ); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set( + "vendor_specific_information_location", + fwdOptsDhcpRelayOptions.vendorSpecificInformationLocation, + ); tfErr != nil { + panic(tfErr) + } +} diff --git a/junos/resource_forwardingoptions_dhcprelay_test.go b/junos/resource_forwardingoptions_dhcprelay_test.go new file mode 100644 index 00000000..84bac183 --- /dev/null +++ b/junos/resource_forwardingoptions_dhcprelay_test.go @@ -0,0 +1,519 @@ +package junos_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccJunosForwardingOptionsDhcpRelay_basic(t *testing.T) { + if os.Getenv("TESTACC_ROUTER") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosForwardingOptionsDhcpRelayConfigCreate(), + }, + { + Config: testAccJunosForwardingOptionsDhcpRelayConfigUpdate(), + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay.testacc_dhcprelay_v4_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay.testacc_dhcprelay_v6_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay.testacc_dhcprelay_v4_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_forwardingoptions_dhcprelay.testacc_dhcprelay_v6_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccJunosForwardingOptionsDhcpRelayConfigUpdate2(), + }, + }, + }) + } +} + +func testAccJunosForwardingOptionsDhcpRelayConfigCreate() string { + return ` +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v4_default" { + active_leasequery {} + bulk_leasequery {} +} + +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v6_default" { + version = "v6" +} + +resource "junos_routing_instance" "testacc_dhcprelay" { + name = "testacc_dhcprelay" +} +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v4_ri" { + routing_instance = junos_routing_instance.testacc_dhcprelay.name + leasequery {} + overrides_v4 { + always_write_option_82 = true + } + relay_option_82 { + circuit_id {} + } +} +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v6_ri" { + routing_instance = junos_routing_instance.testacc_dhcprelay.name + version = "v6" + + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} + +// nolint: lll,nolintlint +func testAccJunosForwardingOptionsDhcpRelayConfigUpdate() string { + return ` +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v4_default" { + active_leasequery { + idle_timeout = 10 + peer_address = "192.0.2.1" + timeout = 11 + topology_discover = true + } + active_server_group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v4_default.name + active_server_group_allow_server_change = true + arp_inspection = true + authentication_password = "test1#1" + authentication_username_include { + circuit_type = true + client_id = true + client_id_exclude_headers = true + client_id_use_automatic_ascii_hex_encoding = true + delimiter = "#" + domain_name = "a domain" + interface_description = "logical" + interface_name = true + mac_address = true + option_60 = true + option_82 = true + option_82_circuit_id = true + option_82_remote_id = true + routing_instance_name = true + user_prefix = "user_#1_" + vlan_tags = true + } + bulk_leasequery {} + client_response_ttl = 60 + duplicate_clients_in_subnet = "option-82" + dynamic_profile = "junos-default-profile" + forward_only = true + forward_only_routing_instance = junos_routing_instance.testacc_dhcprelay.name + maximum_hop_count = 8 + minimum_wait_time = 0 + + overrides_v4 { + allow_snooped_clients = true + always_write_giaddr = true + always_write_option_82 = true + delay_authentication = true + disable_relay = true + layer2_unicast_replies = true + no_bind_on_request = true + proxy_mode = true + relay_source = "lo0.1" + replace_ip_source_with_giaddr = true + send_release_on_delete = true + trust_option_82 = true + user_defined_option_82 = "#test" + } + relay_option { + option_60 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "local-server-group" + group = junos_system_services_dhcp_localserver_group.testacc_dhcprelay_v4_default.name + } + option_60_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v4_default.name + } + option_77 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v4_default.name + } + option_77 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v4_default.name + } + option_77_default_action { + action = "local-server-group" + group = junos_system_services_dhcp_localserver_group.testacc_dhcprelay_v4_default.name + } + option_order = ["77", "60"] + } + relay_option_82 { + circuit_id { + include_irb_and_l2 = true + keep_incoming_circuit_id = true + no_vlan_interface_name = true + prefix_host_name = true + prefix_routing_instance_name = true + use_interface_description = "logical" + } + exclude_relay_agent_identifier = true + link_selection = true + remote_id { + include_irb_and_l2 = true + keep_incoming_remote_id = true + no_vlan_interface_name = true + prefix_routing_instance_name = true + use_interface_description = "device" + } + vendor_specific_host_name = true + vendor_specific_location = true + } + route_suppression_destination = true + server_match_address { + address = "192.0.2.1/30" + action = "create-relay-entry" + } + server_match_default_action = "forward-only" + source_ip_change = true +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_v4_default" { + lifecycle { + create_before_destroy = true + } + name = "testacc_dhcprelay_v4_default" +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcprelay_v4_default" { + lifecycle { + create_before_destroy = true + } + name = "testacc_dhcprelay_v4_default" + + dynamic_profile = "junos-default-profile" +} + +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v6_default" { + version = "v6" + + authentication_username_include { + client_id = true + relay_agent_interface_id = true + relay_agent_remote_id = true + relay_agent_subscriber_id = true + } + bulk_leasequery { + attempts = 4 + timeout = 2 + trigger_automatic = true + } + duplicate_clients_incoming_interface = true + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-dhcp-profile" + exclude_relay_agent_identifier = true + forward_only = true + forward_only_replies = true + forward_only_routing_instance = junos_routing_instance.testacc_dhcprelay.name + lease_time_validation {} + liveness_detection_method_layer2 { + max_consecutive_retries = 4 + transmit_interval = 305 + } + overrides_v6 { + always_process_option_request_option = true + asymmetric_lease_time = 900 + asymmetric_prefix_lease_time = 1000 + client_negotiation_match_incoming_interface = true + delay_authentication = true + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + no_allow_snooped_clients = true + no_bind_on_request = true + relay_source = "lo0.1" + send_release_on_delete = true + } + relay_agent_interface_id {} + relay_agent_option_79 = true + relay_agent_remote_id {} + relay_option { + option_15 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "drop" + } + option_15 { + compare = "equals" + value_type = "hexadecimal" + value = "AABBff" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v6_default.name + } + option_15_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v6_default.name + } + option_16 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v6_default.name + } + option_16 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v6_default.name + } + option_16_default_action { + action = "relay-server-group" + group = junos_forwardingoptions_dhcprelay_servergroup.testacc_dhcprelay_v6_default.name + } + option_order = ["16", "15"] + } + route_suppression_access = true + route_suppression_access_internal = true + server_match_duid { + compare = "equals" + value_type = "ascii" + value = " test_space " + action = "forward-only" + } + vendor_specific_information_host_name = true + vendor_specific_information_location = true +} +resource "junos_forwardingoptions_dhcprelay_servergroup" "testacc_dhcprelay_v6_default" { + lifecycle { + create_before_destroy = true + } + name = "testacc_dhcprelay_v6_default" + version = "v6" +} + +resource "junos_routing_instance" "testacc_dhcprelay" { + name = "testacc_dhcprelay" +} +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v4_ri" { + routing_instance = junos_routing_instance.testacc_dhcprelay.name + + authentication_username_include { + client_id = true + client_id_exclude_headers = true + client_id_use_automatic_ascii_hex_encoding = true + option_82 = true + } + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + forward_snooped_clients = "all-interfaces" + lease_time_validation { + lease_time_threshold = 60099 + violation_action_drop = true + } + liveness_detection_method_bfd { + detection_time_threshold = 200000 + holddown_interval = 2 + minimum_interval = 30003 + minimum_receive_interval = 30004 + multiplier = 5 + no_adaptation = true + session_mode = "multihop" + transmit_interval_minimum = 30006 + transmit_interval_threshold = 30066 + version = "automatic" + } + liveness_detection_failure_action = "log-only" + leasequery { + attempts = 9 + timeout = 8 + } + overrides_v4 { + client_discover_match = "incoming-interface" + always_write_option_82 = true + } + relay_option_82 { + circuit_id { + use_vlan_id = true + } + remote_id { + use_vlan_id = true + } + } + route_suppression_destination = true + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} + +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v6_ri" { + routing_instance = junos_routing_instance.testacc_dhcprelay.name + version = "v6" + + + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + dynamic_profile_aggregate_clients_action = "merge" + + overrides_v6 { + allow_snooped_clients = true + } + relay_agent_interface_id { + include_irb_and_l2 = true + keep_incoming_id = true + keep_incoming_id_strict = true + no_vlan_interface_name = true + prefix_host_name = true + prefix_routing_instance_name = true + use_interface_description = "logical" + use_option_82 = true + } + relay_agent_remote_id { + keep_incoming_id = true + use_option_82 = true + use_option_82_strict = true + use_vlan_id = true + } + relay_option { + option_15 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "forward-only" + } + option_15 { + compare = "equals" + value_type = "hexadecimal" + value = "11BBee" + action = "drop" + } + option_15_default_action { + action = "drop" + } + option_16 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "forward-only" + } + option_16_default_action { + action = "forward-only" + } + option_order = ["15", "16"] + } + remote_id_mismatch_disconnect = true + route_suppression_access = true + service_profile = "service-pro#2" + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} + +// nolint: lll,nolintlint +func testAccJunosForwardingOptionsDhcpRelayConfigUpdate2() string { + return ` +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v4_default" { + + no_snoop = true + overrides_v4 { + allow_no_end_option = true + asymmetric_lease_time = 7200 + bootp_support = true + delete_binding_on_renegotiation = true + no_allow_snooped_clients = true + no_unicast_replies = true + } + persistent_storage_automatic = true + relay_option { + option_60 { + compare = "equals" + value_type = "ascii" + value = " equals ascii " + action = "drop" + } + option_60_default_action { + action = "forward-only" + } + option_77 { + compare = "starts-with" + value_type = "ascii" + value = " start ascii " + action = "forward-only" + } + option_77_default_action { + action = "drop" + } + option_order = ["77", "60"] + } + server_response_time = 12001 +} + +resource "junos_forwardingoptions_dhcprelay" "testacc_dhcprelay_v6_default" { + version = "v6" + + authentication_username_include { + client_id = true + relay_agent_interface_id = true + relay_agent_remote_id = true + relay_agent_subscriber_id = true + } + duplicate_clients_incoming_interface = true + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-dhcp-profile" + exclude_relay_agent_identifier = true + forward_only = true + forward_only_replies = true + lease_time_validation {} + liveness_detection_method_layer2 { + max_consecutive_retries = 4 + transmit_interval = 305 + } + overrides_v6 { + always_process_option_request_option = true + asymmetric_lease_time = 900 + asymmetric_prefix_lease_time = 1000 + client_negotiation_match_incoming_interface = true + delay_authentication = true + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + no_allow_snooped_clients = true + no_bind_on_request = true + relay_source = "lo0.1" + send_release_on_delete = true + } + relay_agent_option_79 = true + route_suppression_access = true + route_suppression_access_internal = true + server_match_duid { + compare = "equals" + value_type = "ascii" + value = " test_space " + action = "forward-only" + } + vendor_specific_information_host_name = true + vendor_specific_information_location = true +} +` +} From 6ded2e22fa09004d41399d8672c0132ca8a0116b Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 5 Oct 2022 09:27:39 +0200 Subject: [PATCH 13/17] r/policyoptions_policy_statement: add bgp_as_path_unique_count block argument inside from block argument Fix #424 --- CHANGELOG.md | 2 + .../policyoptions_policy_statement.md | 7 ++ ...resource_policyoptions_policy_statement.go | 93 ++++++++++++++----- junos/resource_policyoptions_test.go | 62 ++++++++----- 4 files changed, 117 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b82be18e..358aed74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ FEATURES: ENHANCEMENTS: +* resource/`junos_policyoptions_policy_statement`: add `bgp_as_path_unique_count` block argument inside `from` block argument (Fixes #424) + BUG FIXES: ## 1.30.1 (September 09, 2022) diff --git a/docs/resources/policyoptions_policy_statement.md b/docs/resources/policyoptions_policy_statement.md index 405f6d4e..68299939 100644 --- a/docs/resources/policyoptions_policy_statement.md +++ b/docs/resources/policyoptions_policy_statement.md @@ -79,6 +79,13 @@ The following arguments are supported: - **bgp_as_path_group** (Optional, Set of String) Name of AS path group. See resource `junos_policyoptions_as_path_group`. +- **bgp_as_path_unique_count** (Optional, Block Set) + For each count, number of unique BGP ASes excluding confederations. + - **count** (Required, Number) + Number of ASes (0..1024). + - **match** (Required, String) + Type of match: equal values, higher or equal values, lower or equal values. + Need to `equal`, `orhigher` or `orlower`. - **bgp_community** (Optional, Set of String) BGP community. See resource `junos_policyoptions_community`. diff --git a/junos/resource_policyoptions_policy_statement.go b/junos/resource_policyoptions_policy_statement.go index 53b011ab..ca88214a 100644 --- a/junos/resource_policyoptions_policy_statement.go +++ b/junos/resource_policyoptions_policy_statement.go @@ -127,6 +127,24 @@ func schemaPolicyoptionsPolicyStatementFrom() map[string]*schema.Schema { ValidateDiagFunc: validateNameObjectJunos([]string{"default"}, 64, formatDefault), }, }, + "bgp_as_path_unique_count": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "count": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1024), + }, + "match": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equal", "orhigher", "orlower"}, false), + }, + }, + }, + }, "bgp_community": { Type: schema.TypeSet, Optional: true, @@ -719,7 +737,10 @@ func setPolicyStatement(d *schema.ResourceData, clt *Client, junSess *junosSessi setPrefix := "set policy-options policy-statement " + d.Get("name").(string) for _, from := range d.Get("from").([]interface{}) { if from != nil { - configSetFrom := setPolicyStatementOptsFrom(setPrefix, from.(map[string]interface{})) + configSetFrom, err := setPolicyStatementOptsFrom(setPrefix, from.(map[string]interface{})) + if err != nil { + return err + } configSet = append(configSet, configSetFrom...) } } @@ -748,7 +769,10 @@ func setPolicyStatement(d *schema.ResourceData, clt *Client, junSess *junosSessi setPrefixTerm := setPrefix + " term " + termMap["name"].(string) for _, from := range termMap["from"].([]interface{}) { if from != nil { - configSetFrom := setPolicyStatementOptsFrom(setPrefixTerm, from.(map[string]interface{})) + configSetFrom, err := setPolicyStatementOptsFrom(setPrefixTerm, from.(map[string]interface{})) + if err != nil { + return err + } configSet = append(configSet, configSetFrom...) } } @@ -921,7 +945,7 @@ func fillPolicyStatementData(d *schema.ResourceData, policyStatementOptions poli } } -func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) []string { +func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) ([]string, error) { configSet := make([]string, 0) setPrefixFrom := setPrefix + " from " @@ -934,6 +958,17 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) [ for _, v := range sortSetOfString(opts["bgp_as_path_group"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"as-path-group "+v) } + bgpASPathUniqueCountList := make([]int, 0) + for _, block := range opts["bgp_as_path_unique_count"].(*schema.Set).List() { + bgpASPathUniqueCount := block.(map[string]interface{}) + count := bgpASPathUniqueCount["count"].(int) + if bchk.IntInSlice(count, bgpASPathUniqueCountList) { + return configSet, fmt.Errorf("multiple blocks bgp_as_path_unique_count with the same count %d", count) + } + bgpASPathUniqueCountList = append(bgpASPathUniqueCountList, count) + configSet = append(configSet, + setPrefixFrom+"as-path-unique-count "+strconv.Itoa(count)+" "+bgpASPathUniqueCount["match"].(string)) + } for _, v := range sortSetOfString(opts["bgp_community"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"community "+v) } @@ -986,7 +1021,7 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) [ configSet = append(configSet, setRoutFilter) } - return configSet + return configSet, nil } func setPolicyStatementOptsThen(setPrefix string, opts map[string]interface{}) ([]string, error) { @@ -1131,6 +1166,19 @@ func readPolicyStatementOptsFrom(item string, fromMap map[string]interface{}) er case strings.HasPrefix(item, "as-path-group "): fromMap["bgp_as_path_group"] = append(fromMap["bgp_as_path_group"].([]string), strings.TrimPrefix(item, "as-path-group ")) + case strings.HasPrefix(item, "as-path-unique-count "): + countStr := strings.Split(strings.TrimPrefix(item, "as-path-unique-count "), " ")[0] + count, err := strconv.Atoi(countStr) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + fromMap["bgp_as_path_unique_count"] = append( + fromMap["bgp_as_path_unique_count"].([]map[string]interface{}), + map[string]interface{}{ + "count": count, + "match": strings.TrimPrefix(item, "as-path-unique-count "+countStr+" "), + }, + ) case strings.HasPrefix(item, "community "): fromMap["bgp_community"] = append(fromMap["bgp_community"].([]string), strings.TrimPrefix(item, "community ")) case strings.HasPrefix(item, "origin "): @@ -1337,24 +1385,25 @@ func readPolicyStatementOptsTo(item string, toMap map[string]interface{}) error func genMapPolicyStatementOptsFrom() map[string]interface{} { return map[string]interface{}{ - "aggregate_contributor": false, - "bgp_as_path": make([]string, 0), - "bgp_as_path_group": make([]string, 0), - "bgp_community": make([]string, 0), - "bgp_origin": "", - "family": "", - "local_preference": 0, - "routing_instance": "", - "interface": make([]string, 0), - "metric": 0, - "neighbor": make([]string, 0), - "next_hop": make([]string, 0), - "ospf_area": "", - "policy": make([]string, 0), - "preference": 0, - "prefix_list": make([]string, 0), - "protocol": make([]string, 0), - "route_filter": make([]map[string]interface{}, 0), + "aggregate_contributor": false, + "bgp_as_path": make([]string, 0), + "bgp_as_path_group": make([]string, 0), + "bgp_as_path_unique_count": make([]map[string]interface{}, 0), + "bgp_community": make([]string, 0), + "bgp_origin": "", + "family": "", + "local_preference": 0, + "routing_instance": "", + "interface": make([]string, 0), + "metric": 0, + "neighbor": make([]string, 0), + "next_hop": make([]string, 0), + "ospf_area": "", + "policy": make([]string, 0), + "preference": 0, + "prefix_list": make([]string, 0), + "protocol": make([]string, 0), + "route_filter": make([]map[string]interface{}, 0), } } diff --git a/junos/resource_policyoptions_test.go b/junos/resource_policyoptions_test.go index 3a692361..b8c9fbee 100644 --- a/junos/resource_policyoptions_test.go +++ b/junos/resource_policyoptions_test.go @@ -750,17 +750,25 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { from { aggregate_contributor = true bgp_as_path = [junos_policyoptions_as_path.testacc_policyOptions.name] - bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] - bgp_origin = "igp" - family = "inet" - local_preference = 100 - routing_instance = junos_routing_instance.testacc_policyOptions.name - interface = ["st0.0"] - metric = 5 - neighbor = ["192.0.2.4"] - next_hop = ["192.0.2.4"] - ospf_area = "0.0.0.0" - preference = 100 + bgp_as_path_unique_count { + count = 3 + match = "equal" + } + bgp_as_path_unique_count { + count = 2 + match = "orhigher" + } + bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] + bgp_origin = "igp" + family = "inet" + local_preference = 100 + routing_instance = junos_routing_instance.testacc_policyOptions.name + interface = ["st0.0"] + metric = 5 + neighbor = ["192.0.2.4"] + next_hop = ["192.0.2.4"] + ospf_area = "0.0.0.0" + preference = 100 prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name, junos_policyoptions_prefix_list.testacc_policyOptions2.name, ] @@ -805,20 +813,24 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { from { aggregate_contributor = true bgp_as_path = [junos_policyoptions_as_path.testacc_policyOptions.name] - bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] - bgp_origin = "igp" - family = "inet" - local_preference = 100 - routing_instance = junos_routing_instance.testacc_policyOptions.name - interface = ["st0.0"] - metric = 5 - neighbor = ["192.0.2.4"] - next_hop = ["192.0.2.4"] - ospf_area = "0.0.0.0" - policy = [junos_policyoptions_policy_statement.testacc_policyOptions2.name] - preference = 100 - prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name] - protocol = ["bgp"] + bgp_as_path_unique_count { + count = 4 + match = "orlower" + } + bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] + bgp_origin = "igp" + family = "inet" + local_preference = 100 + routing_instance = junos_routing_instance.testacc_policyOptions.name + interface = ["st0.0"] + metric = 5 + neighbor = ["192.0.2.4"] + next_hop = ["192.0.2.4"] + ospf_area = "0.0.0.0" + policy = [junos_policyoptions_policy_statement.testacc_policyOptions2.name] + preference = 100 + prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name] + protocol = ["bgp"] route_filter { route = "192.0.2.0/25" option = "exact" From c3035f3ca8298e94dda3bc2ac7224feb783c4212 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 5 Oct 2022 09:33:15 +0200 Subject: [PATCH 14/17] [workflows] bump golangci-lint to v1.50 and fix errors --- .github/workflows/linters.yml | 2 +- junos/resource_policyoptions_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 7fcf4483..b4bc101e 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -19,7 +19,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3.2.0 with: - version: v1.49 + version: v1.50 args: -c .golangci.yml -v markdown-lint: diff --git a/junos/resource_policyoptions_test.go b/junos/resource_policyoptions_test.go index 3a692361..e0151443 100644 --- a/junos/resource_policyoptions_test.go +++ b/junos/resource_policyoptions_test.go @@ -137,9 +137,9 @@ func TestAccJunosPolicyOptions_basic(t *testing.T) { resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", "then.0.action", "accept"), resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", - "then.0.as_path_expand", "65000 65000"), + "then.0.as_path_expand", "65000 65000"), //nolint: dupword resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", - "then.0.as_path_prepend", "65000 65000"), + "then.0.as_path_prepend", "65000 65000"), //nolint: dupword resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", "then.0.community.#", "3"), resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", @@ -289,7 +289,7 @@ func TestAccJunosPolicyOptions_basic(t *testing.T) { resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", "term.0.then.0.as_path_expand", "last-as count 1"), resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", - "term.0.then.0.as_path_prepend", "65000 65000"), + "term.0.then.0.as_path_prepend", "65000 65000"), //nolint: dupword resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", "term.0.then.0.community.#", "3"), resource.TestCheckResourceAttr("junos_policyoptions_policy_statement.testacc_policyOptions", From 7cdb2987b591dd64cb220a33fd1b6ddaf36a2f6a Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 6 Oct 2022 09:07:12 +0200 Subject: [PATCH 15/17] bump github.com/hashicorp/terraform-plugin-sdk/v2 to v2.23.0 with the go replace to minor fix to accept empty list of primitives on apply --- go.mod | 12 ++++++------ go.sum | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 08b30efb..33f48e70 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.21.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0 github.com/jeremmfr/go-netconf v0.4.6 github.com/jeremmfr/go-utils v0.4.1 github.com/jeremmfr/junosdecode v1.1.0 @@ -17,7 +17,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -27,9 +27,9 @@ require ( github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.13.0 // indirect + github.com/hashicorp/hcl/v2 v2.14.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.17.2 // indirect + github.com/hashicorp/terraform-exec v0.17.3 // indirect github.com/hashicorp/terraform-json v0.14.0 // indirect github.com/hashicorp/terraform-plugin-go v0.14.0 // indirect github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect @@ -47,7 +47,7 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect - github.com/zclconf/go-cty v1.10.0 // indirect + github.com/zclconf/go-cty v1.11.0 // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect @@ -57,4 +57,4 @@ require ( google.golang.org/protobuf v1.28.1 // indirect ) -replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/jeremmfr/terraform-plugin-sdk/v2 v2.21.1-0.20220829112741-881e1cfd6a30 +replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/jeremmfr/terraform-plugin-sdk/v2 v2.23.1-0.20221006070247-a8f40383f40c diff --git a/go.sum b/go.sum index 773706ac..a78e0fbb 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -108,12 +109,12 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk= github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI= -github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= -github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hcl/v2 v2.14.0 h1:jX6+Q38Ly9zaAJlAjnFVyeNSNCKKW8D0wvyg7vij5Wc= +github.com/hashicorp/hcl/v2 v2.14.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.17.2 h1:EU7i3Fh7vDUI9nNRdMATCEfnm9axzTnad8zszYZ73Go= -github.com/hashicorp/terraform-exec v0.17.2/go.mod h1:tuIbsL2l4MlwwIZx9HPM+LOV9vVyEfBYu2GsO1uH3/8= +github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= +github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= github.com/hashicorp/terraform-plugin-go v0.14.0 h1:ttnSlS8bz3ZPYbMb84DpcPhY4F5DsQtcAS7cHo8uvP4= @@ -136,8 +137,8 @@ github.com/jeremmfr/go-utils v0.4.1 h1:/Jhy9vmFW5uPNycVA/KqNxgifw1gEHFIn+Clik6gB github.com/jeremmfr/go-utils v0.4.1/go.mod h1:K0lGadiSvg9OKGJnW4Bs3t18/VApp/6x2+BV93Sts2M= github.com/jeremmfr/junosdecode v1.1.0 h1:Os8QeOzyL+BPuDZJMjyJgz4QPOgA8EChgKB2Ih5wwCc= github.com/jeremmfr/junosdecode v1.1.0/go.mod h1:nTY0XbZC2ePbZdV0wuUboSMtGrJxtpwWVYfHjrS2Oqw= -github.com/jeremmfr/terraform-plugin-sdk/v2 v2.21.1-0.20220829112741-881e1cfd6a30 h1:2+8GTDhi9hrebTs7WwSTTR5EZ61dBQDvDgEvlAu6ib0= -github.com/jeremmfr/terraform-plugin-sdk/v2 v2.21.1-0.20220829112741-881e1cfd6a30/go.mod h1:mYPs/uchNcBq7AclQv9QUtSf9iNcfp1Ag21jqTlDf2M= +github.com/jeremmfr/terraform-plugin-sdk/v2 v2.23.1-0.20221006070247-a8f40383f40c h1:czxC1HtzZxGZBZuJ8IHVqp4DUKTocaRJEnyA4EyRzpw= +github.com/jeremmfr/terraform-plugin-sdk/v2 v2.23.1-0.20221006070247-a8f40383f40c/go.mod h1:xkJGavPvP9kYS/VbiW8o7JuTNgPwm7Tiw/Ie/b46r4c= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= @@ -205,8 +206,9 @@ github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= +github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= From eca7e898da253b87a76ad90dcc97f1b00e15289a Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Sat, 8 Oct 2022 15:43:36 +0200 Subject: [PATCH 16/17] r/policyoptions_policy_statement: add some arguments inside from block arguments : - bgp_as_path_calc_length - bgp_community_count - bgp_srte_discriminator - color - evpn_esi - evpn_mac_route - evpn_tag - next_hop_type_merged - next_hop_weight - route_type - srte_color - state - tunnel_type - validation_database --- CHANGELOG.md | 2 +- .../policyoptions_policy_statement.md | 48 +++ junos/func_common.go | 16 +- ...resource_policyoptions_policy_statement.go | 278 ++++++++++++++++++ junos/resource_policyoptions_test.go | 92 ++++-- 5 files changed, 408 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 358aed74..9f553650 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ FEATURES: ENHANCEMENTS: -* resource/`junos_policyoptions_policy_statement`: add `bgp_as_path_unique_count` block argument inside `from` block argument (Fixes #424) +* resource/`junos_policyoptions_policy_statement`: add `bgp_as_path_calc_length`, `bgp_as_path_unique_count`, `bgp_community_count`, `bgp_srte_discriminator`, `color`, `evpn_esi`, `evpn_mac_route`, `evpn_tag`, `next_hop_type_merged`, `next_hop_weight`, `route_type`, `srte_color`, `state`, `tunnel_type` and `validation_database` arguments inside `from` block arguments (Fixes #424) BUG FIXES: diff --git a/docs/resources/policyoptions_policy_statement.md b/docs/resources/policyoptions_policy_statement.md index 68299939..76b036c4 100644 --- a/docs/resources/policyoptions_policy_statement.md +++ b/docs/resources/policyoptions_policy_statement.md @@ -76,6 +76,13 @@ The following arguments are supported: - **bgp_as_path** (Optional, Set of String) Name of AS path regular expression. See resource `junos_policyoptions_as_path`. +- **bgp_as_path_calc_length** Optional, Block Set) + For each count, number of BGP ASes excluding confederations. + - **count** (Required, Number) + Number of ASes (0..1024). + - **match** (Required, String) + Type of match: equal values, higher or equal values, lower or equal values. + Need to `equal`, `orhigher` or `orlower`. - **bgp_as_path_group** (Optional, Set of String) Name of AS path group. See resource `junos_policyoptions_as_path_group`. @@ -89,9 +96,27 @@ The following arguments are supported: - **bgp_community** (Optional, Set of String) BGP community. See resource `junos_policyoptions_community`. +- **bgp_community_count** (Optional, Block Set) + For each count, number of BGP communities. + - **count** (Required, Number) + Number of communities (0..1024). + - **match** (Required, String) + Type of match: equal values, higher or equal values, lower or equal values. + Need to `equal`, `orhigher` or `orlower`. - **bgp_origin** (Optional, String) BGP origin attribute. Need to be `egp`, `igp` or `incomplete`. +- **bgp_srte_discriminator** (Optional, Number) + Srte discriminator. +- **color** (Optional, Number) + Color (preference) value. +- **evpn_esi** (Optional, Set of String) + ESI in EVPN Route. +- **evpn_mac_route** (Optional, String) + EVPN Mac Route type. + Need to be `mac-ipv4`, `mac-ipv6` or `mac-only`. +- **evpn_tag** (Optional, Set of Number) + Tag in EVPN Route (0..4294967295). - **family** (Optional, String) IP family. - **local_preference** (Optional, Number) @@ -106,6 +131,15 @@ The following arguments are supported: Neighboring router - **next_hop** (Optional, Set of String) Next-hop router +- **next_hop_type_merged** (Optional, Boolean) + Merged next hop. +- **next_hop_weight** (Optional, Block Set) + For each combination of block arguments, weight of the gateway. + - **match** (Required, String) + Type of match for weight. + Need to be `equal`, `greater-than`, `greater-than-equal`, `less-than` or `less-than-equal`. + - **weight** (Required, Weight) + Weight of the gateway (1..65535). - **ospf_area** (Optional, String) OSPF area identifier - **policy** (Optional, List of String) @@ -126,6 +160,20 @@ The following arguments are supported: Need to be `address-mask`, `exact`, `longer`, `orlonger`, `prefix-length-range`, `through` or `upto`. - **option_value** (Optional, String) For options that need an argument +- **route_type** (Optional, String) + Route type. + Need to be `external` or `internal`. +- **srte_color** (Optional, Number) + Srte color. +- **state** (Optional, String) + Route state. + Need to be `active` or `inactive`. +- **tunnel_type** (Optional, Set of String) + Tunnel type. + Element need to be `gre`, `ipip` or `udp`. +- **validation_database** (Optional, String) + Name to identify a validation-state. + Need to be `invalid`, `unknown` or `valid`. --- diff --git a/junos/func_common.go b/junos/func_common.go index 862c875f..2350d0a1 100644 --- a/junos/func_common.go +++ b/junos/func_common.go @@ -280,9 +280,19 @@ func validateFilePermission() schema.SchemaValidateDiagFunc { } func sortSetOfString(list []interface{}) []string { - s := make([]string, 0) - for _, e := range list { - s = append(s, e.(string)) + s := make([]string, len(list)) + for k, e := range list { + s[k] = e.(string) + } + sort.Strings(s) + + return s +} + +func sortSetOfNumberToString(list []interface{}) []string { + s := make([]string, len(list)) + for k, e := range list { + s[k] = strconv.Itoa(e.(int)) } sort.Strings(s) diff --git a/junos/resource_policyoptions_policy_statement.go b/junos/resource_policyoptions_policy_statement.go index ca88214a..f693fc93 100644 --- a/junos/resource_policyoptions_policy_statement.go +++ b/junos/resource_policyoptions_policy_statement.go @@ -3,6 +3,7 @@ package junos import ( "context" "fmt" + "regexp" "strconv" "strings" @@ -119,6 +120,24 @@ func schemaPolicyoptionsPolicyStatementFrom() map[string]*schema.Schema { ValidateDiagFunc: validateNameObjectJunos([]string{"default"}, 64, formatDefault), }, }, + "bgp_as_path_calc_length": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "count": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1024), + }, + "match": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equal", "orhigher", "orlower"}, false), + }, + }, + }, + }, "bgp_as_path_group": { Type: schema.TypeSet, Optional: true, @@ -153,11 +172,63 @@ func schemaPolicyoptionsPolicyStatementFrom() map[string]*schema.Schema { ValidateDiagFunc: validateNameObjectJunos([]string{"default"}, 64, formatDefault), }, }, + "bgp_community_count": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "count": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1024), + }, + "match": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equal", "orhigher", "orlower"}, false), + }, + }, + }, + }, "bgp_origin": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"egp", "igp", "incomplete"}, false), }, + "bgp_srte_discriminator": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "color": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "evpn_esi": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringMatch(regexp.MustCompile( + `^([\d\w]{2}:){9}[\d\w]{2}$`), "bad format or length"), + }, + }, + "evpn_mac_route": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"mac-ipv4", "mac-ipv6", "mac-only"}, false), + }, + "evpn_tag": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + }, "family": { Type: schema.TypeString, Optional: true, @@ -194,6 +265,30 @@ func schemaPolicyoptionsPolicyStatementFrom() map[string]*schema.Schema { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "next_hop_type_merged": { + Type: schema.TypeBool, + Optional: true, + }, + "next_hop_weight": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "match": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "equal", "greater-than", "greater-than-equal", "less-than", "less-than-equal", + }, false), + }, + "weight": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + }, + }, + }, "ospf_area": { Type: schema.TypeString, Optional: true, @@ -247,6 +342,35 @@ func schemaPolicyoptionsPolicyStatementFrom() map[string]*schema.Schema { }, }, }, + "route_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"external", "internal"}, false), + }, + "srte_color": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "state": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"active", "inactive"}, false), + }, + "tunnel_type": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"gre", "ipip", "udp"}, false), + }, + }, + "validation_database": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"invalid", "unknown", "valid"}, false), + }, } } @@ -955,6 +1079,17 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) ( for _, v := range sortSetOfString(opts["bgp_as_path"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"as-path "+v) } + bgpASPathCalcLengthList := make([]int, 0) + for _, block := range opts["bgp_as_path_calc_length"].(*schema.Set).List() { + bgpASPathCalcLength := block.(map[string]interface{}) + count := bgpASPathCalcLength["count"].(int) + if bchk.IntInSlice(count, bgpASPathCalcLengthList) { + return configSet, fmt.Errorf("multiple blocks bgp_as_path_calc_length with the same count %d", count) + } + bgpASPathCalcLengthList = append(bgpASPathCalcLengthList, count) + configSet = append(configSet, + setPrefixFrom+"as-path-calc-length "+strconv.Itoa(count)+" "+bgpASPathCalcLength["match"].(string)) + } for _, v := range sortSetOfString(opts["bgp_as_path_group"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"as-path-group "+v) } @@ -972,9 +1107,35 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) ( for _, v := range sortSetOfString(opts["bgp_community"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"community "+v) } + bgpCommunityCountList := make([]int, 0) + for _, block := range opts["bgp_community_count"].(*schema.Set).List() { + bgpCommunityCount := block.(map[string]interface{}) + count := bgpCommunityCount["count"].(int) + if bchk.IntInSlice(count, bgpCommunityCountList) { + return configSet, fmt.Errorf("multiple blocks bgp_community_count with the same count %d", count) + } + bgpCommunityCountList = append(bgpCommunityCountList, count) + configSet = append(configSet, + setPrefixFrom+"community-count "+strconv.Itoa(count)+" "+bgpCommunityCount["match"].(string)) + } if opts["bgp_origin"].(string) != "" { configSet = append(configSet, setPrefixFrom+"origin "+opts["bgp_origin"].(string)) } + if v := opts["bgp_srte_discriminator"].(int); v != -1 { + configSet = append(configSet, setPrefixFrom+"bgp-srte-discriminator "+strconv.Itoa(v)) + } + if v := opts["color"].(int); v != -1 { + configSet = append(configSet, setPrefixFrom+"color "+strconv.Itoa(v)) + } + for _, v := range sortSetOfString(opts["evpn_esi"].(*schema.Set).List()) { + configSet = append(configSet, setPrefixFrom+"evpn-esi "+v) + } + if v := opts["evpn_mac_route"].(string); v != "" { + configSet = append(configSet, setPrefixFrom+"evpn-mac-route "+v) + } + for _, v := range sortSetOfNumberToString(opts["evpn_tag"].(*schema.Set).List()) { + configSet = append(configSet, setPrefixFrom+"evpn-tag "+v) + } if opts["family"].(string) != "" { configSet = append(configSet, setPrefixFrom+"family "+opts["family"].(string)) } @@ -996,6 +1157,14 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) ( for _, v := range sortSetOfString(opts["next_hop"].(*schema.Set).List()) { configSet = append(configSet, setPrefixFrom+"next-hop "+v) } + if opts["next_hop_type_merged"].(bool) { + configSet = append(configSet, setPrefixFrom+"next-hop-type merged") + } + for _, block := range opts["next_hop_weight"].(*schema.Set).List() { + nextHopWeight := block.(map[string]interface{}) + configSet = append(configSet, + setPrefixFrom+"nexthop-weight "+nextHopWeight["match"].(string)+" "+strconv.Itoa(nextHopWeight["weight"].(int))) + } if opts["ospf_area"].(string) != "" { configSet = append(configSet, setPrefixFrom+"area "+opts["ospf_area"].(string)) } @@ -1020,6 +1189,21 @@ func setPolicyStatementOptsFrom(setPrefix string, opts map[string]interface{}) ( } configSet = append(configSet, setRoutFilter) } + if v := opts["route_type"].(string); v != "" { + configSet = append(configSet, setPrefixFrom+"route-type "+v) + } + if v := opts["srte_color"].(int); v != -1 { + configSet = append(configSet, setPrefixFrom+"srte-color "+strconv.Itoa(v)) + } + if v := opts["state"].(string); v != "" { + configSet = append(configSet, setPrefixFrom+"state "+v) + } + for _, v := range sortSetOfString(opts["tunnel_type"].(*schema.Set).List()) { + configSet = append(configSet, setPrefixFrom+"tunnel-type "+v) + } + if v := opts["validation_database"].(string); v != "" { + configSet = append(configSet, setPrefixFrom+"validation-database "+v) + } return configSet, nil } @@ -1163,6 +1347,19 @@ func readPolicyStatementOptsFrom(item string, fromMap map[string]interface{}) er fromMap["aggregate_contributor"] = true case strings.HasPrefix(item, "as-path "): fromMap["bgp_as_path"] = append(fromMap["bgp_as_path"].([]string), strings.TrimPrefix(item, "as-path ")) + case strings.HasPrefix(item, "as-path-calc-length "): + countStr := strings.Split(strings.TrimPrefix(item, "as-path-calc-length "), " ")[0] + count, err := strconv.Atoi(countStr) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + fromMap["bgp_as_path_calc_length"] = append( + fromMap["bgp_as_path_calc_length"].([]map[string]interface{}), + map[string]interface{}{ + "count": count, + "match": strings.TrimPrefix(item, "as-path-calc-length "+countStr+" "), + }, + ) case strings.HasPrefix(item, "as-path-group "): fromMap["bgp_as_path_group"] = append(fromMap["bgp_as_path_group"].([]string), strings.TrimPrefix(item, "as-path-group ")) @@ -1181,8 +1378,43 @@ func readPolicyStatementOptsFrom(item string, fromMap map[string]interface{}) er ) case strings.HasPrefix(item, "community "): fromMap["bgp_community"] = append(fromMap["bgp_community"].([]string), strings.TrimPrefix(item, "community ")) + case strings.HasPrefix(item, "community-count "): + countStr := strings.Split(strings.TrimPrefix(item, "community-count "), " ")[0] + count, err := strconv.Atoi(countStr) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + fromMap["bgp_community_count"] = append( + fromMap["bgp_community_count"].([]map[string]interface{}), + map[string]interface{}{ + "count": count, + "match": strings.TrimPrefix(item, "community-count "+countStr+" "), + }, + ) case strings.HasPrefix(item, "origin "): fromMap["bgp_origin"] = strings.TrimPrefix(item, "origin ") + case strings.HasPrefix(item, "bgp-srte-discriminator "): + var err error + fromMap["bgp_srte_discriminator"], err = strconv.Atoi(strings.TrimPrefix(item, "bgp-srte-discriminator ")) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + case strings.HasPrefix(item, "color "): + var err error + fromMap["color"], err = strconv.Atoi(strings.TrimPrefix(item, "color ")) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + case strings.HasPrefix(item, "evpn-esi "): + fromMap["evpn_esi"] = append(fromMap["evpn_esi"].([]string), strings.TrimPrefix(item, "evpn-esi ")) + case strings.HasPrefix(item, "evpn-mac-route "): + fromMap["evpn_mac_route"] = strings.TrimPrefix(item, "evpn-mac-route ") + case strings.HasPrefix(item, "evpn-tag "): + tag, err := strconv.Atoi(strings.TrimPrefix(item, "evpn-tag ")) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + fromMap["evpn_tag"] = append(fromMap["evpn_tag"].([]int), tag) case strings.HasPrefix(item, "family "): fromMap["family"] = strings.TrimPrefix(item, "family ") case strings.HasPrefix(item, "local-preference "): @@ -1205,6 +1437,24 @@ func readPolicyStatementOptsFrom(item string, fromMap map[string]interface{}) er fromMap["neighbor"] = append(fromMap["neighbor"].([]string), strings.TrimPrefix(item, "neighbor ")) case strings.HasPrefix(item, "next-hop "): fromMap["next_hop"] = append(fromMap["next_hop"].([]string), strings.TrimPrefix(item, "next-hop ")) + case item == "next-hop-type merged": + fromMap["next_hop_type_merged"] = true + case strings.HasPrefix(item, "nexthop-weight "): + itemSplit := strings.Split(strings.TrimPrefix(item, "nexthop-weight "), " ") + if len(itemSplit) < 2 { + return fmt.Errorf("can't read value for weight in %s", item) + } + weight, err := strconv.Atoi(itemSplit[1]) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + fromMap["next_hop_weight"] = append( + fromMap["next_hop_weight"].([]map[string]interface{}), + map[string]interface{}{ + "match": itemSplit[0], + "weight": weight, + }, + ) case strings.HasPrefix(item, "area "): fromMap["ospf_area"] = strings.TrimPrefix(item, "area ") case strings.HasPrefix(item, "policy "): @@ -1232,6 +1482,20 @@ func readPolicyStatementOptsFrom(item string, fromMap map[string]interface{}) er routeFilterMap["option_value"] = itemSplit[3] } fromMap["route_filter"] = append(fromMap["route_filter"].([]map[string]interface{}), routeFilterMap) + case strings.HasPrefix(item, "route-type "): + fromMap["route_type"] = strings.TrimPrefix(item, "route-type ") + case strings.HasPrefix(item, "srte-color "): + var err error + fromMap["srte_color"], err = strconv.Atoi(strings.TrimPrefix(item, "srte-color ")) + if err != nil { + return fmt.Errorf(failedConvAtoiError, item, err) + } + case strings.HasPrefix(item, "state "): + fromMap["state"] = strings.TrimPrefix(item, "state ") + case strings.HasPrefix(item, "tunnel-type "): + fromMap["tunnel_type"] = append(fromMap["tunnel_type"].([]string), strings.TrimPrefix(item, "tunnel-type ")) + case strings.HasPrefix(item, "validation-database "): + fromMap["validation_database"] = strings.TrimPrefix(item, "validation-database ") } return nil @@ -1387,10 +1651,17 @@ func genMapPolicyStatementOptsFrom() map[string]interface{} { return map[string]interface{}{ "aggregate_contributor": false, "bgp_as_path": make([]string, 0), + "bgp_as_path_calc_length": make([]map[string]interface{}, 0), "bgp_as_path_group": make([]string, 0), "bgp_as_path_unique_count": make([]map[string]interface{}, 0), "bgp_community": make([]string, 0), + "bgp_community_count": make([]map[string]interface{}, 0), "bgp_origin": "", + "bgp_srte_discriminator": -1, + "color": -1, + "evpn_esi": make([]string, 0), + "evpn_mac_route": "", + "evpn_tag": make([]int, 0), "family": "", "local_preference": 0, "routing_instance": "", @@ -1398,12 +1669,19 @@ func genMapPolicyStatementOptsFrom() map[string]interface{} { "metric": 0, "neighbor": make([]string, 0), "next_hop": make([]string, 0), + "next_hop_type_merged": false, + "next_hop_weight": make([]map[string]interface{}, 0), "ospf_area": "", "policy": make([]string, 0), "preference": 0, "prefix_list": make([]string, 0), "protocol": make([]string, 0), "route_filter": make([]map[string]interface{}, 0), + "route_type": "", + "srte_color": -1, + "state": "", + "tunnel_type": make([]string, 0), + "validation_database": "", } } diff --git a/junos/resource_policyoptions_test.go b/junos/resource_policyoptions_test.go index b8c9fbee..29c29a63 100644 --- a/junos/resource_policyoptions_test.go +++ b/junos/resource_policyoptions_test.go @@ -506,19 +506,28 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { from { aggregate_contributor = true bgp_as_path = [junos_policyoptions_as_path.testacc_policyOptions.name] - bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] - bgp_origin = "igp" - family = "inet" - local_preference = 100 - routing_instance = junos_routing_instance.testacc_policyOptions.name - interface = ["st0.0"] - metric = 5 - neighbor = ["192.0.2.4"] - next_hop = ["192.0.2.4"] - ospf_area = "0.0.0.0" - preference = 100 - prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name] - protocol = ["bgp"] + bgp_as_path_calc_length { + count = 4 + match = "orhigher" + } + bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] + bgp_community_count { + count = 6 + match = "orhigher" + } + bgp_origin = "igp" + color = 31 + family = "inet" + local_preference = 100 + routing_instance = junos_routing_instance.testacc_policyOptions.name + interface = ["st0.0"] + metric = 5 + neighbor = ["192.0.2.4"] + next_hop = ["192.0.2.4"] + ospf_area = "0.0.0.0" + preference = 100 + prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name] + protocol = ["bgp"] route_filter { route = "192.0.2.0/25" option = "exact" @@ -750,6 +759,14 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { from { aggregate_contributor = true bgp_as_path = [junos_policyoptions_as_path.testacc_policyOptions.name] + bgp_as_path_calc_length { + count = 4 + match = "orhigher" + } + bgp_as_path_calc_length { + count = 3 + match = "equal" + } bgp_as_path_unique_count { count = 3 match = "equal" @@ -758,17 +775,39 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { count = 2 match = "orhigher" } - bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] - bgp_origin = "igp" - family = "inet" - local_preference = 100 - routing_instance = junos_routing_instance.testacc_policyOptions.name - interface = ["st0.0"] - metric = 5 - neighbor = ["192.0.2.4"] - next_hop = ["192.0.2.4"] - ospf_area = "0.0.0.0" - preference = 100 + bgp_community = [junos_policyoptions_community.testacc_policyOptions.name] + bgp_community_count { + count = 6 + match = "orhigher" + } + bgp_community_count { + count = 5 + match = "equal" + } + bgp_origin = "igp" + bgp_srte_discriminator = 30 + + evpn_esi = ["00:11:11:11:11:11:11:11:11:33", "00:11:11:11:11:11:11:11:11:32"] + evpn_mac_route = "mac-only" + evpn_tag = [36, 35, 33] + family = "evpn" + local_preference = 100 + routing_instance = junos_routing_instance.testacc_policyOptions.name + interface = ["st0.0"] + metric = 5 + neighbor = ["192.0.2.4"] + next_hop = ["192.0.2.4"] + next_hop_type_merged = true + next_hop_weight { + match = "greater-than-equal" + weight = 500 + } + next_hop_weight { + match = "equal" + weight = 200 + } + ospf_area = "0.0.0.0" + preference = 100 prefix_list = [junos_policyoptions_prefix_list.testacc_policyOptions.name, junos_policyoptions_prefix_list.testacc_policyOptions2.name, ] @@ -777,6 +816,11 @@ resource "junos_policyoptions_policy_statement" "testacc_policyOptions" { route = "192.0.2.0/25" option = "exact" } + route_type = "internal" + srte_color = 39 + state = "active" + tunnel_type = ["ipip"] + validation_database = "valid" } to { bgp_as_path = [junos_policyoptions_as_path.testacc_policyOptions.name] From 27dd8a8c14af5512175aa50d17ef61fafc0226ac Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 12 Oct 2022 09:18:38 +0200 Subject: [PATCH 17/17] Release v1.31.0 --- .github/workflows/releases.yml | 2 +- CHANGELOG.md | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index c898d294..f7b274c1 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -43,7 +43,7 @@ jobs: - name: Set up Go 1.19 uses: actions/setup-go@v3 with: - go-version: ^1.19.1 + go-version: ^1.19.2 check-latest: true id: go - name: Show version diff --git a/CHANGELOG.md b/CHANGELOG.md index ed623bb8..5b504d6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,21 @@ FEATURES: -* add `junos_forwardingoptions_dhcprelay` resource (Fixes #423) -* add `junos_forwardingoptions_dhcprelay_group` resource (Fixes #423) -* add `junos_forwardingoptions_dhcprelay_servergroup` resource (Fixes #423) +ENHANCEMENTS: + +BUG FIXES: + +## 1.31.0 (October 12, 2022) + +FEATURES: + +* add `junos_forwardingoptions_dhcprelay` resource (Fixes [#423](https://github.com/jeremmfr/terraform-provider-junos/issues/423)) +* add `junos_forwardingoptions_dhcprelay_group` resource (Fixes [#423](https://github.com/jeremmfr/terraform-provider-junos/issues/423)) +* add `junos_forwardingoptions_dhcprelay_servergroup` resource (Fixes [#423](https://github.com/jeremmfr/terraform-provider-junos/issues/423)) ENHANCEMENTS: -* resource/`junos_policyoptions_policy_statement`: add `bgp_as_path_calc_length`, `bgp_as_path_unique_count`, `bgp_community_count`, `bgp_srte_discriminator`, `color`, `evpn_esi`, `evpn_mac_route`, `evpn_tag`, `next_hop_type_merged`, `next_hop_weight`, `route_type`, `srte_color`, `state`, `tunnel_type` and `validation_database` arguments inside `from` block arguments (Fixes #424) +* resource/`junos_policyoptions_policy_statement`: add `bgp_as_path_calc_length`, `bgp_as_path_unique_count`, `bgp_community_count`, `bgp_srte_discriminator`, `color`, `evpn_esi`, `evpn_mac_route`, `evpn_tag`, `next_hop_type_merged`, `next_hop_weight`, `route_type`, `srte_color`, `state`, `tunnel_type` and `validation_database` arguments inside `from` block arguments (Fixes [#424](https://github.com/jeremmfr/terraform-provider-junos/issues/424)) * resource/`junos_system_services_dhcp_localserver_group`: add `client_id_exclude_headers` and `client_id_use_automatic_ascii_hex_encoding` argument and remove need to `version` = `v6` when set `client_id` = `true` inside `authentication_username_include` block argument BUG FIXES: