Skip to content

Commit

Permalink
add junos_multichassis resource
Browse files Browse the repository at this point in the history
Partial fix #576
  • Loading branch information
jeremmfr committed Dec 11, 2023
1 parent e9dfa59 commit aac642d
Show file tree
Hide file tree
Showing 8 changed files with 432 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .changes/issue-576.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- markdownlint-disable-file MD013 MD041 -->
FEATURES:

* add `junos_multichassis` resource (Partial fix [#576](https://github.com/jeremmfr/terraform-provider-junos/issues/576))
47 changes: 47 additions & 0 deletions docs/resources/multichassis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
page_title: "Junos: junos_multichassis"
---

# junos_multichassis

-> **Note:** This resource should only be created **once**.
It's used to configure static (not object) options in `multi-chassis` block.
By default (without `clean_on_destroy` = true), destroy this resource has no effect on the Junos configuration.

Configure static configuration in `multi-chassis` block.

## Example Usage

```hcl
# Configure multi-chassis
resource "junos_multichassis" "multichassis" {
mc_lag_consistency_check = true
}
```

## Argument Reference

The following arguments are supported:

- **clean_on_destroy** (Optional, Boolean)
Clean entirely `multi-chassis` block when destroy this resource.
- **mc_lag_consistency_check** (Optional, Computed, Boolean)
Consistency Check.
Computed to set to `true` when `mc_lag_consistency_check_comparison_delay_time` is specified.
- **mc_lag_consistency_check_comparison_delay_time** (Optional, Number)
Time after which local and remote config are compared (5..600 seconds).

## Attributes Reference

The following attributes are exported:

- **id** (String)
An identifier for the resource with value `multichassis`.

## Import

Junos multi-chassis can be imported using any id, e.g.

```shell
$ terraform import junos_multichassis.multichassis random
```
1 change: 1 addition & 0 deletions internal/providerfwk/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource
newInterfacePhysicalDisableResource,
newInterfacePhysicalResource,
newInterfaceSt0UnitResource,
newMultichassisResource,
newOamGretunnelInterfaceResource,
newPolicyoptionsASPathResource,
newPolicyoptionsASPathGroupResource,
Expand Down
338 changes: 338 additions & 0 deletions internal/providerfwk/resource_multichassis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
package providerfwk

import (
"context"
"strings"

"github.com/jeremmfr/terraform-provider-junos/internal/junos"
"github.com/jeremmfr/terraform-provider-junos/internal/tfdata"
"github.com/jeremmfr/terraform-provider-junos/internal/tfvalidator"
"github.com/jeremmfr/terraform-provider-junos/internal/utils"

"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
balt "github.com/jeremmfr/go-utils/basicalter"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &multichassis{}
_ resource.ResourceWithConfigure = &multichassis{}
_ resource.ResourceWithModifyPlan = &multichassis{}
_ resource.ResourceWithImportState = &multichassis{}
)

type multichassis struct {
client *junos.Client
}

func newMultichassisResource() resource.Resource {
return &multichassis{}
}

func (rsc *multichassis) typeName() string {
return providerName + "_multichassis"
}

func (rsc *multichassis) junosName() string {
return "multi-chassis"
}

func (rsc *multichassis) junosClient() *junos.Client {
return rsc.client
}

func (rsc *multichassis) Metadata(
_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse,
) {
resp.TypeName = rsc.typeName()
}

func (rsc *multichassis) Configure(
ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse,
) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*junos.Client)
if !ok {
unexpectedResourceConfigureType(ctx, req, resp)

return
}
rsc.client = client
}

func (rsc *multichassis) Schema(
_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse,
) {
resp.Schema = schema.Schema{
Description: "Configure static configuration in `" + rsc.junosName() + "` block",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "An identifier for the resource with value " +
"`multichassis`.",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"clean_on_destroy": schema.BoolAttribute{
Optional: true,
Description: "Clean entirely `" + rsc.junosName() + "` block when destroy this resource.",
},
"mc_lag_consistency_check": schema.BoolAttribute{
Optional: true,
Computed: true,
Description: "Consistency Check.",
Validators: []validator.Bool{
tfvalidator.BoolTrue(),
},
},
"mc_lag_consistency_check_comparison_delay_time": schema.Int64Attribute{
Optional: true,
Description: "Time after which local and remote config are compared (seconds).",
Validators: []validator.Int64{
int64validator.Between(5, 600),
},
},
},
}
}

type multichassisData struct {
CleanOnDestroy types.Bool `tfsdk:"clean_on_destroy"`
MCLagConsistencyCheck types.Bool `tfsdk:"mc_lag_consistency_check"`
ID types.String `tfsdk:"id"`
MCLagConsistencyCheckComparaisonDelayTime types.Int64 `tfsdk:"mc_lag_consistency_check_comparison_delay_time"`
}

func (rsc *multichassis) ModifyPlan(
ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse,
) {
if req.Plan.Raw.IsNull() {
return
}

var config, plan multichassisData
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

if config.MCLagConsistencyCheck.IsNull() {
if config.MCLagConsistencyCheckComparaisonDelayTime.IsNull() {
plan.MCLagConsistencyCheck = types.BoolNull()
} else if !plan.MCLagConsistencyCheckComparaisonDelayTime.IsNull() &&
!plan.MCLagConsistencyCheckComparaisonDelayTime.IsUnknown() {
plan.MCLagConsistencyCheck = types.BoolValue(true)
}
}

resp.Diagnostics.Append(resp.Plan.Set(ctx, &plan)...)
}

func (rsc *multichassis) Create(
ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse,
) {
var plan multichassisData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}

if plan.MCLagConsistencyCheck.IsUnknown() {
plan.MCLagConsistencyCheck = types.BoolNull()
if !plan.MCLagConsistencyCheckComparaisonDelayTime.IsNull() {
plan.MCLagConsistencyCheck = types.BoolValue(true)
}
}

defaultResourceCreate(
ctx,
rsc,
nil,
nil,
&plan,
resp,
)
}

func (rsc *multichassis) Read(
ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse,
) {
var state, data multichassisData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

var _ resourceDataReadFrom0String = &data
defaultResourceRead(
ctx,
rsc,
nil,
&data,
func() {
data.CleanOnDestroy = state.CleanOnDestroy
},
resp,
)
}

func (rsc *multichassis) Update(
ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse,
) {
var plan, state multichassisData
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

if plan.MCLagConsistencyCheck.IsUnknown() {
plan.MCLagConsistencyCheck = types.BoolNull()
if !plan.MCLagConsistencyCheckComparaisonDelayTime.IsNull() {
plan.MCLagConsistencyCheck = types.BoolValue(true)
}
}

var _ resourceDataDelWithOpts = &state
defaultResourceUpdate(
ctx,
rsc,
&state,
&plan,
resp,
)
}

func (rsc *multichassis) Delete(
ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse,
) {
var state multichassisData
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

if state.CleanOnDestroy.ValueBool() {
defaultResourceDelete(
ctx,
rsc,
&state,
resp,
)
}
}

func (rsc *multichassis) ImportState(
ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse,
) {
var data multichassisData

var _ resourceDataReadFrom0String = &data
defaultResourceImportState(
ctx,
rsc,
&data,
req,
resp,
"the `"+rsc.junosName()+"` block is not configured on device",
)
}

func (rscData *multichassisData) fillID() {
rscData.ID = types.StringValue("multichassis")
}

func (rscData *multichassisData) nullID() bool {
return rscData.ID.IsNull()
}

func (rscData *multichassisData) set(
_ context.Context, junSess *junos.Session,
) (
path.Path, error,
) {
setPrefix := "set multi-chassis "
configSet := []string{
setPrefix,
}

if rscData.MCLagConsistencyCheck.ValueBool() {
configSet = append(configSet, setPrefix+"mc-lag consistency-check")
}
if !rscData.MCLagConsistencyCheckComparaisonDelayTime.IsNull() {
configSet = append(configSet, setPrefix+"mc-lag consistency-check comparison-delay-time "+
utils.ConvI64toa(rscData.MCLagConsistencyCheckComparaisonDelayTime.ValueInt64()))
}

return path.Empty(), junSess.ConfigSet(configSet)
}

func (rscData *multichassisData) read(
_ context.Context, junSess *junos.Session,
) (
err error,
) {
showConfig, err := junSess.Command(junos.CmdShowConfig +
"multi-chassis" + junos.PipeDisplaySetRelative)
if err != nil {
return err
}

if showConfig != junos.EmptyW {
rscData.fillID()
for _, item := range strings.Split(showConfig, "\n") {
if strings.Contains(item, junos.XMLStartTagConfigOut) {
continue
}
if strings.Contains(item, junos.XMLEndTagConfigOut) {
break
}
itemTrim := strings.TrimPrefix(item, junos.SetLS)
if balt.CutPrefixInString(&itemTrim, "mc-lag consistency-check") {
rscData.MCLagConsistencyCheck = types.BoolValue(true)
if balt.CutPrefixInString(&itemTrim, " comparison-delay-time ") {
rscData.MCLagConsistencyCheckComparaisonDelayTime, err = tfdata.ConvAtoi64Value(itemTrim)
if err != nil {
return err
}
}
}
}
}

return nil
}

func (rscData *multichassisData) delOpts(
_ context.Context, junSess *junos.Session,
) error {
delLine := "delete multi-chassis "

configSet := []string{
delLine + "mc-lag",
}

return junSess.ConfigSet(configSet)
}

func (rscData *multichassisData) del(
_ context.Context, junSess *junos.Session,
) error {
configSet := []string{
"delete multi-chassis",
}

return junSess.ConfigSet(configSet)
}
Loading

0 comments on commit aac642d

Please sign in to comment.