From 3a02e46f5b9c1d0a5c8adc21b3bef6f7c9bb21b6 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 6 May 2024 08:45:58 +0200 Subject: [PATCH 01/30] tests: bump golangci-lint to v1.58.0 - re-order disable linters (deprecated section with version order, alphabetical order for unwanted) - gci: use localModule - gofumpt: remove unnecessary module-path --- .github/workflows/linters.yml | 2 +- .golangci.yml | 56 +++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 3db9705a..4c09e0ff 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -19,7 +19,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v5 with: - version: 'v1.57.1' + version: 'v1.58.0' args: -c .golangci.yml -v markdown-lint: diff --git a/.golangci.yml b/.golangci.yml index 08d126ad..7b396e58 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,44 +1,49 @@ run: timeout: 5m + linters: enable-all: true disable: - - maligned # deprecated 1.38 - - interfacer # deprecated 1.38 - - scopelint # deprecated 1.39 - - golint # deprecated 1.41 - - exhaustivestruct # deprecated 1.46 - - ifshort # deprecated 1.48 - - deadcode # deprecated 1.49 - - structcheck # deprecated 1.49 - - varcheck # deprecated 1.49 - - funlen + # deprecated + - interfacer # deprecated 1.38.0 + - maligned # deprecated 1.38.0 + - scopelint # deprecated 1.39.0 + - golint # deprecated 1.41.0 + - exhaustivestruct # deprecated 1.46.0 + - ifshort # deprecated 1.48.0 + - deadcode # deprecated 1.49.0 + - structcheck # deprecated 1.49.0 + - varcheck # deprecated 1.49.0 + - execinquery # deprecated 1.58.0 + - gomnd # deprecated 1.58.0 + # unwanted + - cyclop + - depguard - dupl - - wsl - - gomnd - - goerr113 - - nestif + - err113 - exhaustruct - - paralleltest - - cyclop - forcetypeassert + - funlen - gomoddirectives - - varnamelen - - nonamedreturns - - maintidx - - execinquery - - nosnakecase - - musttag - - depguard - gosec - inamedparam - ireturn + - maintidx + - mnd + - musttag + - nestif + - nonamedreturns + - nosnakecase + - paralleltest + - varnamelen + - wsl + linters-settings: gci: custom-order: true sections: - standard - - prefix(github.com/jeremmfr/terraform-provider-junos/) + - localModule - default gocognit: # minimal code complexity to report, 30 by default @@ -52,8 +57,6 @@ linters-settings: # minimal code complexity to report, 30 by default min-complexity: 100 gofumpt: - module-path: github.com/jeremmfr/terraform-provider-junos - # Choose whether to use the extra rules. extra-rules: true govet: enable-all: true @@ -91,6 +94,7 @@ linters-settings: - name: import-alias-naming - name: import-shadowing - name: unhandled-error + issues: exclude-rules: - text: "github.com/jeremmfr/terraform-provider-junos/internal" From e7347d6f5205559ee6237fab3479146bf2128e52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 00:19:36 +0000 Subject: [PATCH 02/30] deps: bump golang.org/x/crypto from 0.22.0 to 0.23.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.23.0. - [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index b82614dc..ed9d6790 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/jeremmfr/go-netconf v0.5.0 github.com/jeremmfr/go-utils v0.12.0 github.com/jeremmfr/junosdecode v1.1.1 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 ) require ( @@ -55,8 +55,8 @@ require ( github.com/zclconf/go-cty v1.14.3 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.13.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index b056c9db..b0d6787e 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/zclconf/go-cty v1.14.3 h1:1JXy1XroaGrzZuG6X9dt7HL6s9AwbY+l4UNL8o5B6ho github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -185,19 +185,19 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From ff64881b44bef9ce9795ee91b37440f4073d8c3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 00:19:42 +0000 Subject: [PATCH 03/30] deps: bump github.com/hashicorp/terraform-plugin-go Bumps [github.com/hashicorp/terraform-plugin-go](https://github.com/hashicorp/terraform-plugin-go) from 0.22.2 to 0.23.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-go/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-go/compare/v0.22.2...v0.23.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b82614dc..f0dec9eb 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-framework v1.8.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 - github.com/hashicorp/terraform-plugin-go v0.22.2 + github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-mux v0.15.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-plugin-testing v1.7.0 @@ -61,7 +61,7 @@ require ( google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.0 // indirect ) replace github.com/hashicorp/terraform-plugin-sdk/v2 => github.com/jeremmfr/terraform-plugin-sdk/v2 v2.33.1-0.20240302165942-47aab524cbd3 diff --git a/go.sum b/go.sum index b056c9db..b36cb24a 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= -github.com/hashicorp/terraform-plugin-go v0.22.2 h1:5o8uveu6eZUf5J7xGPV0eY0TPXg3qpmwX9sce03Bxnc= -github.com/hashicorp/terraform-plugin-go v0.22.2/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= +github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= +github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= @@ -214,8 +214,8 @@ google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From fcc6a3dc94937562fe753d64aafadd2768a5d965 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 00:40:01 +0000 Subject: [PATCH 04/30] workflows: bump golangci/golangci-lint-action from 5 to 6 Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 5 to 6. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v5...v6) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/linters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 4c09e0ff..5f6c04e9 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -17,7 +17,7 @@ jobs: - name: Check out code uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v5 + uses: golangci/golangci-lint-action@v6 with: version: 'v1.58.0' args: -c .golangci.yml -v From d2db31d0b7a989c135f03dbce371d33234b8467b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 07:06:55 +0000 Subject: [PATCH 05/30] deps: bump github.com/hashicorp/terraform-plugin-mux Bumps [github.com/hashicorp/terraform-plugin-mux](https://github.com/hashicorp/terraform-plugin-mux) from 0.15.0 to 0.16.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-mux/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-mux/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-mux/compare/v0.15.0...v0.16.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-mux dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c69309ba..2bbeea85 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/hashicorp/terraform-plugin-framework v1.8.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.23.0 - github.com/hashicorp/terraform-plugin-mux v0.15.0 + github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-plugin-testing v1.7.0 github.com/jeremmfr/go-netconf v0.5.0 diff --git a/go.sum b/go.sum index 95515662..e8446b0b 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/12 github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= -github.com/hashicorp/terraform-plugin-mux v0.15.0/go.mod h1:9ezplb1Dyq394zQ+ldB0nvy/qbNAz3mMoHHseMTMaKo= +github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= +github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= github.com/hashicorp/terraform-plugin-testing v1.7.0/go.mod h1:sbAreCleJNOCz+y5vVHV8EJkIWZKi/t4ndKiUjM9vao= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= From 9fe1490faf4acce92656feffb10c23ff21f79dd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 01:10:55 +0000 Subject: [PATCH 06/30] deps: bump github.com/hashicorp/terraform-plugin-testing Bumps [github.com/hashicorp/terraform-plugin-testing](https://github.com/hashicorp/terraform-plugin-testing) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-testing/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-testing/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-testing/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-testing dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 18 +++++++++--------- go.sum | 52 ++++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 2bbeea85..ef45f698 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 - github.com/hashicorp/terraform-plugin-testing v1.7.0 + github.com/hashicorp/terraform-plugin-testing v1.8.0 github.com/jeremmfr/go-netconf v0.5.0 github.com/jeremmfr/go-utils v0.12.0 github.com/jeremmfr/junosdecode v1.1.1 @@ -18,7 +18,7 @@ require ( ) require ( - github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect + github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect @@ -27,16 +27,16 @@ require ( 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 - github.com/hashicorp/go-hclog v1.6.2 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.6.3 // indirect - github.com/hashicorp/hcl/v2 v2.20.0 // indirect + github.com/hashicorp/hc-install v0.6.4 // indirect + github.com/hashicorp/hcl/v2 v2.20.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.20.0 // indirect - github.com/hashicorp/terraform-json v0.21.0 // indirect + github.com/hashicorp/terraform-exec v0.21.0 // indirect + github.com/hashicorp/terraform-json v0.22.1 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect @@ -52,8 +52,8 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.14.3 // indirect - golang.org/x/mod v0.15.0 // indirect + github.com/zclconf/go-cty v1.14.4 // indirect + golang.org/x/mod v0.16.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect diff --git a/go.sum b/go.sum index e8446b0b..dd7060de 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= @@ -27,8 +27,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -51,8 +51,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= -github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= @@ -62,16 +62,16 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.6.3 h1:yE/r1yJvWbtrJ0STwScgEnCanb0U9v7zp0Gbkmcoxqs= -github.com/hashicorp/hc-install v0.6.3/go.mod h1:KamGdbodYzlufbWh4r9NRo8y6GLHWZP2GBtdnms1Ln0= -github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4= -github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk= +github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0= +github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA= +github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc= +github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4= 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.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= -github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= -github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= -github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= +github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= +github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec= +github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer5AEvJnDuXomBgou6fD8kI= github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= @@ -82,8 +82,8 @@ github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9T github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= -github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= -github.com/hashicorp/terraform-plugin-testing v1.7.0/go.mod h1:sbAreCleJNOCz+y5vVHV8EJkIWZKi/t4ndKiUjM9vao= +github.com/hashicorp/terraform-plugin-testing v1.8.0 h1:wdYIgwDk4iO933gC4S8KbKdnMQShu6BXuZQPScmHvpk= +github.com/hashicorp/terraform-plugin-testing v1.8.0/go.mod h1:o2kOgf18ADUaZGhtOl0YCkfIxg01MAiMATT2EtIHlZk= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -109,8 +109,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -136,10 +134,10 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -153,15 +151,17 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.14.3 h1:1JXy1XroaGrzZuG6X9dt7HL6s9AwbY+l4UNL8o5B6ho= -github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= +github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= From 6d7877330d2ba89ee9f8980dc7d12793f74a1870 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 23 May 2024 08:45:15 +0200 Subject: [PATCH 07/30] tests: bump golangci-lint to v1.58.2 --- .github/workflows/linters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 5f6c04e9..d6d6b747 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -19,7 +19,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: 'v1.58.0' + version: v1.58.2 args: -c .golangci.yml -v markdown-lint: From c2ee2e6949fa7ceadc10f485f50d45fc9c63b8c0 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 27 May 2024 09:31:56 +0200 Subject: [PATCH 08/30] tests: bump golangci-lint to v1.59.0 --- .github/workflows/linters.yml | 2 +- .golangci.yml | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index d6d6b747..c1cd7468 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -19,7 +19,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.58.2 + version: v1.59.0 args: -c .golangci.yml -v markdown-lint: diff --git a/.golangci.yml b/.golangci.yml index 7b396e58..73509739 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,15 +5,6 @@ linters: enable-all: true disable: # deprecated - - interfacer # deprecated 1.38.0 - - maligned # deprecated 1.38.0 - - scopelint # deprecated 1.39.0 - - golint # deprecated 1.41.0 - - exhaustivestruct # deprecated 1.46.0 - - ifshort # deprecated 1.48.0 - - deadcode # deprecated 1.49.0 - - structcheck # deprecated 1.49.0 - - varcheck # deprecated 1.49.0 - execinquery # deprecated 1.58.0 - gomnd # deprecated 1.58.0 # unwanted @@ -33,7 +24,6 @@ linters: - musttag - nestif - nonamedreturns - - nosnakecase - paralleltest - varnamelen - wsl From 7b308fdd7f25cc59d26e672cb993e57d14fcf472 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 28 May 2024 08:55:03 +0200 Subject: [PATCH 09/30] r/rstp: use new provider via framework --- .changes/rstp-with-fwk.md | 6 + docs/resources/rstp.md | 4 +- internal/providerfwk/provider.go | 1 + internal/providerfwk/resource_rstp.go | 745 ++++++++++++++++++ internal/providerfwk/resource_rstp_test.go | 56 ++ .../TestAccResourceRstp_basic/1/main.tf | 3 + .../TestAccResourceRstp_basic/2/main.tf | 7 + .../TestAccResourceRstp_basic/3/main.tf | 20 + .../TestAccResourceRstp_switch/1/main.tf | 16 + .../TestAccResourceRstp_switch/2/main.tf | 32 + internal/providersdk/provider.go | 1 - internal/providersdk/resource_rstp.go | 586 -------------- internal/providersdk/resource_rstp_test.go | 150 ---- 13 files changed, 888 insertions(+), 739 deletions(-) create mode 100644 .changes/rstp-with-fwk.md create mode 100644 internal/providerfwk/resource_rstp.go create mode 100644 internal/providerfwk/resource_rstp_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceRstp_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstp_basic/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstp_basic/3/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstp_switch/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstp_switch/2/main.tf delete mode 100644 internal/providersdk/resource_rstp.go delete mode 100644 internal/providersdk/resource_rstp_test.go diff --git a/.changes/rstp-with-fwk.md b/.changes/rstp-with-fwk.md new file mode 100644 index 00000000..a828128e --- /dev/null +++ b/.changes/rstp-with-fwk.md @@ -0,0 +1,6 @@ + +ENHANCEMENTS: + +* **resource/junos_rstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* diff --git a/docs/resources/rstp.md b/docs/resources/rstp.md index 2673d9c9..0621c127 100644 --- a/docs/resources/rstp.md +++ b/docs/resources/rstp.md @@ -30,7 +30,7 @@ The following arguments are supported: - **backup_bridge_priority** (Optional, String) Priority of the bridge (in increments of 4k - 4k,8k,..60k). - **bpdu_block_on_edge** (Optional, Boolean) - Block BPDU on all interfaces configured as edge (BPDU Protect) + Block BPDU on all interfaces configured as edge (BPDU Protect). - **bpdu_destination_mac_address_provider_bridge_group** (Optional, Boolean) Destination MAC address in the spanning tree BPDUs is 802.1ad provider bridge group address. - **bridge_priority** (Optional, String) @@ -40,7 +40,7 @@ The following arguments are supported: - **extended_system_id** (Optional, Number) Extended system identifier (0..4095). - **force_version_stp** (Optional, Boolean) - Force protocol version stp. + Force protocol version STP. - **forward_delay** (Optional, Number) Time spent in listening or learning state (4..30 seconds). - **hello_time** (Optional, Number) diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index 99338ced..c271adbc 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -261,6 +261,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newPolicyoptionsPolicyStatementResource, newPolicyoptionsPrefixListResource, newRoutingInstanceResource, + newRstpResource, newSecurityResource, newSecurityAddressBookResource, newSecurityGlobalPolicyResource, diff --git a/internal/providerfwk/resource_rstp.go b/internal/providerfwk/resource_rstp.go new file mode 100644 index 00000000..70a08762 --- /dev/null +++ b/internal/providerfwk/resource_rstp.go @@ -0,0 +1,745 @@ +package providerfwk + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/stringvalidator" + "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/stringdefault" + "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 = &rstp{} + _ resource.ResourceWithConfigure = &rstp{} + _ resource.ResourceWithValidateConfig = &rstp{} + _ resource.ResourceWithImportState = &rstp{} +) + +type rstp struct { + client *junos.Client +} + +func newRstpResource() resource.Resource { + return &rstp{} +} + +func (rsc *rstp) typeName() string { + return providerName + "_rstp" +} + +func (rsc *rstp) junosName() string { + return "protocols rstp" +} + +func (rsc *rstp) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *rstp) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *rstp) 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 *rstp) 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 format ``.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for rstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "backup_bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^\d\d?k$`), + "must be a number with increments of 4k - 4k,8k,..60k", + ), + }, + }, + "bpdu_block_on_edge": schema.BoolAttribute{ + Optional: true, + Description: "Block BPDU on all interfaces configured as edge (BPDU Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bpdu_destination_mac_address_provider_bridge_group": schema.BoolAttribute{ + Optional: true, + Description: "Destination MAC address in the spanning tree BPDUs is 802.1ad provider bridge group address.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^(0|\d\d?k)$`), + "must be a number with increments of 4k - 0,4k,8k,..60k", + ), + }, + }, + "disable": schema.BoolAttribute{ + Optional: true, + Description: "Disable STP.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "extended_system_id": schema.Int64Attribute{ + Optional: true, + Description: "Extended system identifier.", + Validators: []validator.Int64{ + int64validator.Between(0, 4095), + }, + }, + "force_version_stp": schema.BoolAttribute{ + Optional: true, + Description: "Force protocol version STP.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "forward_delay": schema.Int64Attribute{ + Optional: true, + Description: "Time spent in listening or learning state (seconds).", + Validators: []validator.Int64{ + int64validator.Between(4, 30), + }, + }, + "hello_time": schema.Int64Attribute{ + Optional: true, + Description: "Time interval between configuration BPDUs (seconds).", + Validators: []validator.Int64{ + int64validator.Between(1, 10), + }, + }, + "max_age": schema.Int64Attribute{ + Optional: true, + Description: "Maximum age of received protocol bpdu (seconds).", + Validators: []validator.Int64{ + int64validator.Between(6, 40), + }, + }, + "priority_hold_time": schema.Int64Attribute{ + Optional: true, + Description: "Hold time before switching to primary priority when core domain becomes up (seconds).", + Validators: []validator.Int64{ + int64validator.Between(1, 255), + }, + }, + "system_identifier": schema.StringAttribute{ + Optional: true, + Description: "System identifier to represent this node.", + Validators: []validator.String{ + tfvalidator.StringMACAddress().WithMac48ColonHexa(), + }, + }, + "vpls_flush_on_topology_change": schema.BoolAttribute{ + Optional: true, + Description: "Enable VPLS MAC flush on root protected CE interface receiving topology change.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "system_id": schema.SetNestedBlock{ + Description: "For each ID, System ID to IP mapping.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Required: true, + Description: "System ID.", + Validators: []validator.String{ + tfvalidator.StringMACAddress().WithMac48ColonHexa(), + }, + }, + "ip_address": schema.StringAttribute{ + Optional: true, + Description: "Peer ID (IP Address).", + Validators: []validator.String{ + tfvalidator.StringCIDR(), + }, + }, + }, + }, + }, + }, + } +} + +//nolint:lll +type rstpData struct { + ID types.String `tfsdk:"id"` + RoutingInstance types.String `tfsdk:"routing_instance"` + BackupBridgePriority types.String `tfsdk:"backup_bridge_priority"` + BpduBlockOnEdge types.Bool `tfsdk:"bpdu_block_on_edge"` + BpduDestinationMacAddressProviderBridgeGroup types.Bool `tfsdk:"bpdu_destination_mac_address_provider_bridge_group"` + BridgePriority types.String `tfsdk:"bridge_priority"` + Disable types.Bool `tfsdk:"disable"` + ExtendedSystemID types.Int64 `tfsdk:"extended_system_id"` + ForceVersionStp types.Bool `tfsdk:"force_version_stp"` + ForwardDelay types.Int64 `tfsdk:"forward_delay"` + HelloTime types.Int64 `tfsdk:"hello_time"` + MaxAge types.Int64 `tfsdk:"max_age"` + PriorityHoldTime types.Int64 `tfsdk:"priority_hold_time"` + SystemIdentifier types.String `tfsdk:"system_identifier"` + VplsFlushOnTopologyChange types.Bool `tfsdk:"vpls_flush_on_topology_change"` + SystemID []rstpBlockSystemID `tfsdk:"system_id"` +} + +type rstpConfig struct { + ID types.String `tfsdk:"id"` + RoutingInstance types.String `tfsdk:"routing_instance"` + BackupBridgePriority types.String `tfsdk:"backup_bridge_priority"` + BpduBlockOnEdge types.Bool `tfsdk:"bpdu_block_on_edge"` + BpduDestinationMacAddressProviderBridgeGroup types.Bool `tfsdk:"bpdu_destination_mac_address_provider_bridge_group"` + BridgePriority types.String `tfsdk:"bridge_priority"` + Disable types.Bool `tfsdk:"disable"` + ExtendedSystemID types.Int64 `tfsdk:"extended_system_id"` + ForceVersionStp types.Bool `tfsdk:"force_version_stp"` + ForwardDelay types.Int64 `tfsdk:"forward_delay"` + HelloTime types.Int64 `tfsdk:"hello_time"` + MaxAge types.Int64 `tfsdk:"max_age"` + PriorityHoldTime types.Int64 `tfsdk:"priority_hold_time"` + SystemIdentifier types.String `tfsdk:"system_identifier"` + VplsFlushOnTopologyChange types.Bool `tfsdk:"vpls_flush_on_topology_change"` + SystemID types.Set `tfsdk:"system_id"` +} + +type rstpBlockSystemID struct { + ID types.String `tfsdk:"id"` + IPAddress types.String `tfsdk:"ip_address"` +} + +func (rsc *rstp) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config rstpConfig + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.BackupBridgePriority.IsNull() && !config.BackupBridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BackupBridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be a multiple of 4k", + ) + } + if v < 4 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be between 4k and 60k", + ) + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if bridgePriority, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v <= bridgePriority { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be worse (higher value) than bridge_priority", + ) + } + } + } + } + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be a multiple of 4k", + ) + } + if v < 0 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be between 0 and 60k", + ) + } + } + } + if !config.SystemID.IsNull() && !config.SystemID.IsUnknown() { + var systemID []rstpBlockSystemID + asDiags := config.SystemID.ElementsAs(ctx, &systemID, false) + if asDiags.HasError() { + resp.Diagnostics.Append(asDiags...) + + return + } + systemIDID := make(map[string]struct{}) + for _, block := range systemID { + if block.ID.IsUnknown() { + continue + } + id := block.ID.ValueString() + if _, ok := systemIDID[id]; ok { + resp.Diagnostics.AddAttributeError( + path.Root("system_id"), + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf("multiple system_id blocks with the same id %q", id), + ) + } + systemIDID[id] = struct{}{} + } + } +} + +func (rsc *rstp) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan rstpData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + + return true + }, + nil, + &plan, + resp, + ) +} + +func (rsc *rstp) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data rstpData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + junSess, err := rsc.junosClient().StartNewSession(ctx) + if err != nil { + resp.Diagnostics.AddError(tfdiag.StartSessErrSummary, err.Error()) + + return + } + defer junSess.Close() + + junos.MutexLock() + if v := state.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(ctx, v, junSess) + if err != nil { + junos.MutexUnlock() + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if !instanceExists { + junos.MutexUnlock() + resp.State.RemoveResource(ctx) + + return + } + } + + err = data.read(ctx, state.RoutingInstance.ValueString(), junSess) + junos.MutexUnlock() + if err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + + if data.nullID() { + resp.State.RemoveResource(ctx) + + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (rsc *rstp) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state rstpData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *rstp) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state rstpData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *rstp) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + junSess, err := rsc.junosClient().StartNewSession(ctx) + if err != nil { + resp.Diagnostics.AddError(tfdiag.StartSessErrSummary, err.Error()) + + return + } + defer junSess.Close() + + if req.ID != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(ctx, req.ID, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if !instanceExists { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", req.ID), + ) + + return + } + } + + var data rstpData + if err := data.read(ctx, req.ID, junSess); err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if data.nullID() { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceImportDontFindMessage(rsc, req.ID)+ + " (id must be )", + ) + + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (rscData *rstpData) fillID() { + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(v) + } else { + rscData.ID = types.StringValue(junos.DefaultW) + } +} + +func (rscData *rstpData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *rstpData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + configSet := make([]string, 0) + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols rstp " + + if v := rscData.BackupBridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) + } + if rscData.BpduBlockOnEdge.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-block-on-edge") + } + if rscData.BpduDestinationMacAddressProviderBridgeGroup.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-destination-mac-address provider-bridge-group") + } + if v := rscData.BridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"bridge-priority "+v) + } + if rscData.Disable.ValueBool() { + configSet = append(configSet, setPrefix+"disable") + } + if !rscData.ExtendedSystemID.IsNull() { + configSet = append(configSet, setPrefix+"extended-system-id "+ + utils.ConvI64toa(rscData.ExtendedSystemID.ValueInt64())) + } + if rscData.ForceVersionStp.ValueBool() { + configSet = append(configSet, setPrefix+"force-version stp") + } + if !rscData.ForwardDelay.IsNull() { + configSet = append(configSet, setPrefix+"forward-delay "+ + utils.ConvI64toa(rscData.ForwardDelay.ValueInt64())) + } + if !rscData.HelloTime.IsNull() { + configSet = append(configSet, setPrefix+"hello-time "+ + utils.ConvI64toa(rscData.HelloTime.ValueInt64())) + } + if !rscData.MaxAge.IsNull() { + configSet = append(configSet, setPrefix+"max-age "+ + utils.ConvI64toa(rscData.MaxAge.ValueInt64())) + } + if !rscData.PriorityHoldTime.IsNull() { + configSet = append(configSet, setPrefix+"priority-hold-time "+ + utils.ConvI64toa(rscData.PriorityHoldTime.ValueInt64())) + } + if v := rscData.SystemIdentifier.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"system-identifier "+v) + } + if rscData.VplsFlushOnTopologyChange.ValueBool() { + configSet = append(configSet, setPrefix+"vpls-flush-on-topology-change") + } + systemIDID := make(map[string]struct{}) + for _, block := range rscData.SystemID { + id := block.ID.ValueString() + if _, ok := systemIDID[id]; ok { + return path.Root("system_id"), + fmt.Errorf("multiple system_id blocks with the same id %q", id) + } + systemIDID[id] = struct{}{} + + configSet = append(configSet, setPrefix+"system-id "+id) + if v := block.IPAddress.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"system-id "+id+" ip-address "+v) + } + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *rstpData) read( + _ context.Context, routingInstance string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols rstp" + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + rscData.fillID() + if showConfig != junos.EmptyW { + 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) + switch { + case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): + rscData.BackupBridgePriority = types.StringValue(itemTrim) + case itemTrim == "bpdu-block-on-edge": + rscData.BpduBlockOnEdge = types.BoolValue(true) + case itemTrim == "bpdu-destination-mac-address provider-bridge-group": + rscData.BpduDestinationMacAddressProviderBridgeGroup = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "bridge-priority "): + rscData.BridgePriority = types.StringValue(itemTrim) + case itemTrim == junos.DisableW: + rscData.Disable = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "extended-system-id "): + rscData.ExtendedSystemID, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case itemTrim == "force-version stp": + rscData.ForceVersionStp = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "forward-delay "): + rscData.ForwardDelay, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "hello-time "): + rscData.HelloTime, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "max-age "): + rscData.MaxAge, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "priority-hold-time "): + rscData.PriorityHoldTime, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "system-identifier "): + rscData.SystemIdentifier = types.StringValue(itemTrim) + case itemTrim == "vpls-flush-on-topology-change": + rscData.VplsFlushOnTopologyChange = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "system-id "): + itemTrimFields := strings.Split(itemTrim, " ") + switch len(itemTrimFields) { // (ip-address )? + case 1: + rscData.SystemID = append(rscData.SystemID, rstpBlockSystemID{ + ID: types.StringValue(itemTrimFields[0]), + }) + case 3: + rscData.SystemID = append(rscData.SystemID, rstpBlockSystemID{ + ID: types.StringValue(itemTrimFields[0]), + IPAddress: types.StringValue(itemTrimFields[2]), + }) + default: + return fmt.Errorf(junos.CantReadValuesNotEnoughFields, "system-id", itemTrim) + } + } + } + } + + return nil +} + +func (rscData *rstpData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + delPrefix += "protocols rstp " + + listLinesToDelete := []string{ + "backup-bridge-priority", + "bpdu-block-on-edge", + "bpdu-destination-mac-address", + "bridge-priority", + "disable", + "extended-system-id", + "force-version", + "forward-delay", + "hello-time", + "max-age", + "priority-hold-time", + "system-identifier", + "vpls-flush-on-topology-change", + } + + configSet := make([]string, len(listLinesToDelete), len(listLinesToDelete)+len(rscData.SystemID)) + for k, line := range listLinesToDelete { + configSet[k] = delPrefix + line + } + for _, block := range rscData.SystemID { + configSet = append(configSet, delPrefix+"system-id "+block.ID.ValueString()) + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_rstp_test.go b/internal/providerfwk/resource_rstp_test.go new file mode 100644 index 00000000..842bdb8c --- /dev/null +++ b/internal/providerfwk/resource_rstp_test.go @@ -0,0 +1,56 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceRstp_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") == "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_rstp.testacc_rstp", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +func TestAccResourceRstp_switch(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_rstp.testacc_ri_rstp", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstp_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceRstp_basic/1/main.tf new file mode 100644 index 00000000..2a315f8e --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstp_basic/1/main.tf @@ -0,0 +1,3 @@ +resource "junos_rstp" "testacc_rstp" { + disable = true +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstp_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceRstp_basic/2/main.tf new file mode 100644 index 00000000..43dd5a6a --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstp_basic/2/main.tf @@ -0,0 +1,7 @@ +resource "junos_rstp" "testacc_rstp" { + backup_bridge_priority = "32k" + bridge_priority = "16k" + system_id { + id = "00:22:33:44:55:aa" + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstp_basic/3/main.tf b/internal/providerfwk/testdata/TestAccResourceRstp_basic/3/main.tf new file mode 100644 index 00000000..490d6dd6 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstp_basic/3/main.tf @@ -0,0 +1,20 @@ +resource "junos_rstp" "testacc_rstp" { + backup_bridge_priority = "60k" + bridge_priority = "4k" + bpdu_destination_mac_address_provider_bridge_group = true + extended_system_id = 0 + force_version_stp = true + forward_delay = 20 + hello_time = 5 + max_age = 22 + priority_hold_time = 100 + system_id { + id = "00:11:22:33:44:55" + } + system_id { + id = "00:22:33:44:55:66" + ip_address = "192.0.2.4/24" + } + system_identifier = "66:55:44:33:22:11" + vpls_flush_on_topology_change = true +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstp_switch/1/main.tf b/internal/providerfwk/testdata/TestAccResourceRstp_switch/1/main.tf new file mode 100644 index 00000000..4888e4ce --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstp_switch/1/main.tf @@ -0,0 +1,16 @@ +resource "junos_rstp" "testacc_rstp" { + bpdu_block_on_edge = true +} + +resource "junos_routing_instance" "testacc_rstp" { + name = "testacc_rstp" + type = "virtual-switch" +} + +resource "junos_rstp" "testacc_ri_rstp" { + routing_instance = junos_routing_instance.testacc_rstp.name + bridge_priority = 0 + system_id { + id = "00:11:22:33:44:56" + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstp_switch/2/main.tf b/internal/providerfwk/testdata/TestAccResourceRstp_switch/2/main.tf new file mode 100644 index 00000000..01ef1de1 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstp_switch/2/main.tf @@ -0,0 +1,32 @@ +resource "junos_rstp" "testacc_rstp" { + bpdu_block_on_edge = true + backup_bridge_priority = "8k" + bridge_priority = 0 +} + +resource "junos_routing_instance" "testacc_rstp" { + name = "testacc_rstp" + type = "virtual-switch" +} + +resource "junos_rstp" "testacc_ri_rstp" { + routing_instance = junos_routing_instance.testacc_rstp.name + backup_bridge_priority = "60k" + bridge_priority = "4k" + bpdu_destination_mac_address_provider_bridge_group = true + extended_system_id = 0 + force_version_stp = true + forward_delay = 20 + hello_time = 5 + max_age = 22 + priority_hold_time = 100 + system_id { + id = "00:11:22:33:44:55" + } + system_id { + id = "00:22:33:44:55:aa" + ip_address = "192.0.2.4/31" + } + system_identifier = "66:55:44:33:22:11" + vpls_flush_on_topology_change = true +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index 85553727..7c66ee79 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -194,7 +194,6 @@ func Provider() *schema.Provider { "junos_routing_options": resourceRoutingOptions(), - "junos_rstp": resourceRstp(), "junos_rstp_interface": resourceRstpInterface(), "junos_security_dynamic_address_feed_server": resourceSecurityDynamicAddressFeedServer(), diff --git a/internal/providersdk/resource_rstp.go b/internal/providersdk/resource_rstp.go deleted file mode 100644 index 86418e34..00000000 --- a/internal/providersdk/resource_rstp.go +++ /dev/null @@ -1,586 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "regexp" - "slices" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type rstpOptions struct { - bpduBlockOnEdge bool - bpduDestMACAddProvBridgeGrp bool - disable bool - forceVersionStp bool - vplsFlushOnTopologyChange bool - extendedSystemID int - forwardDelay int - helloTime int - maxAge int - priorityHoldTime int - backupBridgePriority string - bridgePriority string - routingInstance string - systemIdentifier string - systemID []map[string]interface{} -} - -func resourceRstp() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceRstpCreate, - ReadWithoutTimeout: resourceRstpRead, - UpdateWithoutTimeout: resourceRstpUpdate, - DeleteWithoutTimeout: resourceRstpDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceRstpImport, - }, - Schema: map[string]*schema.Schema{ - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "backup_bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^\d\d?k$`), "must be a number with increments of 4k - 4k,8k,..60k"), - }, - "bpdu_block_on_edge": { - Type: schema.TypeBool, - Optional: true, - }, - "bpdu_destination_mac_address_provider_bridge_group": { - Type: schema.TypeBool, - Optional: true, - }, - "bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^(0|\d\d?k)$`), "must be a number with increments of 4k - 0,4k,8k,..60k"), - }, - "disable": { - Type: schema.TypeBool, - Optional: true, - }, - "extended_system_id": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(0, 4095), - Default: -1, - }, - "force_version_stp": { - Type: schema.TypeBool, - Optional: true, - }, - "forward_delay": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(4, 30), - }, - "hello_time": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 10), - }, - "max_age": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(6, 40), - }, - "priority_hold_time": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 255), - }, - "system_id": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.IsMACAddress, - }, - "ip_address": { - Type: schema.TypeString, - Optional: true, - Default: "", - ValidateFunc: validation.IsCIDR, - }, - }, - }, - }, - "system_identifier": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.IsMACAddress, - }, - "vpls_flush_on_topology_change": { - Type: schema.TypeBool, - Optional: true, - }, - }, - } -} - -func resourceRstpCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setRstp(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(d.Get("routing_instance").(string)) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if d.Get("routing_instance").(string) != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) - } - } - if err := setRstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_rstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.SetId(d.Get("routing_instance").(string)) - - return append(diagWarns, resourceRstpReadWJunSess(d, junSess)...) -} - -func resourceRstpRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceRstpReadWJunSess(d, junSess) -} - -func resourceRstpReadWJunSess(d *schema.ResourceData, junSess *junos.Session) diag.Diagnostics { - junos.MutexLock() - if d.Get("routing_instance").(string) != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), junSess) - if err != nil { - junos.MutexUnlock() - - return diag.FromErr(err) - } - if !instanceExists { - junos.MutexUnlock() - d.SetId("") - - return nil - } - } - rstpOptions, err := readRstp(d.Get("routing_instance").(string), junSess) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - fillRstpData(d, rstpOptions) - - return nil -} - -func resourceRstpUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delRstp(d, junSess); err != nil { - return diag.FromErr(err) - } - if err := setRstp(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delRstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setRstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_rstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceRstpReadWJunSess(d, junSess)...) -} - -func resourceRstpDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delRstp(d, junSess); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delRstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_rstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceRstpImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - if d.Id() != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Id(), junSess) - if err != nil { - return nil, err - } - if !instanceExists { - return nil, fmt.Errorf("routing instance %v doesn't exist", d.Id()) - } - } - result := make([]*schema.ResourceData, 1) - rstpOptions, err := readRstp(d.Id(), junSess) - if err != nil { - return nil, err - } - fillRstpData(d, rstpOptions) - result[0] = d - - return result, nil -} - -func setRstp(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - setPrefix := junos.SetLS - if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " - } - setPrefix += "protocols rstp " - - if v := d.Get("backup_bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) - } - if d.Get("bpdu_block_on_edge").(bool) { - configSet = append(configSet, setPrefix+"bpdu-block-on-edge") - } - if d.Get("bpdu_destination_mac_address_provider_bridge_group").(bool) { - configSet = append(configSet, setPrefix+"bpdu-destination-mac-address provider-bridge-group") - } - if v := d.Get("bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"bridge-priority "+v) - } - if d.Get("disable").(bool) { - configSet = append(configSet, setPrefix+"disable") - } - if v := d.Get("extended_system_id").(int); v != -1 { - configSet = append(configSet, setPrefix+"extended-system-id "+strconv.Itoa(v)) - } - if d.Get("force_version_stp").(bool) { - configSet = append(configSet, setPrefix+"force-version stp") - } - if v := d.Get("forward_delay").(int); v != 0 { - configSet = append(configSet, setPrefix+"forward-delay "+strconv.Itoa(v)) - } - if v := d.Get("hello_time").(int); v != 0 { - configSet = append(configSet, setPrefix+"hello-time "+strconv.Itoa(v)) - } - if v := d.Get("max_age").(int); v != 0 { - configSet = append(configSet, setPrefix+"max-age "+strconv.Itoa(v)) - } - if v := d.Get("priority_hold_time").(int); v != 0 { - configSet = append(configSet, setPrefix+"priority-hold-time "+strconv.Itoa(v)) - } - systemIDList := make([]string, 0) - for _, mSysID := range d.Get("system_id").(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - if slices.Contains(systemIDList, systemID["id"].(string)) { - return fmt.Errorf("multiple blocks system_id with the same id '%s'", systemID["id"].(string)) - } - systemIDList = append(systemIDList, systemID["id"].(string)) - configSet = append(configSet, setPrefix+"system-id "+systemID["id"].(string)) - if ipAdd := systemID["ip_address"].(string); ipAdd != "" { - configSet = append(configSet, setPrefix+"system-id "+systemID["id"].(string)+" ip-address "+ipAdd) - } - } - if v := d.Get("system_identifier").(string); v != "" { - configSet = append(configSet, setPrefix+"system-identifier "+v) - } - if d.Get("vpls_flush_on_topology_change").(bool) { - configSet = append(configSet, setPrefix+"vpls-flush-on-topology-change") - } - - return junSess.ConfigSet(configSet) -} - -func readRstp(routingInstance string, junSess *junos.Session, -) (confRead rstpOptions, err error) { - // default -1 - confRead.extendedSystemID = -1 - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols rstp" + junos.PipeDisplaySetRelative) - if err != nil { - return confRead, err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols rstp" + junos.PipeDisplaySetRelative) - if err != nil { - return confRead, err - } - } - - confRead.routingInstance = routingInstance - if showConfig != junos.EmptyW { - 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) - switch { - case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): - confRead.backupBridgePriority = itemTrim - case itemTrim == "bpdu-block-on-edge": - confRead.bpduBlockOnEdge = true - case itemTrim == "bpdu-destination-mac-address provider-bridge-group": - confRead.bpduDestMACAddProvBridgeGrp = true - case balt.CutPrefixInString(&itemTrim, "bridge-priority "): - confRead.bridgePriority = itemTrim - case itemTrim == junos.DisableW: - confRead.disable = true - case balt.CutPrefixInString(&itemTrim, "extended-system-id "): - confRead.extendedSystemID, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case itemTrim == "force-version stp": - confRead.forceVersionStp = true - case balt.CutPrefixInString(&itemTrim, "forward-delay "): - confRead.forwardDelay, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "hello-time "): - confRead.helloTime, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "max-age "): - confRead.maxAge, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "priority-hold-time "): - confRead.priorityHoldTime, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "system-id "): - itemTrimFields := strings.Split(itemTrim, " ") - switch len(itemTrimFields) { // (ip-address )? - case 1: - confRead.systemID = append(confRead.systemID, map[string]interface{}{ - "id": itemTrimFields[0], - "ip_address": "", - }) - case 3: - confRead.systemID = append(confRead.systemID, map[string]interface{}{ - "id": itemTrimFields[0], - "ip_address": itemTrimFields[2], - }) - default: - return confRead, fmt.Errorf(junos.CantReadValuesNotEnoughFields, "system-id", itemTrim) - } - case balt.CutPrefixInString(&itemTrim, "system-identifier "): - confRead.systemIdentifier = itemTrim - case itemTrim == "vpls-flush-on-topology-change": - confRead.vplsFlushOnTopologyChange = true - } - } - } - - return confRead, nil -} - -func delRstp(d *schema.ResourceData, junSess *junos.Session) error { - delPrefix := junos.DeleteLS - if d.Get("routing_instance").(string) != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + d.Get("routing_instance").(string) + " " - } - delPrefix += "protocols rstp " - - listLinesToDelete := []string{ - "backup-bridge-priority", - "bpdu-block-on-edge", - "bpdu-destination-mac-address", - "bridge-priority", - "disable", - "extended-system-id", - "force-version", - "forward-delay", - "hello-time", - "max-age", - "priority-hold-time", - "system-identifier", - "vpls-flush-on-topology-change", - } - configSet := make([]string, - len(listLinesToDelete), len(listLinesToDelete)+len(d.Get("system_id").(*schema.Set).List())) - for k, line := range listLinesToDelete { - configSet[k] = delPrefix + line - } - if d.HasChange("system_id") { - oSysID, _ := d.GetChange("system_id") - for _, mSysID := range oSysID.(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - configSet = append(configSet, delPrefix+"system-id "+systemID["id"].(string)) - } - } else { - for _, mSysID := range d.Get("system_id").(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - configSet = append(configSet, delPrefix+"system-id "+systemID["id"].(string)) - } - } - - return junSess.ConfigSet(configSet) -} - -func fillRstpData(d *schema.ResourceData, rstpOptions rstpOptions) { - if tfErr := d.Set("routing_instance", rstpOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("backup_bridge_priority", rstpOptions.backupBridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_block_on_edge", rstpOptions.bpduBlockOnEdge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set( - "bpdu_destination_mac_address_provider_bridge_group", - rstpOptions.bpduDestMACAddProvBridgeGrp, - ); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bridge_priority", rstpOptions.bridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("disable", rstpOptions.disable); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("extended_system_id", rstpOptions.extendedSystemID); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("force_version_stp", rstpOptions.forceVersionStp); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("forward_delay", rstpOptions.forwardDelay); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("hello_time", rstpOptions.helloTime); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("max_age", rstpOptions.maxAge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("priority_hold_time", rstpOptions.priorityHoldTime); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("system_id", rstpOptions.systemID); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("system_identifier", rstpOptions.systemIdentifier); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("vpls_flush_on_topology_change", rstpOptions.vplsFlushOnTopologyChange); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_rstp_test.go b/internal/providersdk/resource_rstp_test.go deleted file mode 100644 index 7235ffea..00000000 --- a/internal/providersdk/resource_rstp_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package providersdk_test - -import ( - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccResourceRstp_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceRstpSWConfigCreate(), - }, - { - Config: testAccResourceRstpSWConfigUpdate(), - }, - { - ResourceName: "junos_rstp.testacc_ri_rstp", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } else { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceRstpConfigCreate(), - }, - { - Config: testAccResourceRstpConfigUpdate(), - }, - { - Config: testAccResourceRstpConfigUpdate2(), - }, - { - ResourceName: "junos_rstp.testacc_rstp", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceRstpSWConfigCreate() string { - return ` -resource "junos_rstp" "testacc_rstp" { - bpdu_block_on_edge = true -} -resource "junos_routing_instance" "testacc_rstp" { - name = "testacc_rstp" - type = "virtual-switch" -} -resource "junos_rstp" "testacc_ri_rstp" { - routing_instance = junos_routing_instance.testacc_rstp.name - bridge_priority = 0 - system_id { - id = "00:11:22:33:44:56" - } -} -` -} - -func testAccResourceRstpSWConfigUpdate() string { - return ` -resource "junos_rstp" "testacc_rstp" { - bpdu_block_on_edge = true - backup_bridge_priority = "8k" - bridge_priority = 0 -} -resource "junos_routing_instance" "testacc_rstp" { - name = "testacc_rstp" - type = "virtual-switch" -} -resource "junos_rstp" "testacc_ri_rstp" { - routing_instance = junos_routing_instance.testacc_rstp.name - backup_bridge_priority = "60k" - bridge_priority = "4k" - bpdu_destination_mac_address_provider_bridge_group = true - extended_system_id = 0 - force_version_stp = true - forward_delay = 20 - hello_time = 5 - max_age = 22 - priority_hold_time = 100 - system_id { - id = "00:11:22:33:44:55" - } - system_id { - id = "00:22:33:44:55:aa" - ip_address = "192.0.2.4/31" - } - system_identifier = "66:55:44:33:22:11" - vpls_flush_on_topology_change = true -} -` -} - -func testAccResourceRstpConfigCreate() string { - return ` -resource "junos_rstp" "testacc_rstp" { - disable = true -} -` -} - -func testAccResourceRstpConfigUpdate() string { - return ` -resource "junos_rstp" "testacc_rstp" { - backup_bridge_priority = "32k" - bridge_priority = "16k" - system_id { - id = "00:22:33:44:55:aa" - } -} -` -} - -func testAccResourceRstpConfigUpdate2() string { - return ` -resource "junos_rstp" "testacc_rstp" { - backup_bridge_priority = "60k" - bridge_priority = "4k" - bpdu_destination_mac_address_provider_bridge_group = true - extended_system_id = 0 - force_version_stp = true - forward_delay = 20 - hello_time = 5 - max_age = 22 - priority_hold_time = 100 - system_id { - id = "00:11:22:33:44:55" - } - system_id { - id = "00:22:33:44:55:66" - ip_address = "192.0.2.4/24" - } - system_identifier = "66:55:44:33:22:11" - vpls_flush_on_topology_change = true -} -` -} From 635ff39e696bbdfa085a48cb0402d995f21904b9 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 30 May 2024 18:43:34 +0200 Subject: [PATCH 10/30] r/rstp_interface: use new provider via framework --- .changes/rstp-with-fwk.md | 3 + internal/providerfwk/provider.go | 1 + .../providerfwk/resource_rstp_interface.go | 538 ++++++++++++++++++ .../resource_rstp_interface_test.go | 82 +++ .../1/main.tf | 3 + .../2/main.tf | 10 + .../1/main.tf | 13 + .../2/main.tf | 37 ++ .../2/variables.tf | 3 + internal/providersdk/provider.go | 2 - .../providersdk/resource_rstp_interface.go | 495 ---------------- .../resource_rstp_interface_test.go | 149 ----- 12 files changed, 690 insertions(+), 646 deletions(-) create mode 100644 internal/providerfwk/resource_rstp_interface.go create mode 100644 internal/providerfwk/resource_rstp_interface_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/variables.tf delete mode 100644 internal/providersdk/resource_rstp_interface.go delete mode 100644 internal/providersdk/resource_rstp_interface_test.go diff --git a/.changes/rstp-with-fwk.md b/.changes/rstp-with-fwk.md index a828128e..f20483e1 100644 --- a/.changes/rstp-with-fwk.md +++ b/.changes/rstp-with-fwk.md @@ -4,3 +4,6 @@ ENHANCEMENTS: * **resource/junos_rstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) some of config errors are now sent during Plan instead of during Apply optional boolean attributes doesn't accept value *false* +* **resource/junos_rstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index c271adbc..79664493 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -262,6 +262,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newPolicyoptionsPrefixListResource, newRoutingInstanceResource, newRstpResource, + newRstpInterfaceResource, newSecurityResource, newSecurityAddressBookResource, newSecurityGlobalPolicyResource, diff --git a/internal/providerfwk/resource_rstp_interface.go b/internal/providerfwk/resource_rstp_interface.go new file mode 100644 index 00000000..c6eb366e --- /dev/null +++ b/internal/providerfwk/resource_rstp_interface.go @@ -0,0 +1,538 @@ +package providerfwk + +import ( + "context" + "fmt" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/stringvalidator" + "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/stringdefault" + "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 = &rstpInterface{} + _ resource.ResourceWithConfigure = &rstpInterface{} + _ resource.ResourceWithValidateConfig = &rstpInterface{} + _ resource.ResourceWithImportState = &rstpInterface{} +) + +type rstpInterface struct { + client *junos.Client +} + +func newRstpInterfaceResource() resource.Resource { + return &rstpInterface{} +} + +func (rsc *rstpInterface) typeName() string { + return providerName + "_rstp_interface" +} + +func (rsc *rstpInterface) junosName() string { + return "rstp interface" +} + +func (rsc *rstpInterface) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *rstpInterface) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *rstpInterface) 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 *rstpInterface) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: defaultResourceSchemaDescription(rsc), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "An identifier for the resource with format " + + "`" + junos.IDSeparator + "`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "Interface name or `all`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + tfvalidator.StringFormat(tfvalidator.InterfaceFormat), + tfvalidator.StringDotExclusion(), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for rstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "access_trunk": schema.BoolAttribute{ + Optional: true, + Description: "Send/Receive untagged RSTP BPDUs on this interface.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bpdu_timeout_action_alarm": schema.BoolAttribute{ + Optional: true, + Description: "Generate an alarm on BPDU expiry (Loop Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bpdu_timeout_action_block": schema.BoolAttribute{ + Optional: true, + Description: "Block the interface on BPDU expiry (Loop Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "cost": schema.Int64Attribute{ + Optional: true, + Description: "Cost of the interface.", + Validators: []validator.Int64{ + int64validator.Between(1, 200000000), + }, + }, + "edge": schema.BoolAttribute{ + Optional: true, + Description: "Port is an edge port.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "mode": schema.StringAttribute{ + Optional: true, + Description: "Interface mode (P2P or shared).", + Validators: []validator.String{ + stringvalidator.OneOf("point-to-point", "shared"), + }, + }, + "no_root_port": schema.BoolAttribute{ + Optional: true, + Description: "Do not allow the interface to become root (Root Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "priority": schema.Int64Attribute{ + Optional: true, + Description: "Interface priority (in increments of 16).", + Validators: []validator.Int64{ + int64validator.Between(0, 240), + }, + }, + }, + } +} + +type rstpInterfaceData struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + AccessTrunk types.Bool `tfsdk:"access_trunk"` + BpduTimeoutActionAlarm types.Bool `tfsdk:"bpdu_timeout_action_alarm"` + BpduTimeoutActionBlock types.Bool `tfsdk:"bpdu_timeout_action_block"` + Cost types.Int64 `tfsdk:"cost"` + Edge types.Bool `tfsdk:"edge"` + Mode types.String `tfsdk:"mode"` + NoRootPort types.Bool `tfsdk:"no_root_port"` + Priority types.Int64 `tfsdk:"priority"` +} + +func (rsc *rstpInterface) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config rstpInterfaceData + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.Priority.IsNull() && !config.Priority.IsUnknown() { + if config.Priority.ValueInt64()%16 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("priority"), + "Bad Value Error", + "priority must be a multiple of 16", + ) + } + } +} + +func (rsc *rstpInterface) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan rstpInterfaceData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Name.ValueString() == "" { + resp.Diagnostics.AddAttributeError( + path.Root("name"), + "Empty Name", + defaultResourceCouldNotCreateWithEmptyMessage(rsc, "name"), + ) + + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + interfaceExists, err := checkRstpInterfaceExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if interfaceExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsInRoutingInstanceMessage(rsc, plan.Name, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsMessage(rsc, plan.Name), + ) + } + + return false + } + + return true + }, + func(fnCtx context.Context, junSess *junos.Session) bool { + interfaceExists, err := checkRstpInterfaceExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PostCheckErrSummary, err.Error()) + + return false + } + if !interfaceExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsInRoutingInstanceAfterCommitMessage(rsc, plan.Name, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsAfterCommitMessage(rsc, plan.Name), + ) + } + + return false + } + + return true + }, + &plan, + resp, + ) +} + +func (rsc *rstpInterface) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data rstpInterfaceData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataReadFrom2String = &data + defaultResourceRead( + ctx, + rsc, + []string{ + state.Name.ValueString(), + state.RoutingInstance.ValueString(), + }, + &data, + nil, + resp, + ) +} + +func (rsc *rstpInterface) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state rstpInterfaceData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *rstpInterface) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state rstpInterfaceData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *rstpInterface) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + var data rstpInterfaceData + + var _ resourceDataReadFrom2String = &data + defaultResourceImportState( + ctx, + rsc, + &data, + req, + resp, + defaultResourceImportDontFindMessage(rsc, req.ID)+ + " (id must be "+junos.IDSeparator+")", + ) +} + +func checkRstpInterfaceExists( + _ context.Context, name, routingInstance string, junSess *junos.Session, +) ( + bool, error, +) { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols rstp interface " + name + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} + +func (rscData *rstpInterfaceData) fillID() { + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(rscData.Name.ValueString() + junos.IDSeparator + v) + } else { + rscData.ID = types.StringValue(rscData.Name.ValueString() + junos.IDSeparator + junos.DefaultW) + } +} + +func (rscData *rstpInterfaceData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *rstpInterfaceData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols rstp interface " + rscData.Name.ValueString() + " " + + configSet := []string{ + setPrefix, + } + + if rscData.AccessTrunk.ValueBool() { + configSet = append(configSet, setPrefix+"access-trunk") + } + if rscData.BpduTimeoutActionAlarm.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-timeout-action alarm") + } + if rscData.BpduTimeoutActionBlock.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-timeout-action block") + } + if !rscData.Cost.IsNull() { + configSet = append(configSet, setPrefix+"cost "+ + utils.ConvI64toa(rscData.Cost.ValueInt64())) + } + if rscData.Edge.ValueBool() { + configSet = append(configSet, setPrefix+"edge") + } + if v := rscData.Mode.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"mode "+v) + } + if rscData.NoRootPort.ValueBool() { + configSet = append(configSet, setPrefix+"no-root-port") + } + if !rscData.Priority.IsNull() { + configSet = append(configSet, setPrefix+"priority "+ + utils.ConvI64toa(rscData.Priority.ValueInt64())) + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *rstpInterfaceData) read( + _ context.Context, name, routingInstance string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols rstp interface " + name + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if showConfig != junos.EmptyW { + rscData.Name = types.StringValue(name) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + 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) + switch { + case itemTrim == "access-trunk": + rscData.AccessTrunk = types.BoolValue(true) + case itemTrim == "bpdu-timeout-action alarm": + rscData.BpduTimeoutActionAlarm = types.BoolValue(true) + case itemTrim == "bpdu-timeout-action block": + rscData.BpduTimeoutActionBlock = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "cost "): + rscData.Cost, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case itemTrim == "edge": + rscData.Edge = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "mode "): + rscData.Mode = types.StringValue(itemTrim) + case itemTrim == "no-root-port": + rscData.NoRootPort = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "priority "): + rscData.Priority, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + } + } + } + + return nil +} + +func (rscData *rstpInterfaceData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "protocols rstp interface " + rscData.Name.ValueString(), + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_rstp_interface_test.go b/internal/providerfwk/resource_rstp_interface_test.go new file mode 100644 index 00000000..9bddf2a4 --- /dev/null +++ b/internal/providerfwk/resource_rstp_interface_test.go @@ -0,0 +1,82 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceRstpInterface_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") == "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_rstp_interface.all", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +// export TESTACC_INTERFACE= for choose interface available else it's xe-0/0/3. +func TestAccResourceRstpInterface_switch(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + testaccInterface := junos.DefaultInterfaceSwitchTestAcc + if iface := os.Getenv("TESTACC_INTERFACE"); iface != "" { + testaccInterface = iface + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_rstp_interface.all", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_rstp_interface.all2", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_rstp_interface.testacc_rstp_interface", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/1/main.tf new file mode 100644 index 00000000..ebf42560 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/1/main.tf @@ -0,0 +1,3 @@ +resource "junos_rstp_interface" "all" { + name = "all" +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/2/main.tf new file mode 100644 index 00000000..d62533a8 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstpInterface_basic/2/main.tf @@ -0,0 +1,10 @@ +resource "junos_rstp_interface" "all" { + name = "all" + access_trunk = true + bpdu_timeout_action_alarm = true + bpdu_timeout_action_block = true + cost = 16 + edge = true + mode = "shared" + priority = 240 +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/1/main.tf b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/1/main.tf new file mode 100644 index 00000000..13b33679 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/1/main.tf @@ -0,0 +1,13 @@ +resource "junos_rstp_interface" "all" { + name = "all" +} + +resource "junos_routing_instance" "testacc_rstp_interface" { + name = "testacc_rstp_intface" + type = "virtual-switch" +} + +resource "junos_rstp_interface" "all2" { + name = "all" + routing_instance = junos_routing_instance.testacc_rstp_interface.name +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/main.tf b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/main.tf new file mode 100644 index 00000000..5110c820 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/main.tf @@ -0,0 +1,37 @@ +resource "junos_rstp_interface" "all" { + name = "all" + access_trunk = true + bpdu_timeout_action_alarm = true + bpdu_timeout_action_block = true + cost = 16 + edge = true + mode = "shared" + priority = 240 +} + +resource "junos_interface_physical" "testacc_rstp_interface" { + name = var.interface + vlan_members = ["default"] +} + +resource "junos_rstp_interface" "testacc_rstp_interface" { + name = junos_interface_physical.testacc_rstp_interface.name + no_root_port = true +} + +resource "junos_routing_instance" "testacc_rstp_interface" { + name = "testacc_rstp_interface" + type = "virtual-switch" +} + +resource "junos_rstp_interface" "all2" { + name = "all" + routing_instance = junos_routing_instance.testacc_rstp_interface.name + access_trunk = true + bpdu_timeout_action_alarm = true + bpdu_timeout_action_block = true + cost = 16 + edge = true + mode = "shared" + priority = 240 +} diff --git a/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/variables.tf b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/variables.tf new file mode 100644 index 00000000..d5ed041c --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceRstpInterface_switch/2/variables.tf @@ -0,0 +1,3 @@ +variable "interface" { + type = string +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index 7c66ee79..852a150d 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -194,8 +194,6 @@ func Provider() *schema.Provider { "junos_routing_options": resourceRoutingOptions(), - "junos_rstp_interface": resourceRstpInterface(), - "junos_security_dynamic_address_feed_server": resourceSecurityDynamicAddressFeedServer(), "junos_security_dynamic_address_name": resourceSecurityDynamicAddressName(), "junos_security_idp_custom_attack": resourceSecurityIdpCustomAttack(), diff --git a/internal/providersdk/resource_rstp_interface.go b/internal/providersdk/resource_rstp_interface.go deleted file mode 100644 index 2c5bdcf7..00000000 --- a/internal/providersdk/resource_rstp_interface.go +++ /dev/null @@ -1,495 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type rstpInterfaceOptions struct { - accessTrunk bool - bpduTimeoutActionAlarm bool - bpduTimeoutActionBlock bool - edge bool - noRootPort bool - cost int - priority int - mode string - name string - routingInstance string -} - -func resourceRstpInterface() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceRstpInterfaceCreate, - ReadWithoutTimeout: resourceRstpInterfaceRead, - UpdateWithoutTimeout: resourceRstpInterfaceUpdate, - DeleteWithoutTimeout: resourceRstpInterfaceDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceRstpInterfaceImport, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if strings.Count(value, ".") > 0 { - errors = append(errors, fmt.Errorf( - "%q in %q cannot have a dot", value, k)) - } - - return - }, - }, - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "access_trunk": { - Type: schema.TypeBool, - Optional: true, - }, - "bpdu_timeout_action_alarm": { - Type: schema.TypeBool, - Optional: true, - }, - "bpdu_timeout_action_block": { - Type: schema.TypeBool, - Optional: true, - }, - "cost": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 200000000), - }, - "edge": { - Type: schema.TypeBool, - Optional: true, - }, - "mode": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"point-to-point", "shared"}, false), - }, - "no_root_port": { - Type: schema.TypeBool, - Optional: true, - }, - "priority": { - Type: schema.TypeInt, - Optional: true, - Default: -1, - ValidateFunc: validation.IntBetween(0, 240), - }, - }, - } -} - -func resourceRstpInterfaceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setRstpInterface(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(d.Get("name").(string) + junos.IDSeparator + d.Get("routing_instance").(string)) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if d.Get("routing_instance").(string) != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) - } - } - rstpInterfaceExists, err := checkRstpInterfaceExists( - d.Get("name").(string), - d.Get("routing_instance").(string), - junSess, - ) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if rstpInterfaceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - if d.Get("routing_instance").(string) == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols rstp interface %v already exists", - d.Get("name").(string)))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols rstp interface %v already exists in routing-instance %v", - d.Get("name").(string), d.Get("routing_instance").(string)))...) - } - - if err := setRstpInterface(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_rstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - rstpInterfaceExists, err = checkRstpInterfaceExists( - d.Get("name").(string), - d.Get("routing_instance").(string), - junSess, - ) - if err != nil { - return append(diagWarns, diag.FromErr(err)...) - } - if rstpInterfaceExists { - d.SetId(d.Get("name").(string) + junos.IDSeparator + d.Get("routing_instance").(string)) - } else { - if d.Get("routing_instance").(string) == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols rstp interface %v not exists after commit "+ - "=> check your config", d.Get("name").(string)))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols rstp interface %v not exists in routing-instance %v after commit "+ - "=> check your config", d.Get("name").(string), d.Get("routing_instance").(string)))...) - } - - return append(diagWarns, resourceRstpInterfaceReadWJunSess(d, junSess)...) -} - -func resourceRstpInterfaceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceRstpInterfaceReadWJunSess(d, junSess) -} - -func resourceRstpInterfaceReadWJunSess(d *schema.ResourceData, junSess *junos.Session, -) diag.Diagnostics { - junos.MutexLock() - rstpInterfaceOptions, err := readRstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - junSess, - ) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - if rstpInterfaceOptions.name == "" { - d.SetId("") - } else { - fillRstpInterfaceData(d, rstpInterfaceOptions) - } - - return nil -} - -func resourceRstpInterfaceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delRstpInterface(d.Get("name").(string), d.Get("routing_instance").(string), junSess); err != nil { - return diag.FromErr(err) - } - if err := setRstpInterface(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delRstpInterface(d.Get("name").(string), d.Get("routing_instance").(string), junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setRstpInterface(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_rstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceRstpInterfaceReadWJunSess(d, junSess)...) -} - -func resourceRstpInterfaceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delRstpInterface(d.Get("name").(string), d.Get("routing_instance").(string), junSess); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delRstpInterface(d.Get("name").(string), d.Get("routing_instance").(string), junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_rstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceRstpInterfaceImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - result := make([]*schema.ResourceData, 1) - idSplit := strings.Split(d.Id(), junos.IDSeparator) - if len(idSplit) < 2 { - return nil, fmt.Errorf("missing element(s) in id with separator %v", junos.IDSeparator) - } - rstpInterfaceExists, err := checkRstpInterfaceExists(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - if !rstpInterfaceExists { - return nil, fmt.Errorf("don't find protocols rstp interface with id '%v' "+ - "(id must be "+junos.IDSeparator+")", d.Id()) - } - rstpInterfaceOptions, err := readRstpInterface(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - fillRstpInterfaceData(d, rstpInterfaceOptions) - - result[0] = d - - return result, nil -} - -func checkRstpInterfaceExists(name, routingInstance string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols rstp interface " + name + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols rstp interface " + name + junos.PipeDisplaySet) - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} - -func setRstpInterface(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - - setPrefix := junos.SetLS - if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " - } - setPrefix += "protocols rstp interface " + d.Get("name").(string) + " " - - configSet = append(configSet, setPrefix) - if d.Get("access_trunk").(bool) { - configSet = append(configSet, setPrefix+"access-trunk") - } - if d.Get("bpdu_timeout_action_alarm").(bool) { - configSet = append(configSet, setPrefix+"bpdu-timeout-action alarm") - } - if d.Get("bpdu_timeout_action_block").(bool) { - configSet = append(configSet, setPrefix+"bpdu-timeout-action block") - } - if v := d.Get("cost").(int); v != 0 { - configSet = append(configSet, setPrefix+"cost "+strconv.Itoa(v)) - } - if d.Get("edge").(bool) { - configSet = append(configSet, setPrefix+"edge") - } - if v := d.Get("mode").(string); v != "" { - configSet = append(configSet, setPrefix+"mode "+v) - } - if d.Get("no_root_port").(bool) { - configSet = append(configSet, setPrefix+"no-root-port") - } - if v := d.Get("priority").(int); v != -1 { - configSet = append(configSet, setPrefix+"priority "+strconv.Itoa(v)) - } - - return junSess.ConfigSet(configSet) -} - -func readRstpInterface(name, routingInstance string, junSess *junos.Session, -) (confRead rstpInterfaceOptions, err error) { - // default -1 - confRead.priority = -1 - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols rstp interface " + name + junos.PipeDisplaySetRelative) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols rstp interface " + name + junos.PipeDisplaySetRelative) - } - if err != nil { - return confRead, err - } - if showConfig != junos.EmptyW { - confRead.name = name - confRead.routingInstance = routingInstance - 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) - switch { - case itemTrim == "access-trunk": - confRead.accessTrunk = true - case itemTrim == "bpdu-timeout-action alarm": - confRead.bpduTimeoutActionAlarm = true - case itemTrim == "bpdu-timeout-action block": - confRead.bpduTimeoutActionBlock = true - case balt.CutPrefixInString(&itemTrim, "cost "): - confRead.cost, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case itemTrim == "edge": - confRead.edge = true - case balt.CutPrefixInString(&itemTrim, "mode "): - confRead.mode = itemTrim - case itemTrim == "no-root-port": - confRead.noRootPort = true - case balt.CutPrefixInString(&itemTrim, "priority "): - confRead.priority, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - } - } - } - - return confRead, nil -} - -func delRstpInterface(name, routingInstance string, junSess *junos.Session) error { - configSet := make([]string, 0, 1) - - if routingInstance == junos.DefaultW { - configSet = append(configSet, "delete protocols rstp interface "+name) - } else { - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+" protocols rstp interface "+name) - } - - return junSess.ConfigSet(configSet) -} - -func fillRstpInterfaceData(d *schema.ResourceData, rstpInterfaceOptions rstpInterfaceOptions) { - if tfErr := d.Set("name", rstpInterfaceOptions.name); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("routing_instance", rstpInterfaceOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("access_trunk", rstpInterfaceOptions.accessTrunk); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_timeout_action_alarm", rstpInterfaceOptions.bpduTimeoutActionAlarm); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_timeout_action_block", rstpInterfaceOptions.bpduTimeoutActionBlock); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("cost", rstpInterfaceOptions.cost); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("edge", rstpInterfaceOptions.edge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("mode", rstpInterfaceOptions.mode); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("no_root_port", rstpInterfaceOptions.noRootPort); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("priority", rstpInterfaceOptions.priority); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_rstp_interface_test.go b/internal/providersdk/resource_rstp_interface_test.go deleted file mode 100644 index 9104db72..00000000 --- a/internal/providersdk/resource_rstp_interface_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package providersdk_test - -import ( - "fmt" - "os" - "testing" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -// export TESTACC_INTERFACE= for choose interface available else it's xe-0/0/3. -func TestAccResourceRstpInterface_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - testaccInterface := junos.DefaultInterfaceSwitchTestAcc - if iface := os.Getenv("TESTACC_INTERFACE"); iface != "" { - testaccInterface = iface - } - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceRstpInterfaceSWConfigCreate(), - }, - { - Config: testAccResourceRstpInterfaceSWConfigUpdate(testaccInterface), - }, - { - ResourceName: "junos_rstp_interface.all", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_rstp_interface.all2", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_rstp_interface.testacc_rstp_interface", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } else { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceRstpInterfaceConfigCreate(), - }, - { - Config: testAccResourceRstpInterfaceConfigUpdate(), - }, - { - ResourceName: "junos_rstp_interface.all", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceRstpInterfaceSWConfigCreate() string { - return ` -resource "junos_rstp_interface" "all" { - name = "all" -} - -resource "junos_routing_instance" "testacc_rstp_interface" { - name = "testacc_rstp_intface" - type = "virtual-switch" -} - -resource "junos_rstp_interface" "all2" { - name = "all" - routing_instance = junos_routing_instance.testacc_rstp_interface.name -} -` -} - -func testAccResourceRstpInterfaceSWConfigUpdate(interFace string) string { - return fmt.Sprintf(` -resource "junos_rstp_interface" "all" { - name = "all" - access_trunk = true - bpdu_timeout_action_alarm = true - bpdu_timeout_action_block = true - cost = 16 - edge = true - mode = "shared" - priority = 240 -} - -resource "junos_interface_physical" "testacc_rstp_interface" { - name = "%s" - vlan_members = ["default"] -} - -resource "junos_rstp_interface" "testacc_rstp_interface" { - name = junos_interface_physical.testacc_rstp_interface.name - no_root_port = true -} - -resource "junos_routing_instance" "testacc_rstp_interface" { - name = "testacc_rstp_interface" - type = "virtual-switch" -} - -resource "junos_rstp_interface" "all2" { - name = "all" - routing_instance = junos_routing_instance.testacc_rstp_interface.name - access_trunk = true - bpdu_timeout_action_alarm = true - bpdu_timeout_action_block = true - cost = 16 - edge = true - mode = "shared" - priority = 240 -} -`, interFace) -} - -func testAccResourceRstpInterfaceConfigCreate() string { - return ` -resource "junos_rstp_interface" "all" { - name = "all" -} -` -} - -func testAccResourceRstpInterfaceConfigUpdate() string { - return ` -resource "junos_rstp_interface" "all" { - name = "all" - access_trunk = true - bpdu_timeout_action_alarm = true - bpdu_timeout_action_block = true - cost = 16 - edge = true - mode = "shared" - priority = 240 -} -` -} From 3f304718076e2b85319989cd51bc6ce4fe185221 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 4 Jun 2024 08:58:55 +0200 Subject: [PATCH 11/30] r/vstp: use new provider via framework --- .changes/vstp-with-fwk.md | 6 + docs/resources/vstp.md | 4 +- internal/providerfwk/provider.go | 1 + internal/providerfwk/resource_vstp.go | 549 ++++++++++++++++++ internal/providerfwk/resource_vstp_test.go | 31 + .../TestAccResourceVstp_basic/1/main.tf | 13 + .../TestAccResourceVstp_basic/2/main.tf | 21 + internal/providersdk/provider.go | 1 - internal/providersdk/resource_vstp.go | 445 -------------- internal/providersdk/resource_vstp_test.go | 74 --- 10 files changed, 623 insertions(+), 522 deletions(-) create mode 100644 .changes/vstp-with-fwk.md create mode 100644 internal/providerfwk/resource_vstp.go create mode 100644 internal/providerfwk/resource_vstp_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceVstp_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstp_basic/2/main.tf delete mode 100644 internal/providersdk/resource_vstp.go delete mode 100644 internal/providersdk/resource_vstp_test.go diff --git a/.changes/vstp-with-fwk.md b/.changes/vstp-with-fwk.md new file mode 100644 index 00000000..98231e47 --- /dev/null +++ b/.changes/vstp-with-fwk.md @@ -0,0 +1,6 @@ + +ENHANCEMENTS: + +* **resource/junos_vstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* diff --git a/docs/resources/vstp.md b/docs/resources/vstp.md index ee0e5edc..34542732 100644 --- a/docs/resources/vstp.md +++ b/docs/resources/vstp.md @@ -28,11 +28,11 @@ The following arguments are supported: Need to be `default` (for root level) or the name of routing instance. Defaults to `default`. - **bpdu_block_on_edge** (Optional, Boolean) - Block BPDU on all interfaces configured as edge (BPDU Protect) + Block BPDU on all interfaces configured as edge (BPDU Protect). - **disable** (Optional, Boolean) Disable STP. - **force_version_stp** (Optional, Boolean) - Force protocol version stp. + Force protocol version STP. - **priority_hold_time** (Optional, Number) Hold time before switching to primary priority when core domain becomes up (1..255 seconds). - **system_id** (Optional, Block Set) diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index 99338ced..a0f4eeab 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -301,6 +301,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newSystemTacplusServerResource, newVirtualChassisResource, newVlanResource, + newVstpResource, } } diff --git a/internal/providerfwk/resource_vstp.go b/internal/providerfwk/resource_vstp.go new file mode 100644 index 00000000..9607c962 --- /dev/null +++ b/internal/providerfwk/resource_vstp.go @@ -0,0 +1,549 @@ +package providerfwk + +import ( + "context" + "fmt" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/stringvalidator" + "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/stringdefault" + "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 = &vstp{} + _ resource.ResourceWithConfigure = &vstp{} + _ resource.ResourceWithValidateConfig = &vstp{} + _ resource.ResourceWithImportState = &vstp{} +) + +type vstp struct { + client *junos.Client +} + +func newVstpResource() resource.Resource { + return &vstp{} +} + +func (rsc *vstp) typeName() string { + return providerName + "_vstp" +} + +func (rsc *vstp) junosName() string { + return "protocols vstp" +} + +func (rsc *vstp) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *vstp) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *vstp) 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 *vstp) 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 format ``.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for vstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + + "bpdu_block_on_edge": schema.BoolAttribute{ + Optional: true, + Description: "Block BPDU on all interfaces configured as edge (BPDU Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "disable": schema.BoolAttribute{ + Optional: true, + Description: "Disable STP.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "force_version_stp": schema.BoolAttribute{ + Optional: true, + Description: "Force protocol version STP.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "priority_hold_time": schema.Int64Attribute{ + Optional: true, + Description: "Hold time before switching to primary priority when core domain becomes up (seconds).", + Validators: []validator.Int64{ + int64validator.Between(1, 255), + }, + }, + "vpls_flush_on_topology_change": schema.BoolAttribute{ + Optional: true, + Description: "Enable VPLS MAC flush on root protected CE interface receiving topology change.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "system_id": schema.SetNestedBlock{ + Description: "For each ID, System ID to IP mapping.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Required: true, + Description: "System ID.", + Validators: []validator.String{ + tfvalidator.StringMACAddress().WithMac48ColonHexa(), + }, + }, + "ip_address": schema.StringAttribute{ + Optional: true, + Description: "Peer ID (IP Address).", + Validators: []validator.String{ + tfvalidator.StringCIDR(), + }, + }, + }, + }, + }, + }, + } +} + +type vstpData struct { + ID types.String `tfsdk:"id"` + RoutingInstance types.String `tfsdk:"routing_instance"` + BpduBlockOnEdge types.Bool `tfsdk:"bpdu_block_on_edge"` + Disable types.Bool `tfsdk:"disable"` + ForceVersionStp types.Bool `tfsdk:"force_version_stp"` + PriorityHoldTime types.Int64 `tfsdk:"priority_hold_time"` + VplsFlushOnTopologyChange types.Bool `tfsdk:"vpls_flush_on_topology_change"` + SystemID []vstpBlockSystemID `tfsdk:"system_id"` +} + +type vstpConfig struct { + ID types.String `tfsdk:"id"` + RoutingInstance types.String `tfsdk:"routing_instance"` + BpduBlockOnEdge types.Bool `tfsdk:"bpdu_block_on_edge"` + Disable types.Bool `tfsdk:"disable"` + ForceVersionStp types.Bool `tfsdk:"force_version_stp"` + PriorityHoldTime types.Int64 `tfsdk:"priority_hold_time"` + VplsFlushOnTopologyChange types.Bool `tfsdk:"vpls_flush_on_topology_change"` + SystemID types.Set `tfsdk:"system_id"` +} + +type vstpBlockSystemID struct { + ID types.String `tfsdk:"id"` + IPAddress types.String `tfsdk:"ip_address"` +} + +func (rsc *vstp) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config vstpConfig + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.SystemID.IsNull() && !config.SystemID.IsUnknown() { + var systemID []vstpBlockSystemID + asDiags := config.SystemID.ElementsAs(ctx, &systemID, false) + if asDiags.HasError() { + resp.Diagnostics.Append(asDiags...) + + return + } + systemIDID := make(map[string]struct{}) + for _, block := range systemID { + if block.ID.IsUnknown() { + continue + } + id := block.ID.ValueString() + if _, ok := systemIDID[id]; ok { + resp.Diagnostics.AddAttributeError( + path.Root("system_id"), + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf("multiple system_id blocks with the same id %q", id), + ) + } + systemIDID[id] = struct{}{} + } + } +} + +func (rsc *vstp) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan vstpData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + + return true + }, + nil, + &plan, + resp, + ) +} + +func (rsc *vstp) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data vstpData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + junSess, err := rsc.junosClient().StartNewSession(ctx) + if err != nil { + resp.Diagnostics.AddError(tfdiag.StartSessErrSummary, err.Error()) + + return + } + defer junSess.Close() + + junos.MutexLock() + if v := state.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(ctx, v, junSess) + if err != nil { + junos.MutexUnlock() + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if !instanceExists { + junos.MutexUnlock() + resp.State.RemoveResource(ctx) + + return + } + } + + err = data.read(ctx, state.RoutingInstance.ValueString(), junSess) + junos.MutexUnlock() + if err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + + if data.nullID() { + resp.State.RemoveResource(ctx) + + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (rsc *vstp) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state vstpData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *vstp) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state vstpData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *vstp) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + junSess, err := rsc.junosClient().StartNewSession(ctx) + if err != nil { + resp.Diagnostics.AddError(tfdiag.StartSessErrSummary, err.Error()) + + return + } + defer junSess.Close() + + if req.ID != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(ctx, req.ID, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if !instanceExists { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", req.ID), + ) + + return + } + } + + var data vstpData + if err := data.read(ctx, req.ID, junSess); err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + if data.nullID() { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceImportDontFindMessage(rsc, req.ID)+ + " (id must be )", + ) + + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (rscData *vstpData) fillID() { + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(v) + } else { + rscData.ID = types.StringValue(junos.DefaultW) + } +} + +func (rscData *vstpData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *vstpData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + configSet := make([]string, 0) + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols vstp " + + if rscData.BpduBlockOnEdge.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-block-on-edge") + } + if rscData.Disable.ValueBool() { + configSet = append(configSet, setPrefix+"disable") + } + if rscData.ForceVersionStp.ValueBool() { + configSet = append(configSet, setPrefix+"force-version stp") + } + + if !rscData.PriorityHoldTime.IsNull() { + configSet = append(configSet, setPrefix+"priority-hold-time "+ + utils.ConvI64toa(rscData.PriorityHoldTime.ValueInt64())) + } + if rscData.VplsFlushOnTopologyChange.ValueBool() { + configSet = append(configSet, setPrefix+"vpls-flush-on-topology-change") + } + systemIDID := make(map[string]struct{}) + for _, block := range rscData.SystemID { + id := block.ID.ValueString() + if _, ok := systemIDID[id]; ok { + return path.Root("system_id"), + fmt.Errorf("multiple system_id blocks with the same id %q", id) + } + systemIDID[id] = struct{}{} + + configSet = append(configSet, setPrefix+"system-id "+id) + if v := block.IPAddress.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"system-id "+id+" ip-address "+v) + } + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *vstpData) read( + _ context.Context, routingInstance string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols vstp" + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + rscData.fillID() + if showConfig != junos.EmptyW { + 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) + switch { + case itemTrim == "bpdu-block-on-edge": + rscData.BpduBlockOnEdge = types.BoolValue(true) + case itemTrim == junos.DisableW: + rscData.Disable = types.BoolValue(true) + case itemTrim == "force-version stp": + rscData.ForceVersionStp = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "priority-hold-time "): + rscData.PriorityHoldTime, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case itemTrim == "vpls-flush-on-topology-change": + rscData.VplsFlushOnTopologyChange = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "system-id "): + itemTrimFields := strings.Split(itemTrim, " ") + switch len(itemTrimFields) { // (ip-address )? + case 1: + rscData.SystemID = append(rscData.SystemID, vstpBlockSystemID{ + ID: types.StringValue(itemTrimFields[0]), + }) + case 3: + rscData.SystemID = append(rscData.SystemID, vstpBlockSystemID{ + ID: types.StringValue(itemTrimFields[0]), + IPAddress: types.StringValue(itemTrimFields[2]), + }) + default: + return fmt.Errorf(junos.CantReadValuesNotEnoughFields, "system-id", itemTrim) + } + } + } + } + + return nil +} + +func (rscData *vstpData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + delPrefix += "protocols vstp " + + listLinesToDelete := []string{ + "bpdu-block-on-edge", + "disable", + "force-version", + "priority-hold-time", + "vpls-flush-on-topology-change", + } + + configSet := make([]string, len(listLinesToDelete), len(listLinesToDelete)+len(rscData.SystemID)) + for k, line := range listLinesToDelete { + configSet[k] = delPrefix + line + } + for _, block := range rscData.SystemID { + configSet = append(configSet, delPrefix+"system-id "+block.ID.ValueString()) + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_vstp_test.go b/internal/providerfwk/resource_vstp_test.go new file mode 100644 index 00000000..7b45e435 --- /dev/null +++ b/internal/providerfwk/resource_vstp_test.go @@ -0,0 +1,31 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceVstp_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_vstp.testacc_ri_vstp", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstp_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceVstp_basic/1/main.tf new file mode 100644 index 00000000..65bb9d66 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstp_basic/1/main.tf @@ -0,0 +1,13 @@ +resource "junos_vstp" "testacc_vstp" { + bpdu_block_on_edge = true +} +resource "junos_routing_instance" "testacc_vstp" { + name = "testacc_vstp" + type = "virtual-switch" +} +resource "junos_vstp" "testacc_ri_vstp" { + routing_instance = junos_routing_instance.testacc_vstp.name + system_id { + id = "00:11:22:33:44:56" + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstp_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceVstp_basic/2/main.tf new file mode 100644 index 00000000..96656b21 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstp_basic/2/main.tf @@ -0,0 +1,21 @@ +resource "junos_vstp" "testacc_vstp" { + disable = true +} +resource "junos_routing_instance" "testacc_vstp" { + name = "testacc_vstp" + type = "virtual-switch" +} +resource "junos_vstp" "testacc_ri_vstp" { + routing_instance = junos_routing_instance.testacc_vstp.name + bpdu_block_on_edge = true + force_version_stp = true + priority_hold_time = 10 + system_id { + id = "00:11:22:33:44:55" + } + system_id { + id = "00:22:33:44:55:aa" + ip_address = "192.0.2.4/31" + } + vpls_flush_on_topology_change = true +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index 85553727..d0600da5 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -229,7 +229,6 @@ func Provider() *schema.Provider { "junos_system_root_authentication": resourceSystemRootAuthentication(), "junos_system_services_dhcp_localserver_group": resourceSystemServicesDhcpLocalServerGroup(), - "junos_vstp": resourceVstp(), "junos_vstp_interface": resourceVstpInterface(), "junos_vstp_vlan": resourceVstpVlan(), "junos_vstp_vlan_group": resourceVstpVlanGroup(), diff --git a/internal/providersdk/resource_vstp.go b/internal/providersdk/resource_vstp.go deleted file mode 100644 index 55983d6f..00000000 --- a/internal/providersdk/resource_vstp.go +++ /dev/null @@ -1,445 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "slices" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type vstpOptions struct { - bpduBlockOnEdge bool - disable bool - forceVersionStp bool - vplsFlushOnTopologyChange bool - priorityHoldTime int - routingInstance string - systemID []map[string]interface{} -} - -func resourceVstp() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceVstpCreate, - ReadWithoutTimeout: resourceVstpRead, - UpdateWithoutTimeout: resourceVstpUpdate, - DeleteWithoutTimeout: resourceVstpDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceVstpImport, - }, - Schema: map[string]*schema.Schema{ - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "bpdu_block_on_edge": { - Type: schema.TypeBool, - Optional: true, - }, - "disable": { - Type: schema.TypeBool, - Optional: true, - }, - "force_version_stp": { - Type: schema.TypeBool, - Optional: true, - }, - "priority_hold_time": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 255), - }, - "system_id": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.IsMACAddress, - }, - "ip_address": { - Type: schema.TypeString, - Optional: true, - Default: "", - ValidateFunc: validation.IsCIDR, - }, - }, - }, - }, - "vpls_flush_on_topology_change": { - Type: schema.TypeBool, - Optional: true, - }, - }, - } -} - -func resourceVstpCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setVstp(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(d.Get("routing_instance").(string)) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if d.Get("routing_instance").(string) != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) - } - } - if err := setVstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_vstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.SetId(d.Get("routing_instance").(string)) - - return append(diagWarns, resourceVstpReadWJunSess(d, junSess)...) -} - -func resourceVstpRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceVstpReadWJunSess(d, junSess) -} - -func resourceVstpReadWJunSess(d *schema.ResourceData, junSess *junos.Session) diag.Diagnostics { - junos.MutexLock() - if d.Get("routing_instance").(string) != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), junSess) - if err != nil { - junos.MutexUnlock() - - return diag.FromErr(err) - } - if !instanceExists { - junos.MutexUnlock() - d.SetId("") - - return nil - } - } - vstpOptions, err := readVstp(d.Get("routing_instance").(string), junSess) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - fillVstpData(d, vstpOptions) - - return nil -} - -func resourceVstpUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstp(d, junSess); err != nil { - return diag.FromErr(err) - } - if err := setVstp(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setVstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_vstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceVstpReadWJunSess(d, junSess)...) -} - -func resourceVstpDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstp(d, junSess); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstp(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_vstp") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceVstpImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - if d.Id() != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(d.Id(), junSess) - if err != nil { - return nil, err - } - if !instanceExists { - return nil, fmt.Errorf("routing instance %v doesn't exist", d.Id()) - } - } - result := make([]*schema.ResourceData, 1) - vstpOptions, err := readVstp(d.Id(), junSess) - if err != nil { - return nil, err - } - fillVstpData(d, vstpOptions) - result[0] = d - - return result, nil -} - -func setVstp(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - setPrefix := junos.SetLS - if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " - } - setPrefix += "protocols vstp " - - if d.Get("bpdu_block_on_edge").(bool) { - configSet = append(configSet, setPrefix+"bpdu-block-on-edge") - } - if d.Get("disable").(bool) { - configSet = append(configSet, setPrefix+"disable") - } - if d.Get("force_version_stp").(bool) { - configSet = append(configSet, setPrefix+"force-version stp") - } - if v := d.Get("priority_hold_time").(int); v != 0 { - configSet = append(configSet, setPrefix+"priority-hold-time "+strconv.Itoa(v)) - } - systemIDList := make([]string, 0) - for _, mSysID := range d.Get("system_id").(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - if slices.Contains(systemIDList, systemID["id"].(string)) { - return fmt.Errorf("multiple blocks system_id with the same id '%s'", systemID["id"].(string)) - } - systemIDList = append(systemIDList, systemID["id"].(string)) - configSet = append(configSet, setPrefix+"system-id "+systemID["id"].(string)) - if ipAdd := systemID["ip_address"].(string); ipAdd != "" { - configSet = append(configSet, setPrefix+"system-id "+systemID["id"].(string)+" ip-address "+ipAdd) - } - } - if d.Get("vpls_flush_on_topology_change").(bool) { - configSet = append(configSet, setPrefix+"vpls-flush-on-topology-change") - } - - return junSess.ConfigSet(configSet) -} - -func readVstp(routingInstance string, junSess *junos.Session, -) (confRead vstpOptions, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp" + junos.PipeDisplaySetRelative) - if err != nil { - return confRead, err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp" + junos.PipeDisplaySetRelative) - if err != nil { - return confRead, err - } - } - confRead.routingInstance = routingInstance - if showConfig != junos.EmptyW { - 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) - switch { - case itemTrim == "bpdu-block-on-edge": - confRead.bpduBlockOnEdge = true - case itemTrim == junos.DisableW: - confRead.disable = true - case itemTrim == "force-version stp": - confRead.forceVersionStp = true - case balt.CutPrefixInString(&itemTrim, "priority-hold-time "): - confRead.priorityHoldTime, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "system-id "): - itemTrimFields := strings.Split(itemTrim, " ") - switch len(itemTrimFields) { // (ip-address )? - case 1: - confRead.systemID = append(confRead.systemID, map[string]interface{}{ - "id": itemTrimFields[0], - "ip_address": "", - }) - case 3: - confRead.systemID = append(confRead.systemID, map[string]interface{}{ - "id": itemTrimFields[0], - "ip_address": itemTrimFields[2], - }) - default: - return confRead, fmt.Errorf(junos.CantReadValuesNotEnoughFields, "system-id", itemTrim) - } - case itemTrim == "vpls-flush-on-topology-change": - confRead.vplsFlushOnTopologyChange = true - } - } - } - - return confRead, nil -} - -func delVstp(d *schema.ResourceData, junSess *junos.Session) error { - delPrefix := junos.DeleteLS - if d.Get("routing_instance").(string) != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + d.Get("routing_instance").(string) + " " - } - delPrefix += "protocols vstp " - - listLinesToDelete := []string{ - "bpdu-block-on-edge", - "disable", - "force-version", - "priority-hold-time", - "vpls-flush-on-topology-change", - } - configSet := make([]string, - len(listLinesToDelete), len(listLinesToDelete)+len(d.Get("system_id").(*schema.Set).List())) - for k, line := range listLinesToDelete { - configSet[k] = delPrefix + line - } - if d.HasChange("system_id") { - oSysID, _ := d.GetChange("system_id") - for _, mSysID := range oSysID.(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - configSet = append(configSet, delPrefix+"system-id "+systemID["id"].(string)) - } - } else { - for _, mSysID := range d.Get("system_id").(*schema.Set).List() { - systemID := mSysID.(map[string]interface{}) - configSet = append(configSet, delPrefix+"system-id "+systemID["id"].(string)) - } - } - - return junSess.ConfigSet(configSet) -} - -func fillVstpData(d *schema.ResourceData, vstpOptions vstpOptions) { - if tfErr := d.Set("routing_instance", vstpOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_block_on_edge", vstpOptions.bpduBlockOnEdge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("disable", vstpOptions.disable); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("force_version_stp", vstpOptions.forceVersionStp); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("priority_hold_time", vstpOptions.priorityHoldTime); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("system_id", vstpOptions.systemID); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("vpls_flush_on_topology_change", vstpOptions.vplsFlushOnTopologyChange); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_vstp_test.go b/internal/providersdk/resource_vstp_test.go deleted file mode 100644 index f72bf6d3..00000000 --- a/internal/providersdk/resource_vstp_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package providersdk_test - -import ( - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccResourceVstp_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceVstpSWConfigCreate(), - }, - { - Config: testAccResourceVstpSWConfigUpdate(), - }, - { - ResourceName: "junos_vstp.testacc_ri_vstp", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceVstpSWConfigCreate() string { - return ` -resource "junos_vstp" "testacc_vstp" { - bpdu_block_on_edge = true -} -resource "junos_routing_instance" "testacc_vstp" { - name = "testacc_vstp" - type = "virtual-switch" -} -resource "junos_vstp" "testacc_ri_vstp" { - routing_instance = junos_routing_instance.testacc_vstp.name - system_id { - id = "00:11:22:33:44:56" - } -} -` -} - -func testAccResourceVstpSWConfigUpdate() string { - return ` -resource "junos_vstp" "testacc_vstp" { - disable = true -} -resource "junos_routing_instance" "testacc_vstp" { - name = "testacc_vstp" - type = "virtual-switch" -} -resource "junos_vstp" "testacc_ri_vstp" { - routing_instance = junos_routing_instance.testacc_vstp.name - bpdu_block_on_edge = true - force_version_stp = true - priority_hold_time = 10 - system_id { - id = "00:11:22:33:44:55" - } - system_id { - id = "00:22:33:44:55:aa" - ip_address = "192.0.2.4/31" - } - vpls_flush_on_topology_change = true -} -` -} From 2dc157912b0b4ff02bf1adae809a50dae71fe018 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:29:15 +0000 Subject: [PATCH 12/30] deps: bump golang.org/x/crypto from 0.23.0 to 0.24.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.23.0 to 0.24.0. - [Commits](https://github.com/golang/crypto/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 13 +++++++------ go.sum | 32 ++++++++++++++++---------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index ef45f698..af9402b5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/jeremmfr/go-netconf v0.5.0 github.com/jeremmfr/go-utils v0.12.0 github.com/jeremmfr/junosdecode v1.1.1 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 ) require ( @@ -53,11 +53,12 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect diff --git a/go.sum b/go.sum index dd7060de..2d4fe315 100644 --- a/go.sum +++ b/go.sum @@ -157,22 +157,22 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY3 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -185,24 +185,24 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= From 74ffea01cf2d3387f4808f972e41002bb2747f94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:29:21 +0000 Subject: [PATCH 13/30] deps: bump github.com/hashicorp/terraform-plugin-framework Bumps [github.com/hashicorp/terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-framework/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-framework/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-framework/compare/v1.8.0...v1.9.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-framework dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ef45f698..c6ad9053 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21.0 require ( github.com/google/go-cmp v0.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 - github.com/hashicorp/terraform-plugin-framework v1.8.0 + github.com/hashicorp/terraform-plugin-framework v1.9.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 diff --git a/go.sum b/go.sum index dd7060de..ed7c2c0b 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,8 @@ github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVW github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec= github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= -github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer5AEvJnDuXomBgou6fD8kI= -github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY= +github.com/hashicorp/terraform-plugin-framework v1.9.0 h1:caLcDoxiRucNi2hk8+j3kJwkKfvHznubyFsJMWfZqKU= +github.com/hashicorp/terraform-plugin-framework v1.9.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= From df29a1701a72a046bbc732afb0d5ed00f632e397 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 5 Jun 2024 16:12:03 +0200 Subject: [PATCH 14/30] r/vstp_vlan: use new provider via framework --- .changes/vstp-with-fwk.md | 3 + docs/resources/vstp_vlan.md | 4 +- internal/providerfwk/provider.go | 1 + internal/providerfwk/resource_vstp_vlan.go | 595 ++++++++++++++++++ .../providerfwk/resource_vstp_vlan_test.go | 31 + .../TestAccResourceVstpVlan_basic/1/main.tf | 20 + .../TestAccResourceVstpVlan_basic/2/main.tf | 26 + .../providersdk/func_resource_vstp_vlan.go | 23 + internal/providersdk/provider.go | 1 - internal/providersdk/resource_vstp_vlan.go | 479 -------------- .../providersdk/resource_vstp_vlan_test.go | 86 --- 11 files changed, 701 insertions(+), 568 deletions(-) create mode 100644 internal/providerfwk/resource_vstp_vlan.go create mode 100644 internal/providerfwk/resource_vstp_vlan_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/2/main.tf create mode 100644 internal/providersdk/func_resource_vstp_vlan.go delete mode 100644 internal/providersdk/resource_vstp_vlan.go delete mode 100644 internal/providersdk/resource_vstp_vlan_test.go diff --git a/.changes/vstp-with-fwk.md b/.changes/vstp-with-fwk.md index 98231e47..e7f94a3b 100644 --- a/.changes/vstp-with-fwk.md +++ b/.changes/vstp-with-fwk.md @@ -4,3 +4,6 @@ ENHANCEMENTS: * **resource/junos_vstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) some of config errors are now sent during Plan instead of during Apply optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_vlan**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* diff --git a/docs/resources/vstp_vlan.md b/docs/resources/vstp_vlan.md index d60d3439..98659a52 100644 --- a/docs/resources/vstp_vlan.md +++ b/docs/resources/vstp_vlan.md @@ -25,9 +25,9 @@ The following arguments are supported: Need to be `default` (for root level) or name of routing instance. Defaults to `default`. - **backup_bridge_priority** (Optional, String) - Priority of the bridge (in increments of 4k - 4k,8k,..60k) (4096..61440). + Priority of the bridge (in increments of 4k - 4k,8k,..60k). - **bridge_priority** (Optional, String) - Priority of the bridge (in increments of 4k - 0,4k,8k,..60k) (0..61440). + Priority of the bridge (in increments of 4k - 0,4k,8k,..60k). - **forward_delay** (Optional, Number) Time spent in listening or learning state (4..30 seconds). - **hello_time** (Optional, Number) diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index a0f4eeab..856da2ee 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -302,6 +302,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newVirtualChassisResource, newVlanResource, newVstpResource, + newVstpVlanResource, } } diff --git a/internal/providerfwk/resource_vstp_vlan.go b/internal/providerfwk/resource_vstp_vlan.go new file mode 100644 index 00000000..963cf691 --- /dev/null +++ b/internal/providerfwk/resource_vstp_vlan.go @@ -0,0 +1,595 @@ +package providerfwk + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/stringvalidator" + "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/stringdefault" + "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 = &vstpVlan{} + _ resource.ResourceWithConfigure = &vstpVlan{} + _ resource.ResourceWithValidateConfig = &vstpVlan{} + _ resource.ResourceWithImportState = &vstpVlan{} +) + +type vstpVlan struct { + client *junos.Client +} + +func newVstpVlanResource() resource.Resource { + return &vstpVlan{} +} + +func (rsc *vstpVlan) typeName() string { + return providerName + "_vstp_vlan" +} + +func (rsc *vstpVlan) junosName() string { + return "vstp vlan" +} + +func (rsc *vstpVlan) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *vstpVlan) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *vstpVlan) 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 *vstpVlan) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: defaultResourceSchemaDescription(rsc), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "An identifier for the resource with format " + + "`" + junos.IDSeparator + "`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "vlan_id": schema.StringAttribute{ + Required: true, + Description: "VLAN id or `all`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^(409[0-4]|(40[0-8]|[1-3]\d\d|[1-9]\d|[1-9])\d|[1-9]|all)$`), + "must be a VLAN id (1..4094) or all"), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for vstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "backup_bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^\d\d?k$`), + "must be a number with increments of 4k - 4k,8k,..60k", + ), + }, + }, + "bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^(0|\d\d?k)$`), + "must be a number with increments of 4k - 0,4k,8k,..60k", + ), + }, + }, + "forward_delay": schema.Int64Attribute{ + Optional: true, + Description: "Time spent in listening or learning state (seconds).", + Validators: []validator.Int64{ + int64validator.Between(4, 30), + }, + }, + "hello_time": schema.Int64Attribute{ + Optional: true, + Description: "Time interval between configuration BPDUs (seconds).", + Validators: []validator.Int64{ + int64validator.Between(1, 10), + }, + }, + "max_age": schema.Int64Attribute{ + Optional: true, + Description: "Maximum age of received protocol bpdu (seconds).", + Validators: []validator.Int64{ + int64validator.Between(6, 40), + }, + }, + "system_identifier": schema.StringAttribute{ + Optional: true, + Description: "System identifier to represent this node.", + Validators: []validator.String{ + tfvalidator.StringMACAddress().WithMac48ColonHexa(), + }, + }, + }, + } +} + +type vstpVlanData struct { + ID types.String `tfsdk:"id"` + VlanID types.String `tfsdk:"vlan_id"` + RoutingInstance types.String `tfsdk:"routing_instance"` + BackupBridgePriority types.String `tfsdk:"backup_bridge_priority"` + BridgePriority types.String `tfsdk:"bridge_priority"` + ForwardDelay types.Int64 `tfsdk:"forward_delay"` + HelloTime types.Int64 `tfsdk:"hello_time"` + MaxAge types.Int64 `tfsdk:"max_age"` + SystemIdentifier types.String `tfsdk:"system_identifier"` +} + +func (rsc *vstpVlan) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config vstpVlanData + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.BackupBridgePriority.IsNull() && !config.BackupBridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BackupBridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be a multiple of 4k", + ) + } + if v < 4 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be between 4k and 60k", + ) + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if bridgePriority, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v <= bridgePriority { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be worse (higher value) than bridge_priority", + ) + } + } + } + } + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be a multiple of 4k", + ) + } + if v < 0 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be between 0 and 60k", + ) + } + } + } +} + +func (rsc *vstpVlan) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan vstpVlanData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.VlanID.ValueString() == "" { + resp.Diagnostics.AddAttributeError( + path.Root("vlan_id"), + "Empty Vlan ID", + defaultResourceCouldNotCreateWithEmptyMessage(rsc, "vlan_id"), + ) + + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + vlanExists, err := checkVstpVlanExists( + fnCtx, + plan.VlanID.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if vlanExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsInRoutingInstanceMessage(rsc, plan.VlanID, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsMessage(rsc, plan.VlanID), + ) + } + + return false + } + + return true + }, + func(fnCtx context.Context, junSess *junos.Session) bool { + vlanExists, err := checkVstpVlanExists( + fnCtx, + plan.VlanID.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PostCheckErrSummary, err.Error()) + + return false + } + if !vlanExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsInRoutingInstanceAfterCommitMessage(rsc, plan.VlanID, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsAfterCommitMessage(rsc, plan.VlanID), + ) + } + + return false + } + + return true + }, + &plan, + resp, + ) +} + +func (rsc *vstpVlan) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data vstpVlanData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataReadFrom2String = &data + defaultResourceRead( + ctx, + rsc, + []string{ + state.VlanID.ValueString(), + state.RoutingInstance.ValueString(), + }, + &data, + nil, + resp, + ) +} + +func (rsc *vstpVlan) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state vstpVlanData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataDelWithOpts = &state + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *vstpVlan) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state vstpVlanData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *vstpVlan) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + var data vstpVlanData + + var _ resourceDataReadFrom2String = &data + defaultResourceImportState( + ctx, + rsc, + &data, + req, + resp, + defaultResourceImportDontFindMessage(rsc, req.ID)+ + " (id must be "+junos.IDSeparator+")", + ) +} + +func checkVstpVlanExists( + _ context.Context, vlanID, routingInstance string, junSess *junos.Session, +) ( + bool, error, +) { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} + +func (rscData *vstpVlanData) fillID() { + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(rscData.VlanID.ValueString() + junos.IDSeparator + v) + } else { + rscData.ID = types.StringValue(rscData.VlanID.ValueString() + junos.IDSeparator + junos.DefaultW) + } +} + +func (rscData *vstpVlanData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *vstpVlanData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols vstp vlan " + rscData.VlanID.ValueString() + " " + + configSet := []string{ + setPrefix, + } + + if v := rscData.BackupBridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) + } + if v := rscData.BridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"bridge-priority "+v) + } + if !rscData.ForwardDelay.IsNull() { + configSet = append(configSet, setPrefix+"forward-delay "+ + utils.ConvI64toa(rscData.ForwardDelay.ValueInt64())) + } + if !rscData.HelloTime.IsNull() { + configSet = append(configSet, setPrefix+"hello-time "+ + utils.ConvI64toa(rscData.HelloTime.ValueInt64())) + } + if !rscData.MaxAge.IsNull() { + configSet = append(configSet, setPrefix+"max-age "+ + utils.ConvI64toa(rscData.MaxAge.ValueInt64())) + } + if v := rscData.SystemIdentifier.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"system-identifier "+v) + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *vstpVlanData) read( + _ context.Context, vlanID, routingInstance string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols vstp vlan " + vlanID + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if showConfig != junos.EmptyW { + rscData.VlanID = types.StringValue(vlanID) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + 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) + switch { + case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): + rscData.BackupBridgePriority = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "bridge-priority "): + rscData.BridgePriority = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "forward-delay "): + rscData.ForwardDelay, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "hello-time "): + rscData.HelloTime, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "max-age "): + rscData.MaxAge, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "system-identifier "): + rscData.SystemIdentifier = types.StringValue(itemTrim) + } + } + } + + return nil +} + +func (rscData *vstpVlanData) delOpts( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + delPrefix += "protocols vstp vlan " + rscData.VlanID.ValueString() + " " + + listLinesToDelete := []string{ + "backup-bridge-priority", + "bridge-priority", + "forward-delay", + "hello-time", + "max-age", + "system-identifier", + } + + configSet := make([]string, len(listLinesToDelete)) + for k, line := range listLinesToDelete { + configSet[k] = delPrefix + line + } + + return junSess.ConfigSet(configSet) +} + +func (rscData *vstpVlanData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "protocols vstp vlan " + rscData.VlanID.ValueString(), + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_vstp_vlan_test.go b/internal/providerfwk/resource_vstp_vlan_test.go new file mode 100644 index 00000000..cc541a67 --- /dev/null +++ b/internal/providerfwk/resource_vstp_vlan_test.go @@ -0,0 +1,31 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceVstpVlan_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_vstp_vlan.testacc_ri_vstp_vlan", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/1/main.tf new file mode 100644 index 00000000..358e50fe --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/1/main.tf @@ -0,0 +1,20 @@ +resource "junos_vstp_vlan" "testacc_vstp_vlan" { + vlan_id = "10" +} +resource "junos_vstp_vlan" "testacc_vstp_vlan_all" { + vlan_id = "all" +} +resource "junos_routing_instance" "testacc_vstp_vlan" { + name = "testacc_vstp_vlan" + type = "virtual-switch" +} +resource "junos_vstp_vlan" "testacc_ri_vstp_vlan" { + routing_instance = junos_routing_instance.testacc_vstp_vlan.name + vlan_id = "11" + bridge_priority = "16k" +} +resource "junos_vstp_vlan" "testacc_ri_vstp_vlan_all" { + routing_instance = junos_routing_instance.testacc_vstp_vlan.name + vlan_id = "all" + system_identifier = "00:aa:bc:ed:ff:11" +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/2/main.tf new file mode 100644 index 00000000..31a322e4 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpVlan_basic/2/main.tf @@ -0,0 +1,26 @@ +resource "junos_vstp_vlan" "testacc_vstp_vlan" { + vlan_id = "10" + backup_bridge_priority = "8k" + bridge_priority = "4k" +} +resource "junos_vstp_vlan" "testacc_vstp_vlan_all" { + vlan_id = "all" + hello_time = 2 +} +resource "junos_routing_instance" "testacc_vstp_vlan" { + name = "testacc_vstp_vlan" + type = "virtual-switch" +} +resource "junos_vstp_vlan" "testacc_ri_vstp_vlan" { + routing_instance = junos_routing_instance.testacc_vstp_vlan.name + vlan_id = "11" + backup_bridge_priority = "20k" + bridge_priority = 0 + forward_delay = 22 + hello_time = 3 + max_age = 24 +} +resource "junos_vstp_vlan" "testacc_ri_vstp_vlan_all" { + routing_instance = junos_routing_instance.testacc_vstp_vlan.name + vlan_id = "all" +} diff --git a/internal/providersdk/func_resource_vstp_vlan.go b/internal/providersdk/func_resource_vstp_vlan.go new file mode 100644 index 00000000..8b96f1ef --- /dev/null +++ b/internal/providersdk/func_resource_vstp_vlan.go @@ -0,0 +1,23 @@ +package providersdk + +import "github.com/jeremmfr/terraform-provider-junos/internal/junos" + +func checkVstpVlanExists(vlanID, routingInstance string, junSess *junos.Session, +) (_ bool, err error) { + var showConfig string + if routingInstance == junos.DefaultW { + showConfig, err = junSess.Command(junos.CmdShowConfig + + "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) + } else { + showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + + "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) + } + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index d0600da5..f66f84d2 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -230,7 +230,6 @@ func Provider() *schema.Provider { "junos_system_services_dhcp_localserver_group": resourceSystemServicesDhcpLocalServerGroup(), "junos_vstp_interface": resourceVstpInterface(), - "junos_vstp_vlan": resourceVstpVlan(), "junos_vstp_vlan_group": resourceVstpVlanGroup(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/internal/providersdk/resource_vstp_vlan.go b/internal/providersdk/resource_vstp_vlan.go deleted file mode 100644 index 9cbcd625..00000000 --- a/internal/providersdk/resource_vstp_vlan.go +++ /dev/null @@ -1,479 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type vstpVlanOptions struct { - forwardDelay int - helloTime int - maxAge int - backupBridgePriority string - bridgePriority string - vlanID string - routingInstance string - systemIdentifier string -} - -func resourceVstpVlan() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceVstpVlanCreate, - ReadWithoutTimeout: resourceVstpVlanRead, - UpdateWithoutTimeout: resourceVstpVlanUpdate, - DeleteWithoutTimeout: resourceVstpVlanDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceVstpVlanImport, - }, - Schema: map[string]*schema.Schema{ - "vlan_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^all|[0-9]{1,4}$`), "must be 'all' or a VLAN id"), - }, - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "backup_bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^\d\d?k$`), "must be a number with increments of 4k - 4k,8k,..60k"), - }, - "bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^(0|\d\d?k)$`), "must be a number with increments of 4k - 0,4k,8k,..60k"), - }, - "forward_delay": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(4, 30), - }, - "hello_time": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 10), - }, - "max_age": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(6, 40), - }, - "system_identifier": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.IsMACAddress, - }, - }, - } -} - -func resourceVstpVlanCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - routingInstance := d.Get("routing_instance").(string) - vlanID := d.Get("vlan_id").(string) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setVstpVlan(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(vlanID + junos.IDSeparator + routingInstance) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if routingInstance != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) - } - } - vstpVlanExists, err := checkVstpVlanExists(vlanID, routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if vstpVlanExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - if routingInstance != junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp vlan %v already exists in routing-instance %v", vlanID, routingInstance))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp vlan %v already exists", vlanID))...) - } - if err := setVstpVlan(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_vstp_vlan") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - vstpVlanExists, err = checkVstpVlanExists(vlanID, routingInstance, junSess) - if err != nil { - return append(diagWarns, diag.FromErr(err)...) - } - if vstpVlanExists { - d.SetId(vlanID + junos.IDSeparator + routingInstance) - } else { - if routingInstance != junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp vlan %v not exists in routing-instance %v after commit "+ - "=> check your config", vlanID, routingInstance))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp vlan %v not exists after commit "+ - "=> check your config", vlanID))...) - } - - return append(diagWarns, resourceVstpVlanReadWJunSess(d, junSess)...) -} - -func resourceVstpVlanRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceVstpVlanReadWJunSess(d, junSess) -} - -func resourceVstpVlanReadWJunSess(d *schema.ResourceData, junSess *junos.Session, -) diag.Diagnostics { - junos.MutexLock() - vstpVlanOptions, err := readVstpVlan(d.Get("vlan_id").(string), d.Get("routing_instance").(string), junSess) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - if vstpVlanOptions.vlanID == "" { - d.SetId("") - } else { - fillVstpVlanData(d, vstpVlanOptions) - } - - return nil -} - -func resourceVstpVlanUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpVlan(d.Get("vlan_id").(string), d.Get("routing_instance").(string), false, junSess); err != nil { - return diag.FromErr(err) - } - if err := setVstpVlan(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpVlan( - d.Get("vlan_id").(string), - d.Get("routing_instance").(string), - false, - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setVstpVlan(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_vstp_vlan") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceVstpVlanReadWJunSess(d, junSess)...) -} - -func resourceVstpVlanDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpVlan(d.Get("vlan_id").(string), d.Get("routing_instance").(string), true, junSess); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpVlan( - d.Get("vlan_id").(string), - d.Get("routing_instance").(string), - true, - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_vstp_vlan") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceVstpVlanImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - result := make([]*schema.ResourceData, 1) - idSplit := strings.Split(d.Id(), junos.IDSeparator) - if len(idSplit) < 2 { - return nil, fmt.Errorf("missing element(s) in id with separator %v", junos.IDSeparator) - } - vstpVlanExists, err := checkVstpVlanExists(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - if !vstpVlanExists { - return nil, fmt.Errorf("don't find protocols vstp vlan with id '%v' "+ - "(id must be "+junos.IDSeparator+"", d.Id()) - } - vstpVlanOptions, err := readVstpVlan(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - fillVstpVlanData(d, vstpVlanOptions) - - result[0] = d - - return result, nil -} - -func checkVstpVlanExists(vlanID, routingInstance string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} - -func setVstpVlan(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0, 1) - - setPrefix := junos.SetLS - if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " - } - setPrefix += "protocols vstp vlan " + d.Get("vlan_id").(string) + " " - - configSet = append(configSet, setPrefix) - if v := d.Get("backup_bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) - } - if v := d.Get("bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"bridge-priority "+v) - } - if v := d.Get("forward_delay").(int); v != 0 { - configSet = append(configSet, setPrefix+"forward-delay "+strconv.Itoa(v)) - } - if v := d.Get("hello_time").(int); v != 0 { - configSet = append(configSet, setPrefix+"hello-time "+strconv.Itoa(v)) - } - if v := d.Get("max_age").(int); v != 0 { - configSet = append(configSet, setPrefix+"max-age "+strconv.Itoa(v)) - } - if v := d.Get("system_identifier").(string); v != "" { - configSet = append(configSet, setPrefix+"system-identifier "+v) - } - - return junSess.ConfigSet(configSet) -} - -func readVstpVlan(vlanID, routingInstance string, junSess *junos.Session, -) (confRead vstpVlanOptions, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySetRelative) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySetRelative) - } - if err != nil { - return confRead, err - } - if showConfig != junos.EmptyW { - confRead.vlanID = vlanID - confRead.routingInstance = routingInstance - 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) - switch { - case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): - confRead.backupBridgePriority = itemTrim - case balt.CutPrefixInString(&itemTrim, "bridge-priority "): - confRead.bridgePriority = itemTrim - case balt.CutPrefixInString(&itemTrim, "forward-delay "): - confRead.forwardDelay, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "hello-time "): - confRead.helloTime, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "max-age "): - confRead.maxAge, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "system-identifier "): - confRead.systemIdentifier = itemTrim - } - } - } - - return confRead, nil -} - -func delVstpVlan(vlanID, routingInstance string, deleteAll bool, junSess *junos.Session) error { - delPrefix := junos.DeleteLS - if routingInstance != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + routingInstance + " " - } - delPrefix += "protocols vstp vlan " + vlanID + " " - - if deleteAll { - return junSess.ConfigSet([]string{delPrefix}) - } - listLinesToDelete := []string{ - "backup-bridge-priority", - "bridge-priority", - "forward-delay", - "hello-time", - "max-age", - "system-identifier", - } - configSet := make([]string, len(listLinesToDelete)) - for k, line := range listLinesToDelete { - configSet[k] = delPrefix + line - } - - return junSess.ConfigSet(configSet) -} - -func fillVstpVlanData(d *schema.ResourceData, vstpVlanOptions vstpVlanOptions) { - if tfErr := d.Set("vlan_id", vstpVlanOptions.vlanID); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("routing_instance", vstpVlanOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("backup_bridge_priority", vstpVlanOptions.backupBridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bridge_priority", vstpVlanOptions.bridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("forward_delay", vstpVlanOptions.forwardDelay); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("hello_time", vstpVlanOptions.helloTime); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("max_age", vstpVlanOptions.maxAge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("system_identifier", vstpVlanOptions.systemIdentifier); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_vstp_vlan_test.go b/internal/providersdk/resource_vstp_vlan_test.go deleted file mode 100644 index 931baaf4..00000000 --- a/internal/providersdk/resource_vstp_vlan_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package providersdk_test - -import ( - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccResourceVstpVlan_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceVstpVlanSWConfigCreate(), - }, - { - Config: testAccResourceVstpVlanSWConfigUpdate(), - }, - { - ResourceName: "junos_vstp_vlan.testacc_ri_vstp_vlan", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceVstpVlanSWConfigCreate() string { - return ` -resource "junos_vstp_vlan" "testacc_vstp_vlan" { - vlan_id = "10" -} -resource "junos_vstp_vlan" "testacc_vstp_vlan_all" { - vlan_id = "all" -} -resource "junos_routing_instance" "testacc_vstp_vlan" { - name = "testacc_vstp_vlan" - type = "virtual-switch" -} -resource "junos_vstp_vlan" "testacc_ri_vstp_vlan" { - routing_instance = junos_routing_instance.testacc_vstp_vlan.name - vlan_id = "11" - bridge_priority = "16k" -} -resource "junos_vstp_vlan" "testacc_ri_vstp_vlan_all" { - routing_instance = junos_routing_instance.testacc_vstp_vlan.name - vlan_id = "all" - system_identifier = "00:aa:bc:ed:ff:11" -} -` -} - -func testAccResourceVstpVlanSWConfigUpdate() string { - return ` -resource "junos_vstp_vlan" "testacc_vstp_vlan" { - vlan_id = "10" - backup_bridge_priority = "8k" - bridge_priority = "4k" -} -resource "junos_vstp_vlan" "testacc_vstp_vlan_all" { - vlan_id = "all" - hello_time = 2 -} -resource "junos_routing_instance" "testacc_vstp_vlan" { - name = "testacc_vstp_vlan" - type = "virtual-switch" -} -resource "junos_vstp_vlan" "testacc_ri_vstp_vlan" { - routing_instance = junos_routing_instance.testacc_vstp_vlan.name - vlan_id = "11" - backup_bridge_priority = "20k" - bridge_priority = 0 - forward_delay = 22 - hello_time = 3 - max_age = 24 -} -resource "junos_vstp_vlan" "testacc_ri_vstp_vlan_all" { - routing_instance = junos_routing_instance.testacc_vstp_vlan.name - vlan_id = "all" -} -` -} From 7e460f19a1324ca8da276606f1a763a4033d0f04 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 5 Jun 2024 18:27:32 +0200 Subject: [PATCH 15/30] r/vstp_vlan_group: use new provider via framework --- .changes/vstp-with-fwk.md | 3 + docs/resources/vstp_vlan_group.md | 6 +- internal/providerfwk/provider.go | 1 + .../providerfwk/resource_vstp_vlan_group.go | 626 ++++++++++++++++++ .../resource_vstp_vlan_group_test.go | 31 + .../1/main.tf | 14 + .../2/main.tf | 22 + .../func_resource_vstp_vlan_group.go | 23 + internal/providersdk/provider.go | 3 +- .../providersdk/resource_vstp_vlan_group.go | 508 -------------- .../resource_vstp_vlan_group_test.go | 75 --- 11 files changed, 724 insertions(+), 588 deletions(-) create mode 100644 internal/providerfwk/resource_vstp_vlan_group.go create mode 100644 internal/providerfwk/resource_vstp_vlan_group_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/2/main.tf create mode 100644 internal/providersdk/func_resource_vstp_vlan_group.go delete mode 100644 internal/providersdk/resource_vstp_vlan_group.go delete mode 100644 internal/providersdk/resource_vstp_vlan_group_test.go diff --git a/.changes/vstp-with-fwk.md b/.changes/vstp-with-fwk.md index e7f94a3b..b7b2f6d5 100644 --- a/.changes/vstp-with-fwk.md +++ b/.changes/vstp-with-fwk.md @@ -7,3 +7,6 @@ ENHANCEMENTS: * **resource/junos_vstp_vlan**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) some of config errors are now sent during Plan instead of during Apply optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_vlan_group**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* diff --git a/docs/resources/vstp_vlan_group.md b/docs/resources/vstp_vlan_group.md index cbbcf4f2..725c0e0a 100644 --- a/docs/resources/vstp_vlan_group.md +++ b/docs/resources/vstp_vlan_group.md @@ -26,11 +26,11 @@ The following arguments are supported: Need to be `default` (for root level) or name of routing instance. Defaults to `default`. - **vlan** (Required, Set of String) - VLAN IDs or VLAN ID ranges [1..4094]. + VLAN IDs or VLAN ID ranges (1..4094). - **backup_bridge_priority** (Optional, String) - Priority of the bridge (in increments of 4k - 4k,8k,..60k) (4096..61440). + Priority of the bridge (in increments of 4k - 4k,8k,..60k). - **bridge_priority** (Optional, String) - Priority of the bridge (in increments of 4k - 0,4k,8k,..60k) (0..61440). + Priority of the bridge (in increments of 4k - 0,4k,8k,..60k). - **forward_delay** (Optional, Number) Time spent in listening or learning state (4..30 seconds). - **hello_time** (Optional, Number) diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index 856da2ee..a3dafc90 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -303,6 +303,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newVlanResource, newVstpResource, newVstpVlanResource, + newVstpVlanGroupResource, } } diff --git a/internal/providerfwk/resource_vstp_vlan_group.go b/internal/providerfwk/resource_vstp_vlan_group.go new file mode 100644 index 00000000..7d72cbc7 --- /dev/null +++ b/internal/providerfwk/resource_vstp_vlan_group.go @@ -0,0 +1,626 @@ +package providerfwk + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/stringdefault" + "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 = &vstpVlanGroup{} + _ resource.ResourceWithConfigure = &vstpVlanGroup{} + _ resource.ResourceWithValidateConfig = &vstpVlanGroup{} + _ resource.ResourceWithImportState = &vstpVlanGroup{} +) + +type vstpVlanGroup struct { + client *junos.Client +} + +func newVstpVlanGroupResource() resource.Resource { + return &vstpVlanGroup{} +} + +func (rsc *vstpVlanGroup) typeName() string { + return providerName + "_vstp_vlan_group" +} + +func (rsc *vstpVlanGroup) junosName() string { + return "vstp vlan-group group" +} + +func (rsc *vstpVlanGroup) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *vstpVlanGroup) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *vstpVlanGroup) 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 *vstpVlanGroup) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: defaultResourceSchemaDescription(rsc), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "An identifier for the resource with format " + + "`" + junos.IDSeparator + "`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "VLAN group name.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for vstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "vlan": schema.SetAttribute{ + ElementType: types.StringType, + Required: true, + Description: " VLAN IDs or VLAN ID ranges.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + stringvalidator.RegexMatches(regexp.MustCompile( + `^(409[0-4]|(40[0-8]|[1-3]\d\d|[1-9]\d|[1-9])\d|[1-9])`+ + `(-(409[0-4]|(40[0-8]|[1-3]\d\d|[1-9]\d|[1-9])\d|[1-9]))?$`), + "must be a VLAN id (1..4094) or a range of VLAN id (1..4094)-(1..4094)"), + ), + }, + }, + "backup_bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^\d\d?k$`), + "must be a number with increments of 4k - 4k,8k,..60k", + ), + }, + }, + "bridge_priority": schema.StringAttribute{ + Optional: true, + Description: "Priority of the bridge.", + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^(0|\d\d?k)$`), + "must be a number with increments of 4k - 0,4k,8k,..60k", + ), + }, + }, + "forward_delay": schema.Int64Attribute{ + Optional: true, + Description: "Time spent in listening or learning state (seconds).", + Validators: []validator.Int64{ + int64validator.Between(4, 30), + }, + }, + "hello_time": schema.Int64Attribute{ + Optional: true, + Description: "Time interval between configuration BPDUs (seconds).", + Validators: []validator.Int64{ + int64validator.Between(1, 10), + }, + }, + "max_age": schema.Int64Attribute{ + Optional: true, + Description: "Maximum age of received protocol bpdu (seconds).", + Validators: []validator.Int64{ + int64validator.Between(6, 40), + }, + }, + "system_identifier": schema.StringAttribute{ + Optional: true, + Description: "System identifier to represent this node.", + Validators: []validator.String{ + tfvalidator.StringMACAddress().WithMac48ColonHexa(), + }, + }, + }, + } +} + +type vstpVlanGroupData struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + Vlan []types.String `tfsdk:"vlan"` + BackupBridgePriority types.String `tfsdk:"backup_bridge_priority"` + BridgePriority types.String `tfsdk:"bridge_priority"` + ForwardDelay types.Int64 `tfsdk:"forward_delay"` + HelloTime types.Int64 `tfsdk:"hello_time"` + MaxAge types.Int64 `tfsdk:"max_age"` + SystemIdentifier types.String `tfsdk:"system_identifier"` +} + +type vstpVlanGroupConfig struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + Vlan types.Set `tfsdk:"vlan"` + BackupBridgePriority types.String `tfsdk:"backup_bridge_priority"` + BridgePriority types.String `tfsdk:"bridge_priority"` + ForwardDelay types.Int64 `tfsdk:"forward_delay"` + HelloTime types.Int64 `tfsdk:"hello_time"` + MaxAge types.Int64 `tfsdk:"max_age"` + SystemIdentifier types.String `tfsdk:"system_identifier"` +} + +func (rsc *vstpVlanGroup) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config vstpVlanGroupConfig + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.BackupBridgePriority.IsNull() && !config.BackupBridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BackupBridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be a multiple of 4k", + ) + } + if v < 4 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be between 4k and 60k", + ) + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if bridgePriority, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v <= bridgePriority { + resp.Diagnostics.AddAttributeError( + path.Root("backup_bridge_priority"), + "Bad Value Error", + "backup_bridge_priority must be worse (higher value) than bridge_priority", + ) + } + } + } + } + } + if !config.BridgePriority.IsNull() && !config.BridgePriority.IsUnknown() { + if v, err := strconv.Atoi(strings.TrimSuffix( + config.BridgePriority.ValueString(), "k", + )); err == nil { + if v%4 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be a multiple of 4k", + ) + } + if v < 0 || v > 60 { + resp.Diagnostics.AddAttributeError( + path.Root("bridge_priority"), + "Bad Value Error", + "bridge_priority must be between 0 and 60k", + ) + } + } + } +} + +func (rsc *vstpVlanGroup) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan vstpVlanGroupData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Name.ValueString() == "" { + resp.Diagnostics.AddAttributeError( + path.Root("name"), + "Empty Name", + defaultResourceCouldNotCreateWithEmptyMessage(rsc, "name"), + ) + + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + groupExists, err := checkVstpVlanGroupExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if groupExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsInRoutingInstanceMessage(rsc, plan.Name, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsMessage(rsc, plan.Name), + ) + } + + return false + } + + return true + }, + func(fnCtx context.Context, junSess *junos.Session) bool { + groupExists, err := checkVstpVlanGroupExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PostCheckErrSummary, err.Error()) + + return false + } + if !groupExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsInRoutingInstanceAfterCommitMessage(rsc, plan.Name, v), + ) + } else { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsAfterCommitMessage(rsc, plan.Name), + ) + } + + return false + } + + return true + }, + &plan, + resp, + ) +} + +func (rsc *vstpVlanGroup) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data vstpVlanGroupData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataReadFrom2String = &data + defaultResourceRead( + ctx, + rsc, + []string{ + state.Name.ValueString(), + state.RoutingInstance.ValueString(), + }, + &data, + nil, + resp, + ) +} + +func (rsc *vstpVlanGroup) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state vstpVlanGroupData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataDelWithOpts = &state + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *vstpVlanGroup) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state vstpVlanGroupData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *vstpVlanGroup) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + var data vstpVlanGroupData + + var _ resourceDataReadFrom2String = &data + defaultResourceImportState( + ctx, + rsc, + &data, + req, + resp, + defaultResourceImportDontFindMessage(rsc, req.ID)+ + " (id must be "+junos.IDSeparator+")", + ) +} + +func checkVstpVlanGroupExists( + _ context.Context, name, routingInstance string, junSess *junos.Session, +) ( + bool, error, +) { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} + +func (rscData *vstpVlanGroupData) fillID() { + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(rscData.Name.ValueString() + junos.IDSeparator + v) + } else { + rscData.ID = types.StringValue(rscData.Name.ValueString() + junos.IDSeparator + junos.DefaultW) + } +} + +func (rscData *vstpVlanGroupData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *vstpVlanGroupData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + configSet := make([]string, 0, len(rscData.Vlan)) + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols vstp vlan-group group " + rscData.Name.ValueString() + " " + + for _, v := range rscData.Vlan { + configSet = append(configSet, setPrefix+"vlan "+v.ValueString()) + } + if v := rscData.BackupBridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) + } + if v := rscData.BridgePriority.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"bridge-priority "+v) + } + if !rscData.ForwardDelay.IsNull() { + configSet = append(configSet, setPrefix+"forward-delay "+ + utils.ConvI64toa(rscData.ForwardDelay.ValueInt64())) + } + if !rscData.HelloTime.IsNull() { + configSet = append(configSet, setPrefix+"hello-time "+ + utils.ConvI64toa(rscData.HelloTime.ValueInt64())) + } + if !rscData.MaxAge.IsNull() { + configSet = append(configSet, setPrefix+"max-age "+ + utils.ConvI64toa(rscData.MaxAge.ValueInt64())) + } + if v := rscData.SystemIdentifier.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"system-identifier "+v) + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *vstpVlanGroupData) read( + _ context.Context, name, routingInstance string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols vstp vlan-group group " + name + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if showConfig != junos.EmptyW { + rscData.Name = types.StringValue(name) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + 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) + switch { + case balt.CutPrefixInString(&itemTrim, "vlan "): + rscData.Vlan = append(rscData.Vlan, types.StringValue(itemTrim)) + case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): + rscData.BackupBridgePriority = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "bridge-priority "): + rscData.BridgePriority = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "forward-delay "): + rscData.ForwardDelay, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "hello-time "): + rscData.HelloTime, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "max-age "): + rscData.MaxAge, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "system-identifier "): + rscData.SystemIdentifier = types.StringValue(itemTrim) + } + } + } + + return nil +} + +func (rscData *vstpVlanGroupData) delOpts( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + delPrefix += "protocols vstp vlan-group group " + rscData.Name.ValueString() + " " + + listLinesToDelete := []string{ + "backup-bridge-priority", + "bridge-priority", + "forward-delay", + "hello-time", + "max-age", + "system-identifier", + "vlan", + } + + configSet := make([]string, len(listLinesToDelete)) + for k, line := range listLinesToDelete { + configSet[k] = delPrefix + line + } + + return junSess.ConfigSet(configSet) +} + +func (rscData *vstpVlanGroupData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "protocols vstp vlan-group group " + rscData.Name.ValueString(), + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_vstp_vlan_group_test.go b/internal/providerfwk/resource_vstp_vlan_group_test.go new file mode 100644 index 00000000..9f40f1b5 --- /dev/null +++ b/internal/providerfwk/resource_vstp_vlan_group_test.go @@ -0,0 +1,31 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceVstpVlanGroup_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_vstp_vlan_group.testacc_ri_vstp_vlan_group", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/1/main.tf new file mode 100644 index 00000000..31a01afb --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/1/main.tf @@ -0,0 +1,14 @@ +resource "junos_vstp_vlan_group" "testacc_vstp_vlan_group" { + name = "vlanGroup" + vlan = ["10"] +} +resource "junos_routing_instance" "testacc_vstp_vlan_group" { + name = "testacc_vstp_vlan_group" + type = "virtual-switch" +} +resource "junos_vstp_vlan_group" "testacc_ri_vstp_vlan_group" { + routing_instance = junos_routing_instance.testacc_vstp_vlan_group.name + name = "vlanGroupRI" + vlan = ["12"] + bridge_priority = "16k" +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/2/main.tf new file mode 100644 index 00000000..c595053c --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpVlanGroup_basic/2/main.tf @@ -0,0 +1,22 @@ + +resource "junos_vstp_vlan_group" "testacc_vstp_vlan_group" { + name = "vlanGroup" + vlan = ["10"] + backup_bridge_priority = "8k" + bridge_priority = "4k" + hello_time = 2 +} +resource "junos_routing_instance" "testacc_vstp_vlan_group" { + name = "testacc_vstp_vlan_group" + type = "virtual-switch" +} +resource "junos_vstp_vlan_group" "testacc_ri_vstp_vlan_group" { + routing_instance = junos_routing_instance.testacc_vstp_vlan_group.name + name = "vlanGroupRI" + vlan = ["12", "11"] + backup_bridge_priority = "20k" + forward_delay = 22 + hello_time = 3 + max_age = 24 + system_identifier = "00:aa:bc:ed:ff:11" +} diff --git a/internal/providersdk/func_resource_vstp_vlan_group.go b/internal/providersdk/func_resource_vstp_vlan_group.go new file mode 100644 index 00000000..5da0cba9 --- /dev/null +++ b/internal/providersdk/func_resource_vstp_vlan_group.go @@ -0,0 +1,23 @@ +package providersdk + +import "github.com/jeremmfr/terraform-provider-junos/internal/junos" + +func checkVstpVlanGroupExists(name, routingInstance string, junSess *junos.Session, +) (_ bool, err error) { + var showConfig string + if routingInstance == junos.DefaultW { + showConfig, err = junSess.Command(junos.CmdShowConfig + + "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) + } else { + showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + + "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) + } + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index f66f84d2..90092a76 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -229,8 +229,7 @@ func Provider() *schema.Provider { "junos_system_root_authentication": resourceSystemRootAuthentication(), "junos_system_services_dhcp_localserver_group": resourceSystemServicesDhcpLocalServerGroup(), - "junos_vstp_interface": resourceVstpInterface(), - "junos_vstp_vlan_group": resourceVstpVlanGroup(), + "junos_vstp_interface": resourceVstpInterface(), }, DataSourcesMap: map[string]*schema.Resource{ "junos_system_information": dataSourceSystemInformation(), diff --git a/internal/providersdk/resource_vstp_vlan_group.go b/internal/providersdk/resource_vstp_vlan_group.go deleted file mode 100644 index 8d65fd0e..00000000 --- a/internal/providersdk/resource_vstp_vlan_group.go +++ /dev/null @@ -1,508 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type vstpVlanGroupOptions struct { - forwardDelay int - helloTime int - maxAge int - backupBridgePriority string - bridgePriority string - name string - routingInstance string - systemIdentifier string - vlan []string -} - -func resourceVstpVlanGroup() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceVstpVlanGroupCreate, - ReadWithoutTimeout: resourceVstpVlanGroupRead, - UpdateWithoutTimeout: resourceVstpVlanGroupUpdate, - DeleteWithoutTimeout: resourceVstpVlanGroupDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceVstpVlanGroupImport, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "vlan": { - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "backup_bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^\d\d?k$`), "must be a number with increments of 4k - 4k,8k,..60k"), - }, - "bridge_priority": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^(0|\d\d?k)$`), "must be a number with increments of 4k - 0,4k,8k,..60k"), - }, - "forward_delay": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(4, 30), - }, - "hello_time": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 10), - }, - "max_age": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(6, 40), - }, - "system_identifier": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.IsMACAddress, - }, - }, - } -} - -func resourceVstpVlanGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - routingInstance := d.Get("routing_instance").(string) - name := d.Get("name").(string) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setVstpVlanGroup(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(name + junos.IDSeparator + routingInstance) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if routingInstance != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) - } - } - vstpVlanGroupExists, err := checkVstpVlanGroupExists(name, routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if vstpVlanGroupExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - if routingInstance != junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp vlan-group group %v already exists in routing-instance %v", - name, routingInstance))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp vlan-group group %v already exists", - name))...) - } - if err := setVstpVlanGroup(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_vstp_vlan_group") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - vstpVlanGroupExists, err = checkVstpVlanGroupExists(name, routingInstance, junSess) - if err != nil { - return append(diagWarns, diag.FromErr(err)...) - } - if vstpVlanGroupExists { - d.SetId(name + junos.IDSeparator + routingInstance) - } else { - if routingInstance != junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp vlan-group group %v not exists in routing-instance %v after commit "+ - "=> check your config", name, routingInstance))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp vlan-group group %v not exists after commit "+ - "=> check your config", name))...) - } - - return append(diagWarns, resourceVstpVlanGroupReadWJunSess(d, junSess)...) -} - -func resourceVstpVlanGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceVstpVlanGroupReadWJunSess(d, junSess) -} - -func resourceVstpVlanGroupReadWJunSess(d *schema.ResourceData, junSess *junos.Session, -) diag.Diagnostics { - junos.MutexLock() - vstpVlanGroupOptions, err := readVstpVlanGroup( - d.Get("name").(string), - d.Get("routing_instance").(string), - junSess, - ) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - if vstpVlanGroupOptions.name == "" { - d.SetId("") - } else { - fillVstpVlanGroupData(d, vstpVlanGroupOptions) - } - - return nil -} - -func resourceVstpVlanGroupUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpVlanGroup( - d.Get("name").(string), - d.Get("routing_instance").(string), - false, - junSess, - ); err != nil { - return diag.FromErr(err) - } - if err := setVstpVlanGroup(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpVlanGroup( - d.Get("name").(string), - d.Get("routing_instance").(string), - false, - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setVstpVlanGroup(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_vstp_vlan_group") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceVstpVlanGroupReadWJunSess(d, junSess)...) -} - -func resourceVstpVlanGroupDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpVlanGroup( - d.Get("name").(string), - d.Get("routing_instance").(string), - true, - junSess, - ); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpVlanGroup( - d.Get("name").(string), - d.Get("routing_instance").(string), - true, - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_vstp_vlan_group") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceVstpVlanGroupImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - result := make([]*schema.ResourceData, 1) - idSplit := strings.Split(d.Id(), junos.IDSeparator) - if len(idSplit) < 2 { - return nil, fmt.Errorf("missing element(s) in id with separator %v", junos.IDSeparator) - } - vstpVlanGroupExists, err := checkVstpVlanGroupExists(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - if !vstpVlanGroupExists { - return nil, fmt.Errorf("don't find protocols vstp vlan-group group with id '%v' "+ - "(id must be "+junos.IDSeparator+"", d.Id()) - } - vstpVlanGroupOptions, err := readVstpVlanGroup(idSplit[0], idSplit[1], junSess) - if err != nil { - return nil, err - } - fillVstpVlanGroupData(d, vstpVlanGroupOptions) - - result[0] = d - - return result, nil -} - -func checkVstpVlanGroupExists(name, routingInstance string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} - -func setVstpVlanGroup(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - - setPrefix := junos.SetLS - if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " - } - setPrefix += "protocols vstp vlan-group group " + d.Get("name").(string) + " " - - for _, vlan := range sortSetOfString(d.Get("vlan").(*schema.Set).List()) { - configSet = append(configSet, setPrefix+"vlan "+vlan) - } - if v := d.Get("backup_bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"backup-bridge-priority "+v) - } - if v := d.Get("bridge_priority").(string); v != "" { - configSet = append(configSet, setPrefix+"bridge-priority "+v) - } - if v := d.Get("forward_delay").(int); v != 0 { - configSet = append(configSet, setPrefix+"forward-delay "+strconv.Itoa(v)) - } - if v := d.Get("hello_time").(int); v != 0 { - configSet = append(configSet, setPrefix+"hello-time "+strconv.Itoa(v)) - } - if v := d.Get("max_age").(int); v != 0 { - configSet = append(configSet, setPrefix+"max-age "+strconv.Itoa(v)) - } - if v := d.Get("system_identifier").(string); v != "" { - configSet = append(configSet, setPrefix+"system-identifier "+v) - } - - return junSess.ConfigSet(configSet) -} - -func readVstpVlanGroup(name, routingInstance string, junSess *junos.Session, -) (confRead vstpVlanGroupOptions, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySetRelative) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySetRelative) - } - if err != nil { - return confRead, err - } - if showConfig != junos.EmptyW { - confRead.name = name - confRead.routingInstance = routingInstance - 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) - switch { - case balt.CutPrefixInString(&itemTrim, "vlan "): - confRead.vlan = append(confRead.vlan, itemTrim) - case balt.CutPrefixInString(&itemTrim, "backup-bridge-priority "): - confRead.backupBridgePriority = itemTrim - case balt.CutPrefixInString(&itemTrim, "bridge-priority "): - confRead.bridgePriority = itemTrim - case balt.CutPrefixInString(&itemTrim, "forward-delay "): - confRead.forwardDelay, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "hello-time "): - confRead.helloTime, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "max-age "): - confRead.maxAge, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "system-identifier "): - confRead.systemIdentifier = itemTrim - } - } - } - - return confRead, nil -} - -func delVstpVlanGroup(name, routingInstance string, deleteAll bool, junSess *junos.Session) error { - delPrefix := junos.DeleteLS - if routingInstance != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + routingInstance + " " - } - delPrefix += "protocols vstp vlan-group group " + name + " " - - if deleteAll { - return junSess.ConfigSet([]string{delPrefix}) - } - listLinesToDelete := []string{ - "backup-bridge-priority", - "bridge-priority", - "forward-delay", - "hello-time", - "max-age", - "system-identifier", - "vlan", - } - configSet := make([]string, len(listLinesToDelete)) - for k, line := range listLinesToDelete { - configSet[k] = delPrefix + line - } - - return junSess.ConfigSet(configSet) -} - -func fillVstpVlanGroupData(d *schema.ResourceData, vstpVlanGroupOptions vstpVlanGroupOptions) { - if tfErr := d.Set("name", vstpVlanGroupOptions.name); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("routing_instance", vstpVlanGroupOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("vlan", vstpVlanGroupOptions.vlan); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("backup_bridge_priority", vstpVlanGroupOptions.backupBridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bridge_priority", vstpVlanGroupOptions.bridgePriority); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("forward_delay", vstpVlanGroupOptions.forwardDelay); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("hello_time", vstpVlanGroupOptions.helloTime); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("max_age", vstpVlanGroupOptions.maxAge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("system_identifier", vstpVlanGroupOptions.systemIdentifier); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_vstp_vlan_group_test.go b/internal/providersdk/resource_vstp_vlan_group_test.go deleted file mode 100644 index 53472645..00000000 --- a/internal/providersdk/resource_vstp_vlan_group_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package providersdk_test - -import ( - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccResourceVstpVlanGroup_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceVstpVlanGroupSWConfigCreate(), - }, - { - Config: testAccResourceVstpVlanGroupSWConfigUpdate(), - }, - { - ResourceName: "junos_vstp_vlan_group.testacc_ri_vstp_vlan_group", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceVstpVlanGroupSWConfigCreate() string { - return ` -resource "junos_vstp_vlan_group" "testacc_vstp_vlan_group" { - name = "vlanGroup" - vlan = ["10"] -} -resource "junos_routing_instance" "testacc_vstp_vlan_group" { - name = "testacc_vstp_vlan_group" - type = "virtual-switch" -} -resource "junos_vstp_vlan_group" "testacc_ri_vstp_vlan_group" { - routing_instance = junos_routing_instance.testacc_vstp_vlan_group.name - name = "vlanGroupRI" - vlan = ["12"] - bridge_priority = "16k" -} -` -} - -func testAccResourceVstpVlanGroupSWConfigUpdate() string { - return ` -resource "junos_vstp_vlan_group" "testacc_vstp_vlan_group" { - name = "vlanGroup" - vlan = ["10"] - backup_bridge_priority = "8k" - bridge_priority = "4k" - hello_time = 2 -} -resource "junos_routing_instance" "testacc_vstp_vlan_group" { - name = "testacc_vstp_vlan_group" - type = "virtual-switch" -} -resource "junos_vstp_vlan_group" "testacc_ri_vstp_vlan_group" { - routing_instance = junos_routing_instance.testacc_vstp_vlan_group.name - name = "vlanGroupRI" - vlan = ["12", "11"] - backup_bridge_priority = "20k" - forward_delay = 22 - hello_time = 3 - max_age = 24 - system_identifier = "00:aa:bc:ed:ff:11" -} -` -} From c4242eca56f02fd36fc59677aae19f0ba23965be Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 7 Jun 2024 16:56:18 +0200 Subject: [PATCH 16/30] r/vstp_interface: use new provider via framework --- .changes/vstp-with-fwk.md | 3 + internal/providerfwk/provider.go | 1 + .../providerfwk/resource_vstp_interface.go | 778 +++++++++++++++++ .../resource_vstp_interface_test.go | 87 ++ .../1/main.tf | 50 ++ .../1/variables.tf | 3 + .../2/main.tf | 63 ++ .../2/variables.tf | 3 + .../providersdk/func_resource_vstp_vlan.go | 23 - .../func_resource_vstp_vlan_group.go | 23 - internal/providersdk/provider.go | 2 - .../providersdk/resource_vstp_interface.go | 792 ------------------ .../resource_vstp_interface_test.go | 186 ---- 13 files changed, 988 insertions(+), 1026 deletions(-) create mode 100644 internal/providerfwk/resource_vstp_interface.go create mode 100644 internal/providerfwk/resource_vstp_interface_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/variables.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/variables.tf delete mode 100644 internal/providersdk/func_resource_vstp_vlan.go delete mode 100644 internal/providersdk/func_resource_vstp_vlan_group.go delete mode 100644 internal/providersdk/resource_vstp_interface.go delete mode 100644 internal/providersdk/resource_vstp_interface_test.go diff --git a/.changes/vstp-with-fwk.md b/.changes/vstp-with-fwk.md index b7b2f6d5..952c50b1 100644 --- a/.changes/vstp-with-fwk.md +++ b/.changes/vstp-with-fwk.md @@ -4,6 +4,9 @@ ENHANCEMENTS: * **resource/junos_vstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) some of config errors are now sent during Plan instead of during Apply optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* * **resource/junos_vstp_vlan**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) some of config errors are now sent during Plan instead of during Apply optional boolean attributes doesn't accept value *false* diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index a3dafc90..a0f32991 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -302,6 +302,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newVirtualChassisResource, newVlanResource, newVstpResource, + newVstpInterfaceResource, newVstpVlanResource, newVstpVlanGroupResource, } diff --git a/internal/providerfwk/resource_vstp_interface.go b/internal/providerfwk/resource_vstp_interface.go new file mode 100644 index 00000000..490da5d4 --- /dev/null +++ b/internal/providerfwk/resource_vstp_interface.go @@ -0,0 +1,778 @@ +package providerfwk + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdata" + "github.com/jeremmfr/terraform-provider-junos/internal/tfdiag" + "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-validators/stringvalidator" + "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/stringdefault" + "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 = &vstpInterface{} + _ resource.ResourceWithConfigure = &vstpInterface{} + _ resource.ResourceWithValidateConfig = &vstpInterface{} + _ resource.ResourceWithImportState = &vstpInterface{} +) + +type vstpInterface struct { + client *junos.Client +} + +func newVstpInterfaceResource() resource.Resource { + return &vstpInterface{} +} + +func (rsc *vstpInterface) typeName() string { + return providerName + "_vstp_interface" +} + +func (rsc *vstpInterface) junosName() string { + return "vstp interface" +} + +func (rsc *vstpInterface) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *vstpInterface) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *vstpInterface) 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 *vstpInterface) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Description: defaultResourceSchemaDescription(rsc), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "An identifier for the resource with format " + + "`" + junos.IDSeparator + junos.IDSeparator + "`, " + + "`" + junos.IDSeparator + "v_" + junos.IDSeparator + "` or " + + "`" + junos.IDSeparator + "vg_" + junos.IDSeparator + "`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "Interface name or `all`.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + tfvalidator.StringFormat(tfvalidator.InterfaceFormat), + tfvalidator.StringDotExclusion(), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(junos.DefaultW), + Description: "Routing instance for vstp protocol if not root level.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "vlan": schema.StringAttribute{ + Optional: true, + Description: "Configure interface in VSTP vlan.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile( + `^(409[0-4]|(40[0-8]|[1-3]\d\d|[1-9]\d|[1-9])\d|[1-9]|all)$`), + "must be a VLAN id (1..4094) or all"), + }, + }, + "vlan_group": schema.StringAttribute{ + Optional: true, + Description: "Configure interface in VSTP vlan-group.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "access_trunk": schema.BoolAttribute{ + Optional: true, + Description: "Send/Receive untagged RSTP BPDUs on this interface.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bpdu_timeout_action_alarm": schema.BoolAttribute{ + Optional: true, + Description: "Generate an alarm on BPDU expiry (Loop Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "bpdu_timeout_action_block": schema.BoolAttribute{ + Optional: true, + Description: "Block the interface on BPDU expiry (Loop Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "cost": schema.Int64Attribute{ + Optional: true, + Description: "Cost of the interface.", + Validators: []validator.Int64{ + int64validator.Between(1, 200000000), + }, + }, + "edge": schema.BoolAttribute{ + Optional: true, + Description: "Port is an edge port.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "mode": schema.StringAttribute{ + Optional: true, + Description: "Interface mode (P2P or shared).", + Validators: []validator.String{ + stringvalidator.OneOf("point-to-point", "shared"), + }, + }, + "no_root_port": schema.BoolAttribute{ + Optional: true, + Description: "Do not allow the interface to become root (Root Protect).", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "priority": schema.Int64Attribute{ + Optional: true, + Description: "Interface priority (in increments of 16).", + Validators: []validator.Int64{ + int64validator.Between(0, 240), + }, + }, + }, + } +} + +type vstpInterfaceData struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + Vlan types.String `tfsdk:"vlan"` + VlanGroup types.String `tfsdk:"vlan_group"` + AccessTrunk types.Bool `tfsdk:"access_trunk"` + BpduTimeoutActionAlarm types.Bool `tfsdk:"bpdu_timeout_action_alarm"` + BpduTimeoutActionBlock types.Bool `tfsdk:"bpdu_timeout_action_block"` + Cost types.Int64 `tfsdk:"cost"` + Edge types.Bool `tfsdk:"edge"` + Mode types.String `tfsdk:"mode"` + NoRootPort types.Bool `tfsdk:"no_root_port"` + Priority types.Int64 `tfsdk:"priority"` +} + +func (rsc *vstpInterface) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config vstpInterfaceData + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.Vlan.IsNull() && !config.Vlan.IsUnknown() && + !config.VlanGroup.IsNull() && !config.VlanGroup.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("vlan"), + tfdiag.ConflictConfigErrSummary, + "vlan and vlan_group cannot be configured together", + ) + } + if !config.Priority.IsNull() && !config.Priority.IsUnknown() { + if config.Priority.ValueInt64()%16 != 0 { + resp.Diagnostics.AddAttributeError( + path.Root("priority"), + "Bad Value Error", + "priority must be a multiple of 16", + ) + } + } +} + +func (rsc *vstpInterface) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan vstpInterfaceData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Name.ValueString() == "" { + resp.Diagnostics.AddAttributeError( + path.Root("name"), + "Empty Name", + defaultResourceCouldNotCreateWithEmptyMessage(rsc, "name"), + ) + + return + } + if !plan.Vlan.IsNull() && !plan.VlanGroup.IsNull() { + resp.Diagnostics.AddAttributeError( + path.Root("vlan"), + tfdiag.ConflictConfigErrSummary, + "vlan and vlan_group cannot be configured together", + ) + + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + instanceExists, err := checkRoutingInstanceExists(fnCtx, v, junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !instanceExists { + resp.Diagnostics.AddAttributeError( + path.Root("routing_instance"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("routing instance %q doesn't exist", v), + ) + + return false + } + } + if v := plan.Vlan.ValueString(); v != "" { + vlanExists, err := checkVstpVlanExists(fnCtx, v, plan.RoutingInstance.ValueString(), junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !vlanExists { + if vRI := plan.RoutingInstance.ValueString(); vRI != "" && vRI != junos.DefaultW { + resp.Diagnostics.AddAttributeError( + path.Root("vlan"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("vstp vlan %q in routing-instance %q doesn't exist", v, vRI), + ) + } else { + resp.Diagnostics.AddAttributeError( + path.Root("vlan"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("vstp vlan %q doesn't exist", v), + ) + } + + return false + } + } + if v := plan.VlanGroup.ValueString(); v != "" { + groupExists, err := checkVstpVlanGroupExists(fnCtx, v, plan.RoutingInstance.ValueString(), junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !groupExists { + if vRI := plan.RoutingInstance.ValueString(); vRI != "" && vRI != junos.DefaultW { + resp.Diagnostics.AddAttributeError( + path.Root("vlan_group"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("vstp vlan-group group %q in routing-instance %q doesn't exist", v, vRI), + ) + } else { + resp.Diagnostics.AddAttributeError( + path.Root("vlan_group"), + tfdiag.MissingConfigErrSummary, + fmt.Sprintf("vstp vlan-group group %q doesn't exist", v), + ) + } + + return false + } + } + interfaceExists, err := checkVstpInterfaceExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + plan.Vlan.ValueString(), + plan.VlanGroup.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if interfaceExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + switch { + case plan.Vlan.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf(rsc.junosName()+" %q already exists in routing-instance %q in vlan %q", + plan.Name.ValueString(), v, plan.Vlan.ValueString(), + ), + ) + case plan.VlanGroup.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf(rsc.junosName()+" %q already exists in routing-instance %q in vlan-group group %q", + plan.Name.ValueString(), v, plan.VlanGroup.ValueString(), + ), + ) + default: + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsInRoutingInstanceMessage(rsc, plan.Name, v), + ) + } + } else { + switch { + case plan.Vlan.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf(rsc.junosName()+" %q already exists in vlan %q", + plan.Name.ValueString(), plan.Vlan.ValueString(), + ), + ) + case plan.VlanGroup.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + fmt.Sprintf(rsc.junosName()+" %q already exists in vlan-group group %q", + plan.Name.ValueString(), plan.VlanGroup.ValueString(), + ), + ) + default: + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsMessage(rsc, plan.Name), + ) + } + } + + return false + } + + return true + }, + func(fnCtx context.Context, junSess *junos.Session) bool { + interfaceExists, err := checkVstpInterfaceExists( + fnCtx, + plan.Name.ValueString(), + plan.RoutingInstance.ValueString(), + plan.Vlan.ValueString(), + plan.VlanGroup.ValueString(), + junSess, + ) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PostCheckErrSummary, err.Error()) + + return false + } + if !interfaceExists { + if v := plan.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + switch { + case plan.Vlan.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf(rsc.junosName()+" %q does not exists in routing-instance %q in vlan %q after commit "+ + "=> check your config", + plan.Name.ValueString(), v, plan.Vlan.ValueString()), + ) + case plan.VlanGroup.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf(rsc.junosName()+" %q does not exists in routing-instance %q in vlan-group group %q after commit "+ + "=> check your config", + plan.Name.ValueString(), v, plan.VlanGroup.ValueString()), + ) + default: + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsInRoutingInstanceAfterCommitMessage(rsc, plan.Name, v), + ) + } + } else { + switch { + case plan.Vlan.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf(rsc.junosName()+" %q does not exists in vlan %q after commit "+ + "=> check your config", + plan.Name.ValueString(), plan.Vlan.ValueString()), + ) + case plan.VlanGroup.ValueString() != "": + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf(rsc.junosName()+" %q does not exists in vlan-group group %q after commit "+ + "=> check your config", + plan.Name.ValueString(), plan.VlanGroup.ValueString()), + ) + default: + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsAfterCommitMessage(rsc, plan.Name), + ) + } + } + + return false + } + + return true + }, + &plan, + resp, + ) +} + +func (rsc *vstpInterface) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data vstpInterfaceData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataReadFrom4String = &data + defaultResourceRead( + ctx, + rsc, + []string{ + state.Name.ValueString(), + state.RoutingInstance.ValueString(), + state.Vlan.ValueString(), + state.VlanGroup.ValueString(), + }, + &data, + nil, + resp, + ) +} + +func (rsc *vstpInterface) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state vstpInterfaceData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *vstpInterface) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state vstpInterfaceData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *vstpInterface) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + junSess, err := rsc.junosClient().StartNewSession(ctx) + if err != nil { + resp.Diagnostics.AddError(tfdiag.StartSessErrSummary, err.Error()) + + return + } + defer junSess.Close() + + idList := strings.Split(req.ID, junos.IDSeparator) + var name, routingInstance, vlan, vlanGroup string + switch len(idList) { + case 1: + name = idList[0] + case 2: + name = idList[0] + routingInstance = idList[1] + default: + name = idList[0] + routingInstance = idList[2] + if balt.CutPrefixInString(&idList[1], "v_") { + vlan = idList[1] + } else if balt.CutPrefixInString(&idList[1], "vg_") { + vlanGroup = idList[1] + } + } + + var data vstpInterfaceData + if err := data.read(ctx, name, routingInstance, vlan, vlanGroup, junSess); err != nil { + resp.Diagnostics.AddError(tfdiag.ConfigReadErrSummary, err.Error()) + + return + } + + if data.nullID() { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + fmt.Sprintf("don't find "+rsc.junosName()+" with id %q"+ + " (id must be "+junos.IDSeparator+junos.IDSeparator+", "+ + ""+junos.IDSeparator+"v_"+junos.IDSeparator+" or "+ + ""+junos.IDSeparator+"vg_"+junos.IDSeparator+")", + req.ID), + ) + + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func checkVstpInterfaceExists( + _ context.Context, name, routingInstance, vlan, vlanGroup string, junSess *junos.Session, +) ( + bool, error, +) { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showPrefix += "protocols vstp " + if vlan != "" { + showPrefix += "vlan " + vlan + " " + } else if vlanGroup != "" { + showPrefix += "vlan-group group " + vlanGroup + " " + } + showConfig, err := junSess.Command(showPrefix + + "interface " + name + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} + +func (rscData *vstpInterfaceData) fillID() { + idPrefix := rscData.Name.ValueString() + junos.IDSeparator + if rscData.Vlan.ValueString() != "" { + idPrefix += "v_" + rscData.Vlan.ValueString() + } else if rscData.VlanGroup.ValueString() != "" { + idPrefix += "vg_" + rscData.VlanGroup.ValueString() + } + + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(idPrefix + junos.IDSeparator + v) + } else { + rscData.ID = types.StringValue(idPrefix + junos.IDSeparator + junos.DefaultW) + } +} + +func (rscData *vstpInterfaceData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *vstpInterfaceData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + setPrefix := junos.SetLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " + } + setPrefix += "protocols vstp " + if v := rscData.Vlan.ValueString(); v != "" { + setPrefix += "vlan " + v + " " + } else if v := rscData.VlanGroup.ValueString(); v != "" { + setPrefix += "vlan-group group " + v + " " + } + setPrefix += "interface " + rscData.Name.ValueString() + " " + + configSet := []string{ + setPrefix, + } + + if rscData.AccessTrunk.ValueBool() { + configSet = append(configSet, setPrefix+"access-trunk") + } + if rscData.BpduTimeoutActionAlarm.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-timeout-action alarm") + } + if rscData.BpduTimeoutActionBlock.ValueBool() { + configSet = append(configSet, setPrefix+"bpdu-timeout-action block") + } + if !rscData.Cost.IsNull() { + configSet = append(configSet, setPrefix+"cost "+ + utils.ConvI64toa(rscData.Cost.ValueInt64())) + } + if rscData.Edge.ValueBool() { + configSet = append(configSet, setPrefix+"edge") + } + if v := rscData.Mode.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"mode "+v) + } + if rscData.NoRootPort.ValueBool() { + configSet = append(configSet, setPrefix+"no-root-port") + } + if !rscData.Priority.IsNull() { + configSet = append(configSet, setPrefix+"priority "+ + utils.ConvI64toa(rscData.Priority.ValueInt64())) + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *vstpInterfaceData) read( + _ context.Context, name, routingInstance, vlan, vlanGroup string, junSess *junos.Session, +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showPrefix += "protocols vstp " + if vlan != "" { + showPrefix += "vlan " + vlan + " " + } else if vlanGroup != "" { + showPrefix += "vlan-group group " + vlanGroup + " " + } + showConfig, err := junSess.Command(showPrefix + + "interface " + name + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if showConfig != junos.EmptyW { + rscData.Name = types.StringValue(name) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } + if vlan != "" { + rscData.Vlan = types.StringValue(vlan) + } else if vlanGroup != "" { + rscData.VlanGroup = types.StringValue(vlanGroup) + } + 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) + switch { + case itemTrim == "access-trunk": + rscData.AccessTrunk = types.BoolValue(true) + case itemTrim == "bpdu-timeout-action alarm": + rscData.BpduTimeoutActionAlarm = types.BoolValue(true) + case itemTrim == "bpdu-timeout-action block": + rscData.BpduTimeoutActionBlock = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "cost "): + rscData.Cost, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case itemTrim == "edge": + rscData.Edge = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "mode "): + rscData.Mode = types.StringValue(itemTrim) + case itemTrim == "no-root-port": + rscData.NoRootPort = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "priority "): + rscData.Priority, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + } + } + } + + return nil +} + +func (rscData *vstpInterfaceData) del( + _ context.Context, junSess *junos.Session, +) error { + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } + delPrefix += "protocols vstp " + if v := rscData.Vlan.ValueString(); v != "" { + delPrefix += "vlan " + v + " " + } else if v := rscData.VlanGroup.ValueString(); v != "" { + delPrefix += "vlan-group group " + v + " " + } + + configSet := []string{ + delPrefix + "interface " + rscData.Name.ValueString(), + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_vstp_interface_test.go b/internal/providerfwk/resource_vstp_interface_test.go new file mode 100644 index 00000000..725b9f42 --- /dev/null +++ b/internal/providerfwk/resource_vstp_interface_test.go @@ -0,0 +1,87 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/jeremmfr/terraform-provider-junos/internal/junos" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +// export TESTACC_INTERFACE= for choose interface available else it's xe-0/0/3. +func TestAccResourceVstpInterface_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") != "" { + testaccInterface := junos.DefaultInterfaceSwitchTestAcc + if iface := os.Getenv("TESTACC_INTERFACE"); iface != "" { + testaccInterface = iface + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + }, + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface2", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface3", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface4", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface5", + ImportState: true, + ImportStateVerify: true, + }, + { + ConfigVariables: map[string]config.Variable{ + "interface": config.StringVariable(testaccInterface), + }, + ResourceName: "junos_vstp_interface.testacc_vstp_interface6", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/main.tf new file mode 100644 index 00000000..5ac5b03a --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/main.tf @@ -0,0 +1,50 @@ +resource "junos_vstp_interface" "testacc_vstp_interface" { + name = "all" +} +resource "junos_vstp_vlan" "testacc_vstp_interface2" { + vlan_id = "10" +} +resource "junos_interface_physical" "testacc_vstp_interface2" { + name = var.interface + description = "testacc_vstp_interface2" + vlan_members = ["15"] +} +resource "junos_vstp_interface" "testacc_vstp_interface2" { + name = junos_interface_physical.testacc_vstp_interface2.name + vlan = junos_vstp_vlan.testacc_vstp_interface2.vlan_id +} +resource "junos_vstp_vlan_group" "testacc_vstp_interface3" { + name = "testacc_vstp_interface2" + vlan = ["11"] +} +resource "junos_vstp_interface" "testacc_vstp_interface3" { + name = "all" + vlan_group = junos_vstp_vlan_group.testacc_vstp_interface3.name +} +resource "junos_routing_instance" "testacc_vstp_interface" { + name = "testacc_vstp_intface" + type = "virtual-switch" +} +resource "junos_vstp_interface" "testacc_vstp_interface4" { + name = "all" + routing_instance = junos_routing_instance.testacc_vstp_interface.name +} +resource "junos_vstp_vlan" "testacc_vstp_interface5" { + vlan_id = "all" + routing_instance = junos_routing_instance.testacc_vstp_interface.name +} +resource "junos_vstp_interface" "testacc_vstp_interface5" { + name = "all" + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan = junos_vstp_vlan.testacc_vstp_interface5.vlan_id +} +resource "junos_vstp_vlan_group" "testacc_vstp_interface6" { + name = "testacc_vstp_interface6" + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan = ["13"] +} +resource "junos_vstp_interface" "testacc_vstp_interface6" { + name = var.interface + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan_group = junos_vstp_vlan_group.testacc_vstp_interface6.name +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/variables.tf b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/variables.tf new file mode 100644 index 00000000..d5ed041c --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/1/variables.tf @@ -0,0 +1,3 @@ +variable "interface" { + type = string +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/main.tf new file mode 100644 index 00000000..133ca2a4 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/main.tf @@ -0,0 +1,63 @@ +resource "junos_vstp_interface" "testacc_vstp_interface" { + name = "all" + bpdu_timeout_action_alarm = true + bpdu_timeout_action_block = true + cost = 101 + edge = true + priority = 32 +} +resource "junos_vstp_vlan" "testacc_vstp_interface2" { + vlan_id = "10" +} +resource "junos_interface_physical" "testacc_vstp_interface2" { + name = var.interface + description = "testacc_vstp_interface2" + vlan_members = ["15"] +} +resource "junos_vstp_interface" "testacc_vstp_interface2" { + name = junos_interface_physical.testacc_vstp_interface2.name + access_trunk = true + mode = "shared" + no_root_port = true + vlan = junos_vstp_vlan.testacc_vstp_interface2.vlan_id +} +resource "junos_vstp_vlan_group" "testacc_vstp_interface3" { + name = "testacc_vstp_interface2" + vlan = ["11"] +} +resource "junos_vstp_interface" "testacc_vstp_interface3" { + name = "all" + priority = 32 + vlan_group = junos_vstp_vlan_group.testacc_vstp_interface3.name +} +resource "junos_routing_instance" "testacc_vstp_interface" { + name = "testacc_vstp_intface" + type = "virtual-switch" +} +resource "junos_vstp_interface" "testacc_vstp_interface4" { + name = "all" + edge = true + routing_instance = junos_routing_instance.testacc_vstp_interface.name +} +resource "junos_vstp_vlan" "testacc_vstp_interface5" { + vlan_id = "all" + routing_instance = junos_routing_instance.testacc_vstp_interface.name +} +resource "junos_vstp_interface" "testacc_vstp_interface5" { + name = "all" + mode = "point-to-point" + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan = junos_vstp_vlan.testacc_vstp_interface5.vlan_id +} +resource "junos_vstp_vlan_group" "testacc_vstp_interface6" { + name = "testacc_vstp_interface6" + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan = ["13"] +} +resource "junos_vstp_interface" "testacc_vstp_interface6" { + name = var.interface + no_root_port = true + priority = 64 + routing_instance = junos_routing_instance.testacc_vstp_interface.name + vlan_group = junos_vstp_vlan_group.testacc_vstp_interface6.name +} diff --git a/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/variables.tf b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/variables.tf new file mode 100644 index 00000000..d5ed041c --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVstpInterface_basic/2/variables.tf @@ -0,0 +1,3 @@ +variable "interface" { + type = string +} diff --git a/internal/providersdk/func_resource_vstp_vlan.go b/internal/providersdk/func_resource_vstp_vlan.go deleted file mode 100644 index 8b96f1ef..00000000 --- a/internal/providersdk/func_resource_vstp_vlan.go +++ /dev/null @@ -1,23 +0,0 @@ -package providersdk - -import "github.com/jeremmfr/terraform-provider-junos/internal/junos" - -func checkVstpVlanExists(vlanID, routingInstance string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan " + vlanID + junos.PipeDisplaySet) - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} diff --git a/internal/providersdk/func_resource_vstp_vlan_group.go b/internal/providersdk/func_resource_vstp_vlan_group.go deleted file mode 100644 index 5da0cba9..00000000 --- a/internal/providersdk/func_resource_vstp_vlan_group.go +++ /dev/null @@ -1,23 +0,0 @@ -package providersdk - -import "github.com/jeremmfr/terraform-provider-junos/internal/junos" - -func checkVstpVlanGroupExists(name, routingInstance string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan-group group " + name + junos.PipeDisplaySet) - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index 90092a76..a220c26e 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -228,8 +228,6 @@ func Provider() *schema.Provider { "junos_system_ntp_server": resourceSystemNtpServer(), "junos_system_root_authentication": resourceSystemRootAuthentication(), "junos_system_services_dhcp_localserver_group": resourceSystemServicesDhcpLocalServerGroup(), - - "junos_vstp_interface": resourceVstpInterface(), }, DataSourcesMap: map[string]*schema.Resource{ "junos_system_information": dataSourceSystemInformation(), diff --git a/internal/providersdk/resource_vstp_interface.go b/internal/providersdk/resource_vstp_interface.go deleted file mode 100644 index 7aff11f4..00000000 --- a/internal/providersdk/resource_vstp_interface.go +++ /dev/null @@ -1,792 +0,0 @@ -package providersdk - -import ( - "context" - "errors" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type vstpInterfaceOptions struct { - accessTrunk bool - bpduTimeoutActionAlarm bool - bpduTimeoutActionBlock bool - edge bool - noRootPort bool - cost int - priority int - mode string - name string - routingInstance string - vlan string - vlanGroup string -} - -func resourceVstpInterface() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceVstpInterfaceCreate, - ReadWithoutTimeout: resourceVstpInterfaceRead, - UpdateWithoutTimeout: resourceVstpInterfaceUpdate, - DeleteWithoutTimeout: resourceVstpInterfaceDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceVstpInterfaceImport, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if strings.Count(value, ".") > 0 { - errors = append(errors, fmt.Errorf( - "%q in %q cannot have a dot", value, k)) - } - - return - }, - }, - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: junos.DefaultW, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - }, - "vlan": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile( - `^all|[0-9]{1,4}$`), "must be 'all' or a VLAN id"), - ConflictsWith: []string{"vlan_group"}, - }, - "vlan_group": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), - ConflictsWith: []string{"vlan"}, - }, - "access_trunk": { - Type: schema.TypeBool, - Optional: true, - }, - "bpdu_timeout_action_alarm": { - Type: schema.TypeBool, - Optional: true, - }, - "bpdu_timeout_action_block": { - Type: schema.TypeBool, - Optional: true, - }, - "cost": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 200000000), - }, - "edge": { - Type: schema.TypeBool, - Optional: true, - }, - "mode": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"point-to-point", "shared"}, false), - }, - "no_root_port": { - Type: schema.TypeBool, - Optional: true, - }, - "priority": { - Type: schema.TypeInt, - Optional: true, - Default: -1, - ValidateFunc: validation.IntBetween(0, 240), - }, - }, - } -} - -type vstpInterfaceVIdType int - -const ( - vstpInterfaceVIdTypeNone vstpInterfaceVIdType = iota - vstpInterfaceVIdTypeVlan - vstpInterfaceVIdTypeVlanGroup -) - -func (vType vstpInterfaceVIdType) prefix() string { - switch vType { - case vstpInterfaceVIdTypeNone: - return "" - case vstpInterfaceVIdTypeVlan: - return "v_" - case vstpInterfaceVIdTypeVlanGroup: - return "vg_" - } - - return "" -} - -func resourceVstpInterfaceNewID(d *schema.ResourceData) string { - name := d.Get("name").(string) - routingInstance := d.Get("routing_instance").(string) - vlan := d.Get("vlan").(string) - vlanGroup := d.Get("vlan_group").(string) - switch { - case vlan != "": - return name + junos.IDSeparator + - vstpInterfaceVIdTypeVlan.prefix() + vlan + junos.IDSeparator + - routingInstance - case vlanGroup != "": - return name + junos.IDSeparator + - vstpInterfaceVIdTypeVlanGroup.prefix() + vlanGroup + junos.IDSeparator + - routingInstance - default: - return name + junos.IDSeparator + - vstpInterfaceVIdTypeNone.prefix() + junos.IDSeparator + - routingInstance - } -} - -func resourceVstpInterfaceReadID(resourceID string) (vType vstpInterfaceVIdType, name, vName, routingInstnace string) { - ressIDSplit := strings.Split(resourceID, junos.IDSeparator) - switch len(ressIDSplit) { - case 1: - return vstpInterfaceVIdTypeNone, - ressIDSplit[0], - "", - "" - case 2: - return vstpInterfaceVIdTypeNone, - ressIDSplit[0], - "", - ressIDSplit[1] - default: - switch { - case balt.CutPrefixInString(&ressIDSplit[1], vstpInterfaceVIdTypeVlan.prefix()): - return vstpInterfaceVIdTypeVlan, - ressIDSplit[0], - ressIDSplit[1], - ressIDSplit[2] - case balt.CutPrefixInString(&ressIDSplit[1], vstpInterfaceVIdTypeVlanGroup.prefix()): - return vstpInterfaceVIdTypeVlanGroup, - ressIDSplit[0], - ressIDSplit[1], - ressIDSplit[2] - default: - return vstpInterfaceVIdTypeNone, - ressIDSplit[0], - ressIDSplit[1], - ressIDSplit[2] - } - } -} - -func resourceVstpInterfaceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setVstpInterface(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(resourceVstpInterfaceNewID(d)) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - vstpInterfaceVIdType, name, _, routingInstance := resourceVstpInterfaceReadID(resourceVstpInterfaceNewID(d)) - vlan := d.Get("vlan").(string) - vlanGroup := d.Get("vlan_group").(string) - if routingInstance != junos.DefaultW { - instanceExists, err := checkRoutingInstanceExists(routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !instanceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, - diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", routingInstance))...) - } - } - if vlan != "" { - vstpVlanExists, err := checkVstpVlanExists(vlan, routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !vstpVlanExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - if routingInstance == junos.DefaultW { - return append(diagWarns, - diag.FromErr(fmt.Errorf("protocol vstp vlan %v doesn't exist", vlan))...) - } - - return append(diagWarns, - diag.FromErr(fmt.Errorf("protocol vstp vlan %v in routing-instance %v doesn't exist", vlan, routingInstance))...) - } - } - if vlanGroup != "" { - vstpVlanGroupExists, err := checkVstpVlanGroupExists(vlanGroup, routingInstance, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if !vstpVlanGroupExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - if routingInstance == junos.DefaultW { - return append(diagWarns, - diag.FromErr(fmt.Errorf("protocol vstp vlan-group group %v doesn't exist", vlanGroup))...) - } - - return append(diagWarns, - diag.FromErr(fmt.Errorf( - "protocol vstp vlan-group group %v in routing-instance %v doesn't exist", vlanGroup, routingInstance))...) - } - } - vstpInterfaceExists, err := checkVstpInterfaceExists(name, routingInstance, vlan, vlanGroup, junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if vstpInterfaceExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - switch vstpInterfaceVIdType { - case vstpInterfaceVIdTypeNone: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp interface %v already exists", - name))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v already exists in routing-instance %v", name, routingInstance))...) - case vstpInterfaceVIdTypeVlan: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp interface %v in vlan %v already exists", - name, vlan))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v already exists in vlan %s in routing-instance %v", - name, vlan, routingInstance))...) - case vstpInterfaceVIdTypeVlanGroup: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp interface %v in vlan-group %v already exists", - name, vlanGroup))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v already exists in vlan-group %v in routing-instance %v", - name, vlanGroup, routingInstance))...) - } - } - if err := setVstpInterface(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_vstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - vstpInterfaceExists, err = checkVstpInterfaceExists(name, routingInstance, vlan, vlanGroup, junSess) - if err != nil { - return append(diagWarns, diag.FromErr(err)...) - } - if vstpInterfaceExists { - d.SetId(resourceVstpInterfaceNewID(d)) - } else { - switch vstpInterfaceVIdType { - case vstpInterfaceVIdTypeNone: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf("protocols vstp interface %v not exists after commit "+ - "=> check your config", name))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v not exists in routing-instance %v after commit "+ - "=> check your config", name, routingInstance))...) - case vstpInterfaceVIdTypeVlan: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v in vlan %v not exists after commit "+ - "=> check your config", name, vlan))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v not exists in vlan %v in routing-instance %v after commit "+ - "=> check your config", name, vlan, routingInstance))...) - case vstpInterfaceVIdTypeVlanGroup: - if routingInstance == junos.DefaultW { - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v in vlan-group %v not exists after commit "+ - "=> check your config", name, vlanGroup))...) - } - - return append(diagWarns, diag.FromErr(fmt.Errorf( - "protocols vstp interface %v not exists in vlan-group %v in routing-instance %v after commit "+ - "=> check your config", - name, vlanGroup, routingInstance))...) - } - } - - return append(diagWarns, resourceVstpInterfaceReadWJunSess(d, junSess)...) -} - -func resourceVstpInterfaceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceVstpInterfaceReadWJunSess(d, junSess) -} - -func resourceVstpInterfaceReadWJunSess(d *schema.ResourceData, junSess *junos.Session, -) diag.Diagnostics { - junos.MutexLock() - vstpInterfaceOptions, err := readVstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - d.Get("vlan").(string), - d.Get("vlan_group").(string), - junSess, - ) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - if vstpInterfaceOptions.name == "" { - d.SetId("") - } else { - fillVstpInterfaceData(d, vstpInterfaceOptions) - } - - return nil -} - -func resourceVstpInterfaceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - d.Get("vlan").(string), - d.Get("vlan_group").(string), - junSess, - ); err != nil { - return diag.FromErr(err) - } - if err := setVstpInterface(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - d.Get("vlan").(string), - d.Get("vlan_group").(string), - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setVstpInterface(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_vstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceVstpInterfaceReadWJunSess(d, junSess)...) -} - -func resourceVstpInterfaceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delVstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - d.Get("vlan").(string), - d.Get("vlan_group").(string), - junSess, - ); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delVstpInterface( - d.Get("name").(string), - d.Get("routing_instance").(string), - d.Get("vlan").(string), - d.Get("vlan_group").(string), - junSess, - ); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_vstp_interface") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceVstpInterfaceImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - result := make([]*schema.ResourceData, 1) - if len(strings.Split(d.Id(), junos.IDSeparator)) < 3 { - return nil, fmt.Errorf("missing element(s) in id with separator %v", junos.IDSeparator) - } - vType, name, vName, routingInstance := resourceVstpInterfaceReadID(d.Id()) - var vstpInterfaceExists bool - switch vType { - case vstpInterfaceVIdTypeNone: - vstpInterfaceExists, err = checkVstpInterfaceExists(name, routingInstance, "", "", junSess) - case vstpInterfaceVIdTypeVlan: - vstpInterfaceExists, err = checkVstpInterfaceExists(name, routingInstance, vName, "", junSess) - case vstpInterfaceVIdTypeVlanGroup: - vstpInterfaceExists, err = checkVstpInterfaceExists(name, routingInstance, "", vName, junSess) - } - if err != nil { - return nil, err - } - if !vstpInterfaceExists { - return nil, fmt.Errorf("don't find protocols vstp interface with id '%v' "+ - "(id must be "+junos.IDSeparator+junos.IDSeparator+", "+ - ""+junos.IDSeparator+"%s"+junos.IDSeparator+" or "+ - ""+junos.IDSeparator+"%s"+junos.IDSeparator+")", - d.Id(), - vstpInterfaceVIdTypeVlan.prefix(), - vstpInterfaceVIdTypeVlanGroup.prefix(), - ) - } - var vstpInterfaceOptions vstpInterfaceOptions - switch vType { - case vstpInterfaceVIdTypeNone: - vstpInterfaceOptions, err = readVstpInterface(name, routingInstance, "", "", junSess) - case vstpInterfaceVIdTypeVlan: - vstpInterfaceOptions, err = readVstpInterface(name, routingInstance, vName, "", junSess) - case vstpInterfaceVIdTypeVlanGroup: - vstpInterfaceOptions, err = readVstpInterface(name, routingInstance, "", vName, junSess) - } - if err != nil { - return nil, err - } - fillVstpInterfaceData(d, vstpInterfaceOptions) - d.SetId(resourceVstpInterfaceNewID(d)) - result[0] = d - - return result, nil -} - -func checkVstpInterfaceExists(name, routingInstance, vlan, vlanGroup string, junSess *junos.Session, -) (_ bool, err error) { - var showConfig string - if vlan != "" && vlanGroup != "" { - return false, errors.New("internal error: checkVstpInterfaceExists called with vlan and vlanGroup") - } - if routingInstance == junos.DefaultW { - switch { - case vlan != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan " + vlan + " interface " + name + junos.PipeDisplaySet) - case vlanGroup != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan-group group " + vlanGroup + " interface " + name + junos.PipeDisplaySet) - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp interface " + name + junos.PipeDisplaySet) - } - } else { - switch { - case vlan != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan " + vlan + " interface " + name + junos.PipeDisplaySet) - case vlanGroup != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan-group group " + vlanGroup + " interface " + name + junos.PipeDisplaySet) - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp interface " + name + junos.PipeDisplaySet) - } - } - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} - -func setVstpInterface(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - - name := d.Get("name").(string) - vlan := d.Get("vlan").(string) - vlanGroup := d.Get("vlan_group").(string) - setPrefix := junos.SetLS - if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " - } - switch { - case vlan != "": - setPrefix += "protocols vstp vlan " + vlan + " interface " + name + " " - case vlanGroup != "": - setPrefix += "protocols vstp vlan-group group " + vlanGroup + " interface " + name + " " - default: - setPrefix += "protocols vstp interface " + name + " " - } - - configSet = append(configSet, setPrefix) - if d.Get("access_trunk").(bool) { - configSet = append(configSet, setPrefix+"access-trunk") - } - if d.Get("bpdu_timeout_action_alarm").(bool) { - configSet = append(configSet, setPrefix+"bpdu-timeout-action alarm") - } - if d.Get("bpdu_timeout_action_block").(bool) { - configSet = append(configSet, setPrefix+"bpdu-timeout-action block") - } - if v := d.Get("cost").(int); v != 0 { - configSet = append(configSet, setPrefix+"cost "+strconv.Itoa(v)) - } - if d.Get("edge").(bool) { - configSet = append(configSet, setPrefix+"edge") - } - if v := d.Get("mode").(string); v != "" { - configSet = append(configSet, setPrefix+"mode "+v) - } - if d.Get("no_root_port").(bool) { - configSet = append(configSet, setPrefix+"no-root-port") - } - if v := d.Get("priority").(int); v != -1 { - configSet = append(configSet, setPrefix+"priority "+strconv.Itoa(v)) - } - - return junSess.ConfigSet(configSet) -} - -func readVstpInterface(name, routingInstance, vlan, vlanGroup string, junSess *junos.Session, -) (confRead vstpInterfaceOptions, err error) { - // default -1 - confRead.priority = -1 - if vlan != "" && vlanGroup != "" { - return confRead, errors.New("internal error: readVstpInterface called with vlan and vlanGroup") - } - var showConfig string - if routingInstance == junos.DefaultW { - switch { - case vlan != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan " + vlan + " interface " + name + junos.PipeDisplaySetRelative) - case vlanGroup != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp vlan-group group " + vlanGroup + " interface " + name + junos.PipeDisplaySetRelative) - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols vstp interface " + name + junos.PipeDisplaySetRelative) - } - } else { - switch { - case vlan != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan " + vlan + " interface " + name + junos.PipeDisplaySetRelative) - case vlanGroup != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp vlan-group group " + vlanGroup + " interface " + name + junos.PipeDisplaySetRelative) - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols vstp interface " + name + junos.PipeDisplaySetRelative) - } - } - if err != nil { - return confRead, err - } - if showConfig != junos.EmptyW { - confRead.name = name - confRead.routingInstance = routingInstance - confRead.vlan = vlan - confRead.vlanGroup = vlanGroup - 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) - switch { - case itemTrim == "access-trunk": - confRead.accessTrunk = true - case itemTrim == "bpdu-timeout-action alarm": - confRead.bpduTimeoutActionAlarm = true - case itemTrim == "bpdu-timeout-action block": - confRead.bpduTimeoutActionBlock = true - case balt.CutPrefixInString(&itemTrim, "cost "): - confRead.cost, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case itemTrim == "edge": - confRead.edge = true - case balt.CutPrefixInString(&itemTrim, "mode "): - confRead.mode = itemTrim - case itemTrim == "no-root-port": - confRead.noRootPort = true - case balt.CutPrefixInString(&itemTrim, "priority "): - confRead.priority, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - } - } - } - - return confRead, nil -} - -func delVstpInterface(name, routingInstance, vlan, vlanGroup string, junSess *junos.Session) error { - configSet := make([]string, 0, 1) - if vlan != "" && vlanGroup != "" { - return errors.New("internal error: delVstpInterface called with vlan and vlanGroup") - } - if routingInstance == junos.DefaultW { - switch { - case vlan != "": - configSet = append(configSet, "delete protocols vstp vlan "+vlan+" interface "+name) - case vlanGroup != "": - configSet = append(configSet, "delete protocols vstp vlan-group group "+vlanGroup+" interface "+name) - default: - configSet = append(configSet, "delete protocols vstp interface "+name) - } - } else { - switch { - case vlan != "": - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+ - " protocols vstp vlan "+vlan+" interface "+name) - case vlanGroup != "": - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+ - " protocols vstp vlan-group group "+vlanGroup+" interface "+name) - default: - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+ - " protocols vstp interface "+name) - } - } - - return junSess.ConfigSet(configSet) -} - -func fillVstpInterfaceData(d *schema.ResourceData, vstpInterfaceOptions vstpInterfaceOptions) { - if tfErr := d.Set("name", vstpInterfaceOptions.name); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("routing_instance", vstpInterfaceOptions.routingInstance); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("vlan", vstpInterfaceOptions.vlan); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("vlan_group", vstpInterfaceOptions.vlanGroup); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("access_trunk", vstpInterfaceOptions.accessTrunk); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_timeout_action_alarm", vstpInterfaceOptions.bpduTimeoutActionAlarm); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("bpdu_timeout_action_block", vstpInterfaceOptions.bpduTimeoutActionBlock); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("cost", vstpInterfaceOptions.cost); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("edge", vstpInterfaceOptions.edge); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("mode", vstpInterfaceOptions.mode); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("no_root_port", vstpInterfaceOptions.noRootPort); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("priority", vstpInterfaceOptions.priority); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_vstp_interface_test.go b/internal/providersdk/resource_vstp_interface_test.go deleted file mode 100644 index b5b0d27a..00000000 --- a/internal/providersdk/resource_vstp_interface_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package providersdk_test - -import ( - "fmt" - "os" - "testing" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -// export TESTACC_INTERFACE= for choose interface available else it's xe-0/0/3. -func TestAccResourceVstpInterface_basic(t *testing.T) { - if os.Getenv("TESTACC_SWITCH") != "" { - testaccInterface := junos.DefaultInterfaceSwitchTestAcc - if iface := os.Getenv("TESTACC_INTERFACE"); iface != "" { - testaccInterface = iface - } - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceVstpInterfaceSWConfigCreate(testaccInterface), - }, - { - Config: testAccResourceVstpInterfaceSWConfigUpdate(testaccInterface), - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface2", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface3", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface4", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface5", - ImportState: true, - ImportStateVerify: true, - }, - { - ResourceName: "junos_vstp_interface.testacc_vstp_interface6", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceVstpInterfaceSWConfigCreate(interFace string) string { - return fmt.Sprintf(` -resource "junos_vstp_interface" "testacc_vstp_interface" { - name = "all" -} -resource "junos_vstp_vlan" "testacc_vstp_interface2" { - vlan_id = "10" -} -resource "junos_interface_physical" "testacc_vstp_interface2" { - name = "%s" - description = "testacc_vstp_interface2" - vlan_members = ["15"] -} -resource "junos_vstp_interface" "testacc_vstp_interface2" { - name = junos_interface_physical.testacc_vstp_interface2.name - vlan = junos_vstp_vlan.testacc_vstp_interface2.vlan_id -} -resource "junos_vstp_vlan_group" "testacc_vstp_interface3" { - name = "testacc_vstp_interface2" - vlan = ["11"] -} -resource "junos_vstp_interface" "testacc_vstp_interface3" { - name = "all" - vlan_group = junos_vstp_vlan_group.testacc_vstp_interface3.name -} -resource "junos_routing_instance" "testacc_vstp_interface" { - name = "testacc_vstp_intface" - type = "virtual-switch" -} -resource "junos_vstp_interface" "testacc_vstp_interface4" { - name = "all" - routing_instance = junos_routing_instance.testacc_vstp_interface.name -} -resource "junos_vstp_vlan" "testacc_vstp_interface5" { - vlan_id = "all" - routing_instance = junos_routing_instance.testacc_vstp_interface.name -} -resource "junos_vstp_interface" "testacc_vstp_interface5" { - name = "all" - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan = junos_vstp_vlan.testacc_vstp_interface5.vlan_id -} -resource "junos_vstp_vlan_group" "testacc_vstp_interface6" { - name = "testacc_vstp_interface6" - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan = ["13"] -} -resource "junos_vstp_interface" "testacc_vstp_interface6" { - name = "%s" - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan_group = junos_vstp_vlan_group.testacc_vstp_interface6.name -} -`, interFace, interFace) -} - -func testAccResourceVstpInterfaceSWConfigUpdate(interFace string) string { - return fmt.Sprintf(` -resource "junos_vstp_interface" "testacc_vstp_interface" { - name = "all" - bpdu_timeout_action_alarm = true - bpdu_timeout_action_block = true - cost = 101 - edge = true - priority = 32 -} -resource "junos_vstp_vlan" "testacc_vstp_interface2" { - vlan_id = "10" -} -resource "junos_interface_physical" "testacc_vstp_interface2" { - name = "%s" - description = "testacc_vstp_interface2" - vlan_members = ["15"] -} -resource "junos_vstp_interface" "testacc_vstp_interface2" { - name = junos_interface_physical.testacc_vstp_interface2.name - access_trunk = true - mode = "shared" - no_root_port = true - vlan = junos_vstp_vlan.testacc_vstp_interface2.vlan_id -} -resource "junos_vstp_vlan_group" "testacc_vstp_interface3" { - name = "testacc_vstp_interface2" - vlan = ["11"] -} -resource "junos_vstp_interface" "testacc_vstp_interface3" { - name = "all" - priority = 32 - vlan_group = junos_vstp_vlan_group.testacc_vstp_interface3.name -} -resource "junos_routing_instance" "testacc_vstp_interface" { - name = "testacc_vstp_intface" - type = "virtual-switch" -} -resource "junos_vstp_interface" "testacc_vstp_interface4" { - name = "all" - edge = true - routing_instance = junos_routing_instance.testacc_vstp_interface.name -} -resource "junos_vstp_vlan" "testacc_vstp_interface5" { - vlan_id = "all" - routing_instance = junos_routing_instance.testacc_vstp_interface.name -} -resource "junos_vstp_interface" "testacc_vstp_interface5" { - name = "all" - mode = "point-to-point" - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan = junos_vstp_vlan.testacc_vstp_interface5.vlan_id -} -resource "junos_vstp_vlan_group" "testacc_vstp_interface6" { - name = "testacc_vstp_interface6" - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan = ["13"] -} -resource "junos_vstp_interface" "testacc_vstp_interface6" { - name = "%s" - no_root_port = true - priority = 64 - routing_instance = junos_routing_instance.testacc_vstp_interface.name - vlan_group = junos_vstp_vlan_group.testacc_vstp_interface6.name -} -`, interFace, interFace) -} From 5db0fe1f485106e38c6e31fab6ae1d78b94623c0 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 11 Jun 2024 13:24:24 +0200 Subject: [PATCH 17/30] r/*: providerfwk: adjust code lines in same way for - show config command lines - set/delete prefix definition lines - set/read routing_instance value --- internal/junos/constants.go | 4 +- .../providerfwk/resource_aggregate_route.go | 21 +-- internal/providerfwk/resource_application.go | 5 +- .../providerfwk/resource_application_set.go | 4 +- internal/providerfwk/resource_bgp_group.go | 91 +++++----- internal/providerfwk/resource_bgp_neighbor.go | 102 +++++------- .../providerfwk/resource_bridge_domain.go | 103 +++++------- .../resource_eventoptions_destination.go | 4 +- .../resource_eventoptions_generate_event.go | 4 +- .../resource_eventoptions_policy.go | 15 +- internal/providerfwk/resource_evpn.go | 57 +++---- .../providerfwk/resource_firewall_filter.go | 4 +- .../providerfwk/resource_firewall_policer.go | 6 +- .../resource_forwardingoptions_evpn_vxlan.go | 9 +- .../resource_forwardingoptions_sampling.go | 27 ++- ...rce_forwardingoptions_sampling_instance.go | 47 +++--- ...forwardingoptions_storm_control_profile.go | 6 +- .../providerfwk/resource_generate_route.go | 22 ++- internal/providerfwk/resource_iccp.go | 6 +- internal/providerfwk/resource_iccp_peer.go | 4 +- .../providerfwk/resource_interface_logical.go | 12 +- .../resource_interface_physical.go | 3 +- internal/providerfwk/resource_multichassis.go | 6 +- .../resource_multichassis_protection_peer.go | 5 +- .../resource_oam_gretunnel_interface.go | 5 +- internal/providerfwk/resource_ospf.go | 44 ++--- internal/providerfwk/resource_ospf_area.go | 155 +++++++----------- .../resource_policyoptions_as_path.go | 4 +- .../resource_policyoptions_as_path_group.go | 4 +- .../resource_policyoptions_community.go | 4 +- ...resource_policyoptions_policy_statement.go | 6 +- .../resource_policyoptions_prefix_list.go | 4 +- .../providerfwk/resource_routing_instance.go | 10 +- internal/providerfwk/resource_security.go | 26 ++- .../resource_security_address_book.go | 4 +- .../resource_security_global_policy.go | 4 +- .../resource_security_ike_gateway.go | 6 +- .../resource_security_ike_policy.go | 4 +- .../resource_security_ike_proposal.go | 4 +- .../resource_security_ipsec_policy.go | 4 +- .../resource_security_ipsec_proposal.go | 4 +- .../resource_security_ipsec_vpn.go | 4 +- .../resource_security_nat_destination_pool.go | 1 + .../resource_security_nat_static.go | 4 +- .../resource_security_nat_static_rule.go | 1 + .../providerfwk/resource_security_policy.go | 4 +- ...urce_security_policy_tunnel_pair_policy.go | 4 +- .../providerfwk/resource_security_zone.go | 5 +- .../resource_security_zone_book_address.go | 4 +- ...resource_security_zone_book_address_set.go | 4 +- ...rce_services_flowmonitoring_v9_template.go | 4 +- ...services_flowmonitoring_vipfix_template.go | 4 +- internal/providerfwk/resource_snmp.go | 7 +- .../providerfwk/resource_snmp_clientlist.go | 5 +- .../providerfwk/resource_snmp_community.go | 6 +- .../providerfwk/resource_snmp_v3_community.go | 5 +- .../providerfwk/resource_snmp_v3_usm_user.go | 99 +++++------ .../resource_snmp_v3_vacm_accessgroup.go | 6 +- .../resource_snmp_v3_vacm_securitytogroup.go | 4 +- internal/providerfwk/resource_snmp_view.go | 6 +- internal/providerfwk/resource_static_route.go | 23 +-- .../providerfwk/resource_switch_options.go | 4 +- internal/providerfwk/resource_system.go | 6 +- .../resource_system_radius_server.go | 7 +- .../resource_system_syslog_file.go | 4 +- .../resource_system_syslog_host.go | 4 +- .../resource_system_syslog_user.go | 4 +- .../resource_system_tacplus_server.go | 7 +- .../providerfwk/resource_virtual_chassis.go | 9 +- internal/providerfwk/resource_vlan.go | 6 +- internal/providerfwk/resourcedata_bgp.go | 4 + .../providerfwk/upgradestate_bridge_domain.go | 8 +- internal/providersdk/constants.go | 7 + ...resource_access_address_assignment_pool.go | 4 +- .../resource_forwardingoptions_dhcprelay.go | 6 +- ...ource_forwardingoptions_dhcprelay_group.go | 6 +- ...forwardingoptions_dhcprelay_servergroup.go | 6 +- .../resource_igmp_snooping_vlan.go | 4 +- internal/providersdk/resource_rip_group.go | 4 +- internal/providersdk/resource_rip_neighbor.go | 4 +- ..._system_services_dhcp_localserver_group.go | 6 +- 81 files changed, 499 insertions(+), 670 deletions(-) diff --git a/internal/junos/constants.go b/internal/junos/constants.go index 377cb058..8ac8049a 100644 --- a/internal/junos/constants.go +++ b/internal/junos/constants.go @@ -13,9 +13,7 @@ const ( PipeDisplaySet = " | display set" PipeDisplaySetRelative = PipeDisplaySet + " relative" - RoutingInstancesWS = "routing-instances " // routing-instances word + space - SetRoutingInstances = SetLS + RoutingInstancesWS - DelRoutingInstances = DeleteLS + RoutingInstancesWS + RoutingInstancesWS = "routing-instances " // routing-instances word + space RoutingOptionsWS = "routing-options " RibInet60WS = "rib inet6.0 " diff --git a/internal/providerfwk/resource_aggregate_route.go b/internal/providerfwk/resource_aggregate_route.go index 15f26955..ad898524 100644 --- a/internal/providerfwk/resource_aggregate_route.go +++ b/internal/providerfwk/resource_aggregate_route.go @@ -493,7 +493,7 @@ func (rsc *aggregateRoute) ImportState( func checkAggregateRouteExists( _ context.Context, destination, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showPrefix := junos.CmdShowConfig switch routingInstance { @@ -508,11 +508,11 @@ func checkAggregateRouteExists( showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "aggregate route " + destination + junos.PipeDisplaySet) + showConfig, err := junSess.Command(showPrefix + + "aggregate route " + destination + junos.PipeDisplaySet) if err != nil { return false, err } - if showConfig == junos.EmptyW { return false, nil } @@ -551,6 +551,7 @@ func (rscData *aggregateRouteData) set( } } setPrefix += "aggregate route " + rscData.Destination.ValueString() + " " + configSet := []string{ setPrefix, } @@ -603,9 +604,7 @@ func (rscData *aggregateRouteData) set( func (rscData *aggregateRouteData) read( _ context.Context, destination, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig switch routingInstance { case junos.DefaultW, "": @@ -619,16 +618,17 @@ func (rscData *aggregateRouteData) read( showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "aggregate route " + destination + junos.PipeDisplaySetRelative) + showConfig, err := junSess.Command(showPrefix + + "aggregate route " + destination + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { rscData.Destination = types.StringValue(destination) - rscData.RoutingInstance = types.StringValue(routingInstance) - if rscData.RoutingInstance.ValueString() == "" { + if routingInstance == "" { rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) } rscData.fillID() for _, item := range strings.Split(showConfig, "\n") { @@ -700,6 +700,7 @@ func (rscData *aggregateRouteData) del( delPrefix += "rib " + routingInstance + ".inet6.0 " } } + configSet := []string{ delPrefix + "aggregate route " + rscData.Destination.ValueString(), } diff --git a/internal/providerfwk/resource_application.go b/internal/providerfwk/resource_application.go index dc8cbf61..de049e4e 100644 --- a/internal/providerfwk/resource_application.go +++ b/internal/providerfwk/resource_application.go @@ -692,6 +692,7 @@ func (block *applicationBlockTerm) configSet( error, // error ) { setPrefix += "term " + block.Name.ValueString() + " " + configSet := []string{ setPrefix, setPrefix + "protocol " + block.Protocol.ValueString(), @@ -742,9 +743,7 @@ func (block *applicationBlockTerm) configSet( func (rscData *applicationData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "applications application " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_application_set.go b/internal/providerfwk/resource_application_set.go index 84e20346..6dba3b5f 100644 --- a/internal/providerfwk/resource_application_set.go +++ b/internal/providerfwk/resource_application_set.go @@ -358,9 +358,7 @@ func (rscData *applicationSetData) set( func (rscData *applicationSetData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "applications application-set " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_bgp_group.go b/internal/providerfwk/resource_bgp_group.go index ee7f76a9..01d2738d 100644 --- a/internal/providerfwk/resource_bgp_group.go +++ b/internal/providerfwk/resource_bgp_group.go @@ -888,7 +888,7 @@ func (rsc *bgpGroup) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("advertise_peer_as"), tfdiag.ConflictConfigErrSummary, - "advertise_peer_as and no_advertise_peer_as can't be true in same time ", + "advertise_peer_as and no_advertise_peer_as can't be true in same time", ) } if !config.KeepAll.IsNull() && !config.KeepAll.IsUnknown() && @@ -896,7 +896,7 @@ func (rsc *bgpGroup) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("keep_all"), tfdiag.ConflictConfigErrSummary, - "keep_all and keep_none can't be true in same time ", + "keep_all and keep_none can't be true in same time", ) } if !config.AuthenticationKey.IsNull() && !config.AuthenticationKey.IsUnknown() { @@ -1084,7 +1084,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_evpn").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_evpn block ", + " in accepted_prefix_limit block in family_evpn block", ) } } @@ -1104,7 +1104,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_evpn").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_evpn block ", + " in prefix_limit block family_evpn block", ) } } @@ -1147,7 +1147,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_inet").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_inet block ", + " in accepted_prefix_limit block in family_inet block", ) } } @@ -1167,7 +1167,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_inet").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_inet block ", + " in prefix_limit block family_inet block", ) } } @@ -1210,7 +1210,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_inet6").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_inet6 block ", + " in accepted_prefix_limit block in family_inet6 block", ) } } @@ -1230,7 +1230,7 @@ func (rsc *bgpGroup) ValidateConfig( path.Root("family_inet6").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_inet6 block ", + " in prefix_limit block family_inet6 block", ) } } @@ -1537,22 +1537,16 @@ func checkBgpGroupExists( routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { - var showConfig string - if routingInstance == "" || routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySet) - if err != nil { - return false, err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySet) - if err != nil { - return false, err - } + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySet) + if err != nil { + return false, err } if showConfig == junos.EmptyW { return false, nil @@ -1578,11 +1572,12 @@ func (rscData *bgpGroupData) set( ) ( path.Path, error, ) { - setPrefix := "set protocols bgp group \"" + rscData.Name.ValueString() + "\" " + setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + - " protocols bgp group \"" + rscData.Name.ValueString() + "\" " + setPrefix += junos.RoutingInstancesWS + v + " " } + setPrefix += "protocols bgp group \"" + rscData.Name.ValueString() + "\" " + configSet := []string{ setPrefix + "type " + rscData.Type.ValueString(), } @@ -1789,23 +1784,15 @@ func (rscData *bgpGroupData) set( func (rscData *bgpGroupData) read( _ context.Context, name, routingInstance string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string - if routingInstance == "" || routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySetRelative) - if err != nil { - return err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySetRelative) - if err != nil { - return err - } +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols bgp group \"" + name + "\"" + junos.PipeDisplaySetRelative) + if err != nil { + return err } if showConfig != junos.EmptyW { rscData.Name = types.StringValue(name) @@ -2020,11 +2007,11 @@ func (rscData *bgpGroupData) delOpts( _ context.Context, junSess *junos.Session, ) error { configSet := make([]string, 0) - delPrefix := "delete protocols bgp group \"" + rscData.Name.ValueString() + "\" " + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + - " protocols bgp group \"" + rscData.Name.ValueString() + "\" " + delPrefix += junos.RoutingInstancesWS + v + " " } + delPrefix += "protocols bgp group \"" + rscData.Name.ValueString() + "\" " configSet = append(configSet, delPrefix+"accept-remote-nexthop", @@ -2073,13 +2060,13 @@ func (rscData *bgpGroupData) delOpts( func (rscData *bgpGroupData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 1) + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet[0] = junos.DelRoutingInstances + v + - " protocols bgp group \"" + rscData.Name.ValueString() + "\"" - } else { - configSet[0] = junos.DeleteW + - " protocols bgp group \"" + rscData.Name.ValueString() + "\"" + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "protocols bgp group \"" + rscData.Name.ValueString() + "\"", } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_bgp_neighbor.go b/internal/providerfwk/resource_bgp_neighbor.go index 24dbbdcd..0936be5b 100644 --- a/internal/providerfwk/resource_bgp_neighbor.go +++ b/internal/providerfwk/resource_bgp_neighbor.go @@ -887,7 +887,7 @@ func (rsc *bgpNeighbor) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("advertise_peer_as"), tfdiag.ConflictConfigErrSummary, - "advertise_peer_as and no_advertise_peer_as can't be true in same time ", + "advertise_peer_as and no_advertise_peer_as can't be true in same time", ) } if !config.KeepAll.IsNull() && !config.KeepAll.IsUnknown() && @@ -895,7 +895,7 @@ func (rsc *bgpNeighbor) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("keep_all"), tfdiag.ConflictConfigErrSummary, - "keep_all and keep_none can't be true in same time ", + "keep_all and keep_none can't be true in same time", ) } if !config.AuthenticationKey.IsNull() && !config.AuthenticationKey.IsUnknown() { @@ -1083,7 +1083,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_evpn").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_evpn block ", + " in accepted_prefix_limit block in family_evpn block", ) } } @@ -1103,7 +1103,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_evpn").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_evpn block ", + " in prefix_limit block family_evpn block", ) } } @@ -1146,7 +1146,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_inet").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_inet block ", + " in accepted_prefix_limit block in family_inet block", ) } } @@ -1166,7 +1166,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_inet").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_inet block ", + " in prefix_limit block family_inet block", ) } } @@ -1209,7 +1209,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_inet6").AtListIndex(i).AtName("accepted_prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in accepted_prefix_limit block in family_inet6 block ", + " in accepted_prefix_limit block in family_inet6 block", ) } } @@ -1229,7 +1229,7 @@ func (rsc *bgpNeighbor) ValidateConfig( path.Root("family_inet6").AtListIndex(i).AtName("prefix_limit").AtName("teardown_idle_timeout"), tfdiag.ConflictConfigErrSummary, "teardown_idle_timeout and teardown_idle_timeout_forever cannot be configured together"+ - " in prefix_limit block family_inet6 block ", + " in prefix_limit block family_inet6 block", ) } } @@ -1573,24 +1573,16 @@ func checkBgpNeighborExists( group string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { - var showConfig string - if routingInstance == junos.DefaultW || routingInstance == "" { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols bgp group \"" + group + "\"" + - " neighbor " + ip + junos.PipeDisplaySet) - if err != nil { - return false, err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "protocols bgp group \"" + group + "\"" + - " neighbor " + ip + junos.PipeDisplaySet) - if err != nil { - return false, err - } + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols bgp group \"" + group + "\" neighbor " + ip + junos.PipeDisplaySet) + if err != nil { + return false, err } if showConfig == junos.EmptyW { return false, nil @@ -1624,13 +1616,12 @@ func (rscData *bgpNeighborData) set( ) ( path.Path, error, ) { - setPrefix := "set protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() + " " + setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + - " protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() + " " + setPrefix += junos.RoutingInstancesWS + v + " " } + setPrefix += "protocols bgp group \"" + rscData.Group.ValueString() + "\" neighbor " + rscData.IP.ValueString() + " " + configSet := []string{ setPrefix, } @@ -1841,25 +1832,15 @@ func (rscData *bgpNeighborData) read( routingInstance, group string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols bgp group \"" + group + "\"" + - " neighbor " + ip + junos.PipeDisplaySetRelative) - if err != nil { - return err - } - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "protocols bgp group \"" + group + "\"" + - " neighbor " + ip + junos.PipeDisplaySetRelative) - if err != nil { - return err - } +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols bgp group \"" + group + "\" neighbor " + ip + junos.PipeDisplaySetRelative) + if err != nil { + return err } if showConfig != junos.EmptyW { rscData.IP = types.StringValue(ip) @@ -2073,14 +2054,11 @@ func (rscData *bgpNeighborData) delOpts( _ context.Context, junSess *junos.Session, ) error { configSet := make([]string, 0) - delPrefix := junos.DeleteW + - " protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() + " " + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + - " protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() + " " + delPrefix += junos.RoutingInstancesWS + v + " " } + delPrefix += "protocols bgp group \"" + rscData.Group.ValueString() + "\" neighbor " + rscData.IP.ValueString() + " " configSet = append(configSet, delPrefix+"accept-remote-nexthop", @@ -2129,15 +2107,13 @@ func (rscData *bgpNeighborData) delOpts( func (rscData *bgpNeighborData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 1) + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet[0] = junos.DelRoutingInstances + v + - " protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() - } else { - configSet[0] = junos.DeleteW + - " protocols bgp group \"" + rscData.Group.ValueString() + "\"" + - " neighbor " + rscData.IP.ValueString() + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "protocols bgp group \"" + rscData.Group.ValueString() + "\" neighbor " + rscData.IP.ValueString(), } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_bridge_domain.go b/internal/providerfwk/resource_bridge_domain.go index 48cd2921..a0e1cbc7 100644 --- a/internal/providerfwk/resource_bridge_domain.go +++ b/internal/providerfwk/resource_bridge_domain.go @@ -219,6 +219,13 @@ func (rsc *bridgeDomain) Schema( int64validator.Between(0, 16777214), }, }, + "vni_extend_evpn": schema.BoolAttribute{ + Optional: true, + Description: "Extend VNI to EVPN.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, "decapsulate_accept_inner_vlan": schema.BoolAttribute{ Optional: true, Description: "Accept VXLAN packets with inner VLAN.", @@ -261,13 +268,6 @@ func (rsc *bridgeDomain) Schema( int64validator.Between(300, 1800), }, }, - "vni_extend_evpn": schema.BoolAttribute{ - Optional: true, - Description: "Extend VNI to EVPN.", - Validators: []validator.Bool{ - tfvalidator.BoolTrue(), - }, - }, }, PlanModifiers: []planmodifier.Object{ tfplanmodifier.BlockRemoveNull(), @@ -316,14 +316,14 @@ func (rscConfig *bridgeDomainConfig) isEmpty() bool { } type bridgeDomainBlockVXLAN struct { - VNI types.Int64 `tfsdk:"vni"` + Vni types.Int64 `tfsdk:"vni"` + VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` MulticastGroup types.String `tfsdk:"multicast_group"` OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` - VNIExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` } func (rsc *bridgeDomain) ValidateConfig( @@ -352,7 +352,7 @@ func (rsc *bridgeDomain) ValidateConfig( ) } if config.VXLAN != nil { - if config.VXLAN.VNI.IsNull() { + if config.VXLAN.Vni.IsNull() { resp.Diagnostics.AddAttributeError( path.Root("vxlan").AtName("vni"), tfdiag.MissingConfigErrSummary, @@ -554,17 +554,17 @@ func (rsc *bridgeDomain) ImportState( func checkBridgeDomainExists( _ context.Context, name, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - showConfig, err := junSess.Command(showPrefix + "bridge-domains \"" + name + "\"" + junos.PipeDisplaySet) + showConfig, err := junSess.Command(showPrefix + + "bridge-domains \"" + name + "\"" + junos.PipeDisplaySet) if err != nil { return false, err } - if showConfig == junos.EmptyW { return false, nil } @@ -590,12 +590,11 @@ func (rscData *bridgeDomainData) set( path.Path, error, ) { configSet := make([]string, 0) - setPrefix := junos.SetLS - routingInstance := rscData.RoutingInstance.ValueString() - if routingInstance != "" && routingInstance != junos.DefaultW { - setPrefix += junos.RoutingInstancesWS + routingInstance + " " + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + setPrefix += junos.RoutingInstancesWS + v + " " } + setPrefixProtocolsEvpn := setPrefix + "protocols evpn " setPrefix += "bridge-domains \"" + rscData.Name.ValueString() + "\" " for _, v := range rscData.CommunityVlans { @@ -634,16 +633,11 @@ func (rscData *bridgeDomainData) set( } if rscData.VXLAN != nil { configSet = append(configSet, setPrefix+"vxlan vni "+ - utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - - if rscData.VXLAN.VNIExtendEvpn.ValueBool() { - if routingInstance != "" && routingInstance != junos.DefaultW { - configSet = append(configSet, junos.SetRoutingInstances+routingInstance+" protocols evpn extended-vni-list "+ - utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } else { - configSet = append(configSet, "set protocols evpn extended-vni-list "+ - utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } + utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + + if rscData.VXLAN.VniExtendEvpn.ValueBool() { + configSet = append(configSet, setPrefixProtocolsEvpn+"extended-vni-list "+ + utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) } if rscData.VXLAN.DecapsulateAcceptInnerVlan.ValueBool() { configSet = append(configSet, setPrefix+"vxlan decapsulate-accept-inner-vlan") @@ -671,18 +665,16 @@ func (rscData *bridgeDomainData) set( func (rscData *bridgeDomainData) read( _ context.Context, name, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - showConfig, err := junSess.Command(showPrefix + "bridge-domains \"" + name + "\"" + junos.PipeDisplaySetRelative) + showConfig, err := junSess.Command(showPrefix + + "bridge-domains \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { rscData.Name = types.StringValue(name) if routingInstance == "" { @@ -738,19 +730,14 @@ func (rscData *bridgeDomainData) read( } switch { case balt.CutPrefixInString(&itemTrim, "vni "): - rscData.VXLAN.VNI, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.VXLAN.Vni, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } - showPrefixEvpn := junos.CmdShowConfig - if routingInstance != "" && routingInstance != junos.DefaultW { - showPrefixEvpn += junos.RoutingInstancesWS + routingInstance + " " - } - showConfigEvpn, err := junSess.Command(showPrefixEvpn + "protocols evpn" + junos.PipeDisplaySetRelative) + showConfigEvpn, err := junSess.Command(showPrefix + "protocols evpn" + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfigEvpn != junos.EmptyW { for _, itemEvpn := range strings.Split(showConfigEvpn, "\n") { if strings.Contains(itemEvpn, junos.XMLStartTagConfigOut) { @@ -760,7 +747,7 @@ func (rscData *bridgeDomainData) read( break } if strings.HasPrefix(itemEvpn, junos.SetLS+"extended-vni-list "+itemTrim) { - rscData.VXLAN.VNIExtendEvpn = types.BoolValue(true) + rscData.VXLAN.VniExtendEvpn = types.BoolValue(true) break } @@ -794,8 +781,9 @@ func (rscData *bridgeDomainData) delOpts( ) error { delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + " " + delPrefix += junos.RoutingInstancesWS + v + " " } + delPrefixProtocolsEvpn := delPrefix + "protocols evpn " delPrefix += "bridge-domains \"" + rscData.Name.ValueString() + "\" " configSet := []string{ @@ -814,14 +802,9 @@ func (rscData *bridgeDomainData) delOpts( configSet = append(configSet, delPrefix+"interface "+v.ValueString()) } if rscData.VXLAN != nil { - if rscData.VXLAN.VNIExtendEvpn.ValueBool() { - if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet = append(configSet, junos.DelRoutingInstances+v+" "+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } else { - configSet = append(configSet, junos.DeleteLS+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } + if rscData.VXLAN.VniExtendEvpn.ValueBool() { + configSet = append(configSet, delPrefixProtocolsEvpn+ + "extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) } } @@ -831,22 +814,18 @@ func (rscData *bridgeDomainData) delOpts( func (rscData *bridgeDomainData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 0, 1) + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet = append(configSet, junos.DelRoutingInstances+v+ - " bridge-domains \""+rscData.Name.ValueString()+"\"") - } else { - configSet = append(configSet, "delete bridge-domains \""+rscData.Name.ValueString()+"\"") + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "bridge-domains \"" + rscData.Name.ValueString() + "\"", } if rscData.VXLAN != nil { - if rscData.VXLAN.VNIExtendEvpn.ValueBool() { - if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet = append(configSet, junos.DelRoutingInstances+v+" "+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } else { - configSet = append(configSet, junos.DeleteLS+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.VNI.ValueInt64())) - } + if rscData.VXLAN.VniExtendEvpn.ValueBool() { + configSet = append(configSet, delPrefix+ + "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) } } diff --git a/internal/providerfwk/resource_eventoptions_destination.go b/internal/providerfwk/resource_eventoptions_destination.go index fc8c5b88..5669b1f6 100644 --- a/internal/providerfwk/resource_eventoptions_destination.go +++ b/internal/providerfwk/resource_eventoptions_destination.go @@ -389,9 +389,7 @@ func (rscData *eventoptionsDestinationData) set( func (rscData *eventoptionsDestinationData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "event-options destinations \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_eventoptions_generate_event.go b/internal/providerfwk/resource_eventoptions_generate_event.go index 98056b00..3646f2e0 100644 --- a/internal/providerfwk/resource_eventoptions_generate_event.go +++ b/internal/providerfwk/resource_eventoptions_generate_event.go @@ -368,9 +368,7 @@ func (rscData *eventoptionsGenerateEventData) set( func (rscData *eventoptionsGenerateEventData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "event-options generate-event \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_eventoptions_policy.go b/internal/providerfwk/resource_eventoptions_policy.go index e5065336..071d3a4c 100644 --- a/internal/providerfwk/resource_eventoptions_policy.go +++ b/internal/providerfwk/resource_eventoptions_policy.go @@ -1316,8 +1316,8 @@ func (block *eventoptionsPolicyBlockThen) configSet( path.Path, // pathErr error, // error ) { - setPrefix += "then " configSet := make([]string, 0) + setPrefix += "then " if block.Ignore.ValueBool() { configSet = append(configSet, setPrefix+"ignore") @@ -1389,8 +1389,8 @@ func (block *eventoptionsPolicyBlockThenBlockChangeConfigurtion) configSet( path.Path, // pathErr error, // error ) { - setPrefix += "change-configuration " configSet := make([]string, 0, len(block.Commands)) + setPrefix += "change-configuration " for _, v := range block.Commands { configSet = append(configSet, setPrefix+"commands \""+v.ValueString()+"\"") @@ -1443,6 +1443,7 @@ func (block *eventoptionsPolicyBlockThenBlockEventScript) configSet( error, // error ) { setPrefix += "event-script \"" + block.Filename.ValueString() + "\" " + configSet := []string{ setPrefix, } @@ -1486,8 +1487,8 @@ func (block *eventoptionsPolicyBlockThenBlockExecuteCommands) configSet( path.Path, // pathErr error, // error ) { - setPrefix += "execute-commands " configSet := make([]string, 0, len(block.Commands)) + setPrefix += "execute-commands " for _, v := range block.Commands { configSet = append(configSet, setPrefix+"commands \""+v.ValueString()+"\"") @@ -1521,6 +1522,7 @@ func (block *eventoptionsPolicyBlockThenBlockUpload) configSet( ) { setPrefix += "upload filename \"" + block.Filename.ValueString() + "\"" + " destination \"" + block.Destination.ValueString() + "\" " + configSet := []string{ setPrefix, } @@ -1558,6 +1560,7 @@ func (block *eventoptionsPolicyBlockThenBlockDestination) configSet( error, // error ) { setPrefix += "destination \"" + block.Name.ValueString() + "\" " + configSet := []string{ setPrefix, } @@ -1591,8 +1594,8 @@ func (block *eventoptionsPolicyBlockWithin) configSet( path.Path, // pathErr error, // error ) { - setPrefix += "within " + utils.ConvI64toa(block.TimeInterval.ValueInt64()) + " " configSet := make([]string, 0) + setPrefix += "within " + utils.ConvI64toa(block.TimeInterval.ValueInt64()) + " " for _, v := range block.Events { configSet = append(configSet, setPrefix+"events \""+v.ValueString()+"\"") @@ -1619,9 +1622,7 @@ func (block *eventoptionsPolicyBlockWithin) configSet( func (rscData *eventoptionsPolicyData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "event-options policy \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_evpn.go b/internal/providerfwk/resource_evpn.go index 387bdddb..cdd9ddc2 100644 --- a/internal/providerfwk/resource_evpn.go +++ b/internal/providerfwk/resource_evpn.go @@ -581,7 +581,11 @@ func (rsc *evpn) ImportState( } func (rscData *evpnData) fillID() { - rscData.ID = types.StringValue(rscData.RoutingInstance.ValueString()) + if v := rscData.RoutingInstance.ValueString(); v != "" { + rscData.ID = types.StringValue(v) + } else { + rscData.ID = types.StringValue(junos.DefaultW) + } } func (rscData *evpnData) nullID() bool { @@ -593,6 +597,7 @@ func (rscData *evpnData) set( ) ( path.Path, error, ) { + configSet := make([]string, 0) setPrefix := junos.SetLS setSwitchRIPrefix := junos.SetLS switch routingInstance := rscData.RoutingInstance.ValueString(); routingInstance { @@ -609,14 +614,12 @@ func (rscData *evpnData) set( return path.Root("default_gateway"), fmt.Errorf("default_gateway cannot be configured when routing_instance = %q", junos.DefaultW) } - setPrefix += "protocols evpn " setSwitchRIPrefix += "switch-options " default: - setPrefix += junos.RoutingInstancesWS + routingInstance + " protocols evpn " + setPrefix += junos.RoutingInstancesWS + routingInstance + " " setSwitchRIPrefix += junos.RoutingInstancesWS + routingInstance + " " } - - configSet := make([]string, 0) + setPrefix += "protocols evpn " if rscData.RoutingInstanceEvpn.ValueBool() { if rscData.SwitchOrRIOptions == nil { @@ -686,31 +689,30 @@ func (rscData *evpnData) set( func (rscData *evpnData) read( _ context.Context, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig - showSwitchRIPrefix := junos.CmdShowConfig - switch routingInstance { - case junos.DefaultW, "": - showPrefix += "protocols evpn" - showSwitchRIPrefix += "switch-options" - default: - showPrefix += junos.RoutingInstancesWS + routingInstance + " protocols evpn" - showSwitchRIPrefix += junos.RoutingInstancesWS + routingInstance - } - - showConfig, err := junSess.Command(showPrefix + junos.PipeDisplaySetRelative) + showSwitchRI := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + showSwitchRI += junos.RoutingInstancesWS + routingInstance + } else { + showSwitchRI += "switch-options" + } + showConfig, err := junSess.Command(showPrefix + + "protocols evpn" + junos.PipeDisplaySetRelative) if err != nil { return err } - showConfigSwitchRI, err := junSess.Command(showSwitchRIPrefix + junos.PipeDisplaySetRelative) + showConfigSwitchRI, err := junSess.Command(showSwitchRI + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { - rscData.RoutingInstance = types.StringValue(routingInstance) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } rscData.fillID() for _, item := range strings.Split(showConfig, "\n") { if strings.Contains(item, junos.XMLStartTagConfigOut) { @@ -801,12 +803,12 @@ func (rscData *evpnData) delOpts( delSwitchRIPrefix := junos.DeleteLS switch routingInstance := rscData.RoutingInstance.ValueString(); routingInstance { case junos.DefaultW, "": - delPrefix += "protocols evpn " delSwitchRIPrefix += "switch-options " default: - delPrefix += junos.RoutingInstancesWS + routingInstance + " protocols evpn " + delPrefix += junos.RoutingInstancesWS + routingInstance + " " delSwitchRIPrefix += junos.RoutingInstancesWS + routingInstance + " " } + delPrefix += "protocols evpn " configSet := []string{ delPrefix + "default-gateway", @@ -835,19 +837,18 @@ func (rscData *evpnData) delOpts( func (rscData *evpnData) del( _ context.Context, junSess *junos.Session, ) error { - delLine := junos.DeleteLS + delPrefix := junos.DeleteLS delSwitchRIPrefix := junos.DeleteLS switch routingInstance := rscData.RoutingInstance.ValueString(); routingInstance { case junos.DefaultW, "": - delLine += "protocols evpn" delSwitchRIPrefix += "switch-options " default: - delLine += junos.RoutingInstancesWS + routingInstance + " protocols evpn" + delPrefix += junos.RoutingInstancesWS + routingInstance + " " delSwitchRIPrefix += junos.RoutingInstancesWS + routingInstance + " " } configSet := []string{ - delLine, + delPrefix + "protocols evpn", } if rscData.RoutingInstanceEvpn.ValueBool() { diff --git a/internal/providerfwk/resource_firewall_filter.go b/internal/providerfwk/resource_firewall_filter.go index 52cbb59d..5a0cb512 100644 --- a/internal/providerfwk/resource_firewall_filter.go +++ b/internal/providerfwk/resource_firewall_filter.go @@ -1989,9 +1989,7 @@ func (block *firewallFilterBlockTermBlockThen) configSet(setPrefix string) []str func (rscData *firewallFilterData) read( _ context.Context, name, family string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "firewall family " + family + " filter \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_firewall_policer.go b/internal/providerfwk/resource_firewall_policer.go index 79d637b9..f0a137a0 100644 --- a/internal/providerfwk/resource_firewall_policer.go +++ b/internal/providerfwk/resource_firewall_policer.go @@ -547,7 +547,7 @@ func (rsc *firewallPolicer) ImportState( func checkFirewallPolicerExists( _ context.Context, name string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showConfig, err := junSess.Command(junos.CmdShowConfig + "firewall policer \"" + name + "\"" + junos.PipeDisplaySet) @@ -629,9 +629,7 @@ func (rscData *firewallPolicerData) set( func (rscData *firewallPolicerData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "firewall policer \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_forwardingoptions_evpn_vxlan.go b/internal/providerfwk/resource_forwardingoptions_evpn_vxlan.go index acad3e33..8ac3d290 100644 --- a/internal/providerfwk/resource_forwardingoptions_evpn_vxlan.go +++ b/internal/providerfwk/resource_forwardingoptions_evpn_vxlan.go @@ -311,7 +311,7 @@ func (rscData *forwardingoptionsEvpnVxlanData) set( configSet := make([]string, 0) setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + " " + setPrefix += junos.RoutingInstancesWS + v + " " } setPrefix += "forwarding-options evpn-vxlan " @@ -324,14 +324,11 @@ func (rscData *forwardingoptionsEvpnVxlanData) set( func (rscData *forwardingoptionsEvpnVxlanData) read( _ context.Context, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - showConfig, err := junSess.Command(showPrefix + "forwarding-options evpn-vxlan" + junos.PipeDisplaySetRelative) if err != nil { @@ -366,7 +363,7 @@ func (rscData *forwardingoptionsEvpnVxlanData) del( ) error { delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + " " + delPrefix += junos.RoutingInstancesWS + v + " " } delPrefix += "forwarding-options evpn-vxlan " diff --git a/internal/providerfwk/resource_forwardingoptions_sampling.go b/internal/providerfwk/resource_forwardingoptions_sampling.go index 3ef7b648..d7bba663 100644 --- a/internal/providerfwk/resource_forwardingoptions_sampling.go +++ b/internal/providerfwk/resource_forwardingoptions_sampling.go @@ -1377,11 +1377,11 @@ func (rscData *forwardingoptionsSamplingData) set( path.Path, error, ) { configSet := make([]string, 0) - setPrefix := "set forwarding-options sampling " - + setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + " forwarding-options sampling " + setPrefix += junos.RoutingInstancesWS + v + " " } + setPrefix += "forwarding-options sampling " if rscData.Disable.ValueBool() { configSet = append(configSet, setPrefix+"disable") @@ -1824,18 +1824,13 @@ func (block *forwardingoptionsSamplingBlockOutputBlockInterface) configSet(setPr func (rscData *forwardingoptionsSamplingData) read( _ context.Context, routingInstance string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string +) error { + showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "forwarding-options sampling" + junos.PipeDisplaySetRelative) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "forwarding-options sampling" + junos.PipeDisplaySetRelative) + showPrefix += junos.RoutingInstancesWS + routingInstance + " " } + showConfig, err := junSess.Command(showPrefix + + "forwarding-options sampling" + junos.PipeDisplaySetRelative) if err != nil { return err } @@ -2246,10 +2241,12 @@ func (block *forwardingoptionsSamplingBlockOutputBlockInterface) read(itemTrim s func (rscData *forwardingoptionsSamplingData) del( _ context.Context, junSess *junos.Session, ) error { - delPrefix := "delete forwarding-options sampling " + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + " forwarding-options sampling " + delPrefix += junos.RoutingInstancesWS + v + " " } + delPrefix += "forwarding-options sampling " + configSet := []string{ delPrefix + junos.DisableW, delPrefix + "pre-rewrite-tos", diff --git a/internal/providerfwk/resource_forwardingoptions_sampling_instance.go b/internal/providerfwk/resource_forwardingoptions_sampling_instance.go index 0dffa07a..26f6b597 100644 --- a/internal/providerfwk/resource_forwardingoptions_sampling_instance.go +++ b/internal/providerfwk/resource_forwardingoptions_sampling_instance.go @@ -1292,17 +1292,14 @@ func (rsc *forwardingoptionsSamplingInstance) ImportState( func checkForwardingoptionsSamplingInstanceExists( _ context.Context, name, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { - var showConfig string + showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySet) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySet) + showPrefix += junos.RoutingInstancesWS + routingInstance + " " } + showConfig, err := junSess.Command(showPrefix + + "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySet) if err != nil { return false, err } @@ -1331,12 +1328,11 @@ func (rscData *forwardingoptionsSamplingInstanceData) set( path.Path, error, ) { configSet := make([]string, 0) - setPrefix := "set forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\" " - + setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + - " forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\" " + setPrefix += junos.RoutingInstancesWS + v + " " } + setPrefix += "forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\" " if rscData.Disable.ValueBool() { configSet = append(configSet, setPrefix+"disable") @@ -1762,18 +1758,13 @@ func (block *forwardingoptionsSamplingInstanceBlockOutputBlockInterface) configS func (rscData *forwardingoptionsSamplingInstanceData) read( _ context.Context, name, routingInstance string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string +) error { + showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - junos.RoutingInstancesWS + routingInstance + " " + - "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySetRelative) - } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySetRelative) + showPrefix += junos.RoutingInstancesWS + routingInstance + " " } + showConfig, err := junSess.Command(showPrefix + + "forwarding-options sampling instance \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { return err } @@ -2153,13 +2144,13 @@ func (block *forwardingoptionsSamplingInstanceBlockOutputBlockInterface) read(it func (rscData *forwardingoptionsSamplingInstanceData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 1) + delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - configSet[0] = junos.DelRoutingInstances + v + - " forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\"" - } else { - configSet[0] = junos.DeleteW + - " forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\"" + delPrefix += junos.RoutingInstancesWS + v + " " + } + + configSet := []string{ + delPrefix + "forwarding-options sampling instance \"" + rscData.Name.ValueString() + "\"", } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_forwardingoptions_storm_control_profile.go b/internal/providerfwk/resource_forwardingoptions_storm_control_profile.go index 01b677ec..ff4258d0 100644 --- a/internal/providerfwk/resource_forwardingoptions_storm_control_profile.go +++ b/internal/providerfwk/resource_forwardingoptions_storm_control_profile.go @@ -394,7 +394,7 @@ func (rsc *forwardingoptionsStormControlProfile) ImportState( func checkForwardingoptionsStormControlProfileExists( _ context.Context, name string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showConfig, err := junSess.Command(junos.CmdShowConfig + "forwarding-options storm-control-profiles \"" + name + "\"" + junos.PipeDisplaySet) @@ -464,9 +464,7 @@ func (rscData *forwardingoptionsStormControlProfileData) set( func (rscData *forwardingoptionsStormControlProfileData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "forwarding-options storm-control-profiles \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_generate_route.go b/internal/providerfwk/resource_generate_route.go index 0eaa1e00..d6a936a5 100644 --- a/internal/providerfwk/resource_generate_route.go +++ b/internal/providerfwk/resource_generate_route.go @@ -511,7 +511,7 @@ func (rsc *generateRoute) ImportState( func checkGenerateRouteExists( _ context.Context, destination, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showPrefix := junos.CmdShowConfig switch routingInstance { @@ -526,11 +526,11 @@ func checkGenerateRouteExists( showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "generate route " + destination + junos.PipeDisplaySet) + showConfig, err := junSess.Command(showPrefix + + "generate route " + destination + junos.PipeDisplaySet) if err != nil { return false, err } - if showConfig == junos.EmptyW { return false, nil } @@ -569,6 +569,7 @@ func (rscData *generateRouteData) set( } } setPrefix += "generate route " + rscData.Destination.ValueString() + " " + configSet := []string{ setPrefix, } @@ -624,9 +625,7 @@ func (rscData *generateRouteData) set( func (rscData *generateRouteData) read( _ context.Context, destination, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig switch routingInstance { case junos.DefaultW, "": @@ -640,14 +639,18 @@ func (rscData *generateRouteData) read( showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "generate route " + destination + junos.PipeDisplaySetRelative) + showConfig, err := junSess.Command(showPrefix + + "generate route " + destination + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { rscData.Destination = types.StringValue(destination) - rscData.RoutingInstance = types.StringValue(routingInstance) + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) + } rscData.fillID() for _, item := range strings.Split(showConfig, "\n") { if strings.Contains(item, junos.XMLStartTagConfigOut) { @@ -720,6 +723,7 @@ func (rscData *generateRouteData) del( delPrefix += "rib " + routingInstance + ".inet6.0 " } } + configSet := []string{ delPrefix + "generate route " + rscData.Destination.ValueString(), } diff --git a/internal/providerfwk/resource_iccp.go b/internal/providerfwk/resource_iccp.go index b6f3c7bd..4f653541 100644 --- a/internal/providerfwk/resource_iccp.go +++ b/internal/providerfwk/resource_iccp.go @@ -223,6 +223,7 @@ func (rscData *iccpData) set( path.Path, error, ) { setPrefix := "set protocols iccp " + configSet := []string{ setPrefix + "local-ip-addr " + rscData.LocalIPAddr.ValueString(), } @@ -240,15 +241,12 @@ func (rscData *iccpData) set( func (rscData *iccpData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "protocols iccp" + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { rscData.fillID() for _, item := range strings.Split(showConfig, "\n") { diff --git a/internal/providerfwk/resource_iccp_peer.go b/internal/providerfwk/resource_iccp_peer.go index c00a9be4..ced93360 100644 --- a/internal/providerfwk/resource_iccp_peer.go +++ b/internal/providerfwk/resource_iccp_peer.go @@ -518,9 +518,7 @@ func (rscData *iccpPeerData) set( func (rscData *iccpPeerData) read( _ context.Context, ipAddress string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "protocols iccp peer " + ipAddress + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_interface_logical.go b/internal/providerfwk/resource_interface_logical.go index da9f5062..b95a46de 100644 --- a/internal/providerfwk/resource_interface_logical.go +++ b/internal/providerfwk/resource_interface_logical.go @@ -2291,6 +2291,7 @@ func (rscData *interfaceLogicalData) set( } setPrefix := "set interfaces " + rscData.Name.ValueString() + " " + configSet := []string{ setPrefix, } @@ -2405,7 +2406,7 @@ func (rscData *interfaceLogicalData) set( } } if v := rscData.RoutingInstance.ValueString(); v != "" { - configSet = append(configSet, junos.SetRoutingInstances+v+" interface "+rscData.Name.ValueString()) + configSet = append(configSet, junos.SetLS+junos.RoutingInstancesWS+v+" interface "+rscData.Name.ValueString()) } if securityZone := rscData.SecurityZone.ValueString(); securityZone != "" { configSet = append(configSet, "set security zones security-zone "+securityZone+ @@ -2466,6 +2467,7 @@ func (block *interfaceLogicalBlockFamilyInetBlockAddress) configSet( error, // error ) { setPrefix += "family inet address " + block.CidrIP.ValueString() + configSet := []string{ setPrefix, } @@ -2569,6 +2571,7 @@ func (block *interfaceLogicalBlockFamilyInet6BlockAddress) configSet( error, // error ) { setPrefix += "family inet6 address " + block.CidrIP.ValueString() + configSet := []string{ setPrefix, } @@ -2668,6 +2671,7 @@ func (block *interfaceLogicalBlockFamilyInetBlockDhcp) configSet(setPrefix strin } else { setPrefix += " " } + configSet := []string{ setPrefix, } @@ -2736,6 +2740,7 @@ func (block *interfaceLogicalBlockFamilyInetBlockDhcp) configSet(setPrefix strin func (block *interfaceLogicalBlockFamilyInet6BlockDhcpV6Client) configSet(setPrefix string) []string { setPrefix += "family inet6 dhcpv6-client " + configSet := []string{ setPrefix + "client-identifier duid-type " + block.ClientIdentifierDuidType.ValueString(), setPrefix + "client-type " + block.ClientType.ValueString(), @@ -2786,7 +2791,6 @@ func (rscData *interfaceLogicalData) read( if err != nil { return err } - rscData.Name = types.StringValue(name) rscData.fillID() if showConfig != junos.EmptyW { @@ -3288,7 +3292,6 @@ func (rscData *interfaceLogicalData) readSecurityZoneInboundTraffic( if err != nil { return err } - if showConfig != junos.EmptyW { for _, item := range strings.Split(showConfig, "\n") { if strings.Contains(item, junos.XMLStartTagConfigOut) { @@ -3345,6 +3348,7 @@ func (rscData *interfaceLogicalData) delOpts( _ context.Context, junSess *junos.Session, ) error { delPrefix := "delete interfaces " + rscData.Name.ValueString() + " " + configSet := []string{ delPrefix + "description", delPrefix + "disable", @@ -3372,7 +3376,7 @@ func (rscData *interfaceLogicalData) delRoutingInstance( _ context.Context, junSess *junos.Session, ) error { configSet := []string{ - junos.DelRoutingInstances + rscData.RoutingInstance.ValueString() + + junos.DeleteLS + junos.RoutingInstancesWS + rscData.RoutingInstance.ValueString() + " interface " + rscData.Name.ValueString(), } diff --git a/internal/providerfwk/resource_interface_physical.go b/internal/providerfwk/resource_interface_physical.go index 6736cdb5..8b8e6662 100644 --- a/internal/providerfwk/resource_interface_physical.go +++ b/internal/providerfwk/resource_interface_physical.go @@ -1770,6 +1770,7 @@ func (rscData *interfacePhysicalData) set( path.Path, error, ) { setPrefix := "set interfaces " + rscData.Name.ValueString() + " " + configSet := []string{ setPrefix, } @@ -1962,7 +1963,6 @@ func (block *interfacePhysicalBlockParentEtherOpts) configSet( error, // error ) { configSet := make([]string, 0) - switch { case strings.HasPrefix(interfaceName, "ae"): setPrefix += "aggregated-ether-options " @@ -2162,7 +2162,6 @@ func (rscData *interfacePhysicalData) read( if err != nil { return err } - rscData.Name = types.StringValue(name) rscData.fillID() if showConfig != junos.EmptyW { diff --git a/internal/providerfwk/resource_multichassis.go b/internal/providerfwk/resource_multichassis.go index 816d101d..8ccd64d0 100644 --- a/internal/providerfwk/resource_multichassis.go +++ b/internal/providerfwk/resource_multichassis.go @@ -264,6 +264,7 @@ func (rscData *multichassisData) set( path.Path, error, ) { setPrefix := "set multi-chassis " + configSet := []string{ setPrefix, } @@ -281,15 +282,12 @@ func (rscData *multichassisData) set( func (rscData *multichassisData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) 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") { diff --git a/internal/providerfwk/resource_multichassis_protection_peer.go b/internal/providerfwk/resource_multichassis_protection_peer.go index dbb2e8ef..72c22fce 100644 --- a/internal/providerfwk/resource_multichassis_protection_peer.go +++ b/internal/providerfwk/resource_multichassis_protection_peer.go @@ -287,6 +287,7 @@ func (rscData *multichassisProtectionPeerData) set( path.Path, error, ) { setPrefix := "set multi-chassis multi-chassis-protection " + rscData.IPAddress.ValueString() + " " + configSet := []string{ setPrefix, setPrefix + "interface " + rscData.Interface.ValueString(), @@ -302,9 +303,7 @@ func (rscData *multichassisProtectionPeerData) set( func (rscData *multichassisProtectionPeerData) read( _ context.Context, ipAddress string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "multi-chassis multi-chassis-protection " + ipAddress + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_oam_gretunnel_interface.go b/internal/providerfwk/resource_oam_gretunnel_interface.go index 97d1c64e..7e46566f 100644 --- a/internal/providerfwk/resource_oam_gretunnel_interface.go +++ b/internal/providerfwk/resource_oam_gretunnel_interface.go @@ -313,6 +313,7 @@ func (rscData *oamGretunnelInterfaceData) set( path.Path, error, ) { setPrefix := "set protocols oam gre-tunnel interface " + rscData.Name.ValueString() + " " + configSet := []string{ setPrefix, } @@ -331,9 +332,7 @@ func (rscData *oamGretunnelInterfaceData) set( func (rscData *oamGretunnelInterfaceData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "protocols oam gre-tunnel interface " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_ospf.go b/internal/providerfwk/resource_ospf.go index 8762ebaf..31f1482f 100644 --- a/internal/providerfwk/resource_ospf.go +++ b/internal/providerfwk/resource_ospf.go @@ -806,10 +806,9 @@ func (rscData *ospfData) set( ) { configSet := make([]string, 0) setPrefix := junos.SetLS - routingInstance := rscData.RoutingInstance.ValueString() if routingInstance != "" && routingInstance != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + routingInstance + " " + setPrefix += junos.RoutingInstancesWS + routingInstance + " " } ospfVersion := junos.OspfV2 if rscData.Version.ValueString() == "v3" { @@ -910,6 +909,7 @@ func (rscData *ospfData) set( func (block *ospfBlockDatabaseProtection) configSet(setPrefix string) []string { setPrefix += "database-protection " + configSet := []string{ setPrefix + "maximum-lsa " + utils.ConvI64toa(block.MaximumLsa.ValueInt64()), } @@ -978,6 +978,7 @@ func (block *ospfBlockGracefulRestart) configSet( func (block *ospfBlockOverload) configSet(setPrefix string) []string { setPrefix += "overload " + configSet := []string{ setPrefix, } @@ -1024,30 +1025,30 @@ func (block *ospfBlockSpfOptions) configSet(setPrefix string) []string { func (rscData *ospfData) read( _ context.Context, version, routingInstance string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string +) error { ospfVersion := junos.OspfV2 if version == "v3" { ospfVersion = junos.OspfV3 } - if routingInstance == junos.DefaultW { - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols " + ospfVersion + junos.PipeDisplaySetRelative) - if err != nil { - return err - } + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " + } + showConfig, err := junSess.Command(showPrefix + + "protocols " + ospfVersion + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if version == "v3" { + rscData.Version = types.StringValue(version) } else { - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols " + ospfVersion + junos.PipeDisplaySetRelative) - if err != nil { - return err - } + rscData.Version = types.StringValue("v2") + } + if routingInstance == "" { + rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) } - - rscData.Version = types.StringValue(version) - rscData.RoutingInstance = types.StringValue(routingInstance) rscData.fillID() if showConfig != junos.EmptyW { for _, item := range strings.Split(showConfig, "\n") { @@ -1259,7 +1260,7 @@ func (rscData *ospfData) del( } delPrefix := junos.DeleteLS if v := rscData.RoutingInstance.ValueString(); v != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + v + " " + delPrefix += junos.RoutingInstancesWS + v + " " } delPrefix += "protocols " + ospfVersion + " " @@ -1284,6 +1285,7 @@ func (rscData *ospfData) del( "sham-link", "spf-options", } + configSet := make([]string, len(listLinesToDelete)) for k, line := range listLinesToDelete { configSet[k] = delPrefix + line diff --git a/internal/providerfwk/resource_ospf_area.go b/internal/providerfwk/resource_ospf_area.go index c004a06f..fb47c516 100644 --- a/internal/providerfwk/resource_ospf_area.go +++ b/internal/providerfwk/resource_ospf_area.go @@ -1697,41 +1697,28 @@ func (rsc *ospfArea) ImportState( func checkOspfAreaExists( _ context.Context, areaID, version, realm, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { - var showConfig string - ospfVersion := junos.OspfV2 - if version == "v3" { - ospfVersion = junos.OspfV3 - } else if realm != "" { - return false, errors.New("realm can't set if version != v3") + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - switch { - case (routingInstance == junos.DefaultW || routingInstance == "") && realm == "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols " + ospfVersion + " area " + areaID + junos.PipeDisplaySet) - if err != nil { - return false, err - } - case (routingInstance == junos.DefaultW || routingInstance == "") && realm != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols " + ospfVersion + " realm " + realm + " area " + areaID + junos.PipeDisplaySet) - if err != nil { - return false, err - } - case realm != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols " + ospfVersion + " realm " + realm + " area " + areaID + junos.PipeDisplaySet) - if err != nil { - return false, err - } - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols " + ospfVersion + " area " + areaID + junos.PipeDisplaySet) - if err != nil { - return false, err + if version == "v3" { + showPrefix += "protocols " + junos.OspfV3 + " " + } else { + showPrefix += "protocols " + junos.OspfV2 + " " + if realm != "" { + return false, errors.New("realm can't set if version != v3") } } + if realm != "" { + showPrefix += "realm " + realm + " " + } + showConfig, err := junSess.Command(showPrefix + + "area " + areaID + junos.PipeDisplaySet) + if err != nil { + return false, err + } if showConfig == junos.EmptyW { return false, nil } @@ -1772,15 +1759,16 @@ func (rscData *ospfAreaData) set( configSet := make([]string, 0) setPrefix := junos.SetLS if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + v + " " + setPrefix += junos.RoutingInstancesWS + v + " " } - ospfVersion := junos.OspfV2 if rscData.Version.ValueString() == "v3" { - ospfVersion = junos.OspfV3 - } else if rscData.Realm.ValueString() != "" { - return path.Root("realm"), errors.New("realm can't set if version != v3") + setPrefix += "protocols " + junos.OspfV3 + " " + } else { + setPrefix += "protocols " + junos.OspfV2 + " " + if rscData.Realm.ValueString() != "" { + return path.Root("realm"), errors.New("realm can't set if version != v3") + } } - setPrefix += "protocols " + ospfVersion + " " if v := rscData.Realm.ValueString(); v != "" { setPrefix += "realm " + v + " " } @@ -1876,6 +1864,7 @@ func (block *ospfAreaBlockInterface) configSet( error, // error ) { setPrefix += "interface " + block.Name.ValueString() + " " + configSet := []string{ setPrefix, } @@ -2135,6 +2124,7 @@ func (block *ospfAreaBlockInterfaceBlockBfdLivenessDetection) configSet(setPrefi func (block *ospfAreaBlockAreaRange) configSet(setPrefix string) []string { setPrefix += "area-range " + block.Range.ValueString() + " " + configSet := []string{ setPrefix, } @@ -2161,6 +2151,7 @@ func (block *ospfAreaBlockNssa) configSet( error, // error ) { setPrefix += "nssa " + configSet := []string{ setPrefix, } @@ -2208,6 +2199,7 @@ func (block *ospfAreaBlockVirtualLink) configSet(setPrefix string) []string { " neighbor-id " + block.NeighborID.ValueString() + " transit-area " + block.TransitArea.ValueString() + " " + configSet := []string{ setPrefix, } @@ -2250,43 +2242,27 @@ func (block *ospfAreaBlockVirtualLink) configSet(setPrefix string) []string { func (rscData *ospfAreaData) read( _ context.Context, areaID, version, realm, routingInstance string, junSess *junos.Session, -) ( - err error, -) { - var showConfig string - ospfVersion := junos.OspfV2 - if version == "v3" { - ospfVersion = junos.OspfV3 - } else if realm != "" { - return errors.New("realm can't set if version != v3") +) error { + showPrefix := junos.CmdShowConfig + if routingInstance != "" && routingInstance != junos.DefaultW { + showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - switch { - case (routingInstance == junos.DefaultW || routingInstance == "") && realm == "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols " + ospfVersion + " area " + areaID + junos.PipeDisplaySetRelative) - if err != nil { - return err - } - case (routingInstance == junos.DefaultW || routingInstance == "") && realm != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + - "protocols " + ospfVersion + " realm " + realm + " area " + areaID + junos.PipeDisplaySetRelative) - if err != nil { - return err - } - case realm != "": - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols " + ospfVersion + " realm " + realm + " area " + areaID + junos.PipeDisplaySetRelative) - if err != nil { - return err - } - default: - showConfig, err = junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + routingInstance + " " + - "protocols " + ospfVersion + " area " + areaID + junos.PipeDisplaySetRelative) - if err != nil { - return err + if version == "v3" { + showPrefix += "protocols " + junos.OspfV3 + " " + } else { + showPrefix += "protocols " + junos.OspfV2 + " " + if realm != "" { + return errors.New("realm can't set if version != v3") } } - + if realm != "" { + showPrefix += "realm " + realm + " " + } + showConfig, err := junSess.Command(showPrefix + + "area " + areaID + junos.PipeDisplaySetRelative) + if err != nil { + return err + } if showConfig != junos.EmptyW { rscData.AreaID = types.StringValue(areaID) if version == "v3" { @@ -2707,31 +2683,24 @@ func (block *ospfAreaBlockNssa) read(itemTrim string) (err error) { func (rscData *ospfAreaData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 0, 1) - ospfVersion := junos.OspfV2 + delPrefix := junos.DeleteLS + if v := rscData.RoutingInstance.ValueString(); v != "" && v != junos.DefaultW { + delPrefix += junos.RoutingInstancesWS + v + " " + } if rscData.Version.ValueString() == "v3" { - ospfVersion = junos.OspfV3 - } else if rscData.Realm.ValueString() != "" { - return errors.New("realm can't set if version != v3") + delPrefix += "protocols " + junos.OspfV3 + " " + } else { + delPrefix += "protocols " + junos.OspfV2 + " " + if rscData.Realm.ValueString() != "" { + return errors.New("realm can't set if version != v3") + } } - routingInstance := junos.DefaultW - if v := rscData.RoutingInstance.ValueString(); v != "" { - routingInstance = v + if v := rscData.Realm.ValueString(); v != "" { + delPrefix += "realm " + v + " " } - realm := rscData.Realm.ValueString() - switch { - case (routingInstance == junos.DefaultW || routingInstance == "") && realm == "": - configSet = append(configSet, junos.DeleteW+ - " protocols "+ospfVersion+" area "+rscData.AreaID.ValueString()) - case (routingInstance == junos.DefaultW || routingInstance == "") && realm != "": - configSet = append(configSet, junos.DeleteW+ - " protocols "+ospfVersion+" realm "+realm+" area "+rscData.AreaID.ValueString()) - case realm != "": - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+ - " protocols "+ospfVersion+" realm "+realm+" area "+rscData.AreaID.ValueString()) - default: - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+ - " protocols "+ospfVersion+" area "+rscData.AreaID.ValueString()) + + configSet := []string{ + delPrefix + "area " + rscData.AreaID.ValueString(), } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_policyoptions_as_path.go b/internal/providerfwk/resource_policyoptions_as_path.go index 9a4b809f..8a52df50 100644 --- a/internal/providerfwk/resource_policyoptions_as_path.go +++ b/internal/providerfwk/resource_policyoptions_as_path.go @@ -318,9 +318,7 @@ func (rscData *policyoptionsASPathData) set( func (rscData *policyoptionsASPathData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options as-path \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_policyoptions_as_path_group.go b/internal/providerfwk/resource_policyoptions_as_path_group.go index f6b3c119..930647c5 100644 --- a/internal/providerfwk/resource_policyoptions_as_path_group.go +++ b/internal/providerfwk/resource_policyoptions_as_path_group.go @@ -385,9 +385,7 @@ func (rscData *policyoptionsASPathGroupData) set( func (rscData *policyoptionsASPathGroupData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options as-path-group \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_policyoptions_community.go b/internal/providerfwk/resource_policyoptions_community.go index 3e1c57be..1599b9bd 100644 --- a/internal/providerfwk/resource_policyoptions_community.go +++ b/internal/providerfwk/resource_policyoptions_community.go @@ -351,9 +351,7 @@ func (rscData *policyoptionsCommunityData) set( func (rscData *policyoptionsCommunityData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options community \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_policyoptions_policy_statement.go b/internal/providerfwk/resource_policyoptions_policy_statement.go index c8d8f825..e0301523 100644 --- a/internal/providerfwk/resource_policyoptions_policy_statement.go +++ b/internal/providerfwk/resource_policyoptions_policy_statement.go @@ -1804,7 +1804,7 @@ func (rsc *policyoptionsPolicyStatement) ImportState( func checkPolicyoptionsPolicyStatementExists( _ context.Context, name string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options policy-statement \"" + name + "\"" + junos.PipeDisplaySet) @@ -2236,9 +2236,7 @@ func (block *policyoptionsPolicyStatementBlockThen) configSet( func (rscData *policyoptionsPolicyStatementData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options policy-statement \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_policyoptions_prefix_list.go b/internal/providerfwk/resource_policyoptions_prefix_list.go index a564606c..fa004294 100644 --- a/internal/providerfwk/resource_policyoptions_prefix_list.go +++ b/internal/providerfwk/resource_policyoptions_prefix_list.go @@ -317,9 +317,7 @@ func (rscData *policyoptionsPrefixListData) set( func (rscData *policyoptionsPrefixListData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "policy-options prefix-list \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_routing_instance.go b/internal/providerfwk/resource_routing_instance.go index c0da3dbc..a187d131 100644 --- a/internal/providerfwk/resource_routing_instance.go +++ b/internal/providerfwk/resource_routing_instance.go @@ -536,7 +536,7 @@ func (rscData *routingInstanceData) set( path.Path, error, ) { configSet := make([]string, 0) - setPrefix := junos.SetRoutingInstances + rscData.Name.ValueString() + " " + setPrefix := junos.SetLS + junos.RoutingInstancesWS + rscData.Name.ValueString() + " " if rscData.ConfigureTypeSingly.ValueBool() { if rscData.Type.ValueString() != "" { @@ -595,9 +595,7 @@ func (rscData *routingInstanceData) set( func (rscData *routingInstanceData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + junos.RoutingInstancesWS + name + junos.PipeDisplaySetRelative) if err != nil { @@ -661,7 +659,7 @@ func (rscData *routingInstanceData) delOpts( _ context.Context, junSess *junos.Session, ) error { configSet := make([]string, 0) - setPrefix := junos.DelRoutingInstances + rscData.Name.ValueString() + " " + setPrefix := junos.DeleteLS + junos.RoutingInstancesWS + rscData.Name.ValueString() + " " configSet = append(configSet, setPrefix+"description", setPrefix+junos.RoutingOptionsWS+"autonomous-system", @@ -689,7 +687,7 @@ func (rscData *routingInstanceData) del( _ context.Context, junSess *junos.Session, ) error { configSet := []string{ - junos.DelRoutingInstances + rscData.Name.ValueString(), + junos.DeleteLS + junos.RoutingInstancesWS + rscData.Name.ValueString(), } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_security.go b/internal/providerfwk/resource_security.go index 0b08a0c3..8b4196c0 100644 --- a/internal/providerfwk/resource_security.go +++ b/internal/providerfwk/resource_security.go @@ -2094,8 +2094,8 @@ func (rscData *securityData) set( ) ( path.Path, error, ) { - setPrefix := "set security " configSet := make([]string, 0) + setPrefix := "set security " if rscData.Alg != nil { if rscData.Alg.isEmpty() { @@ -2217,8 +2217,8 @@ func (rscData *securityData) set( } func (block *securityBlockAlg) configSet() []string { - setPrefix := "set security alg " configSet := make([]string, 0) + setPrefix := "set security alg " if block.DNSDisable.ValueBool() { configSet = append(configSet, setPrefix+"dns disable") @@ -2271,8 +2271,8 @@ func (block *securityBlockFlow) configSet() ( path.Path, // pathErr error, // error ) { - setPrefix := "set security flow " configSet := make([]string, 0) + setPrefix := "set security flow " if block.AdvancedOptions != nil { if block.AdvancedOptions.isEmpty() { @@ -2449,8 +2449,8 @@ func (block *securityBlockFlow) configSet() ( } func (block *securityBlockForwardingOptions) configSet() []string { - setPrefix := "set security forwarding-options " configSet := make([]string, 0) + setPrefix := "set security forwarding-options " if v := block.Inet6Mode.ValueString(); v != "" { configSet = append(configSet, setPrefix+"family inet6 mode "+v) @@ -2466,8 +2466,8 @@ func (block *securityBlockForwardingOptions) configSet() []string { } func (block *securityBlockIdpSecurityPackage) configSet() []string { - setPrefix := "set security idp security-package " configSet := make([]string, 0) + setPrefix := "set security idp security-package " if block.AutomaticEnable.ValueBool() { configSet = append(configSet, setPrefix+"automatic enable") @@ -2496,8 +2496,8 @@ func (block *securityBlockIdpSecurityPackage) configSet() []string { } func (block *securityBlockIdpSensorConfiguration) configSet() []string { - setPrefix := "set security idp sensor-configuration " configSet := make([]string, 0) + setPrefix := "set security idp sensor-configuration " if !block.LogCacheSize.IsNull() { configSet = append(configSet, setPrefix+"log cache-size "+ @@ -2563,8 +2563,8 @@ func (block *securityBlockIkeTraceoptions) configSet() ( path.Path, // pathErr error, // error ) { - setPrefix := "set security ike traceoptions " configSet := make([]string, 0) + setPrefix := "set security ike traceoptions " if block.File != nil { if block.File.isEmpty() { @@ -2618,8 +2618,8 @@ func (block *securityBlockLog) configSet() ( path.Path, // pathErr error, // error ) { - setPrefix := "set security log " configSet := make([]string, 0) + setPrefix := "set security log " if block.Disable.ValueBool() { configSet = append(configSet, setPrefix+"disable") @@ -2697,8 +2697,8 @@ func (block *securityBlockLog) configSet() ( } func (block *securityBlockNatSource) configSet() []string { - setPrefix := "set security nat source " configSet := make([]string, 0) + setPrefix := "set security nat source " if block.AddressPersistent.ValueBool() { configSet = append(configSet, setPrefix+"address-persistent") @@ -2749,8 +2749,8 @@ func (block *securityBlockNatSource) configSet() []string { } func (block *securityBlockUserIdentificationAuthSource) configSet() []string { - setPrefix := "set security user-identification authentication-source " configSet := make([]string, 0) + setPrefix := "set security user-identification authentication-source " if !block.ADAuthPriority.IsNull() { configSet = append(configSet, setPrefix+"active-directory-authentication-table priority "+ @@ -2777,8 +2777,8 @@ func (block *securityBlockUserIdentificationAuthSource) configSet() []string { } func (block *securityBlockUtm) configSet() []string { - setPrefix := "set security utm " configSet := make([]string, 0) + setPrefix := "set security utm " if v := block.FeatureProfileWebFilteringType.ValueString(); v != "" { configSet = append(configSet, setPrefix+"feature-profile web-filtering type "+v) @@ -2807,9 +2807,7 @@ func (block *securityBlockUtm) configSet() []string { func (rscData *securityData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_address_book.go b/internal/providerfwk/resource_security_address_book.go index c6c93d16..da88bce3 100644 --- a/internal/providerfwk/resource_security_address_book.go +++ b/internal/providerfwk/resource_security_address_book.go @@ -832,9 +832,7 @@ func (rscData *securityAddressBookData) set( func (rscData *securityAddressBookData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security address-book \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_global_policy.go b/internal/providerfwk/resource_security_global_policy.go index dc399b59..c68100eb 100644 --- a/internal/providerfwk/resource_security_global_policy.go +++ b/internal/providerfwk/resource_security_global_policy.go @@ -691,9 +691,7 @@ func (rscData *securityGlobalPolicyData) set( func (rscData *securityGlobalPolicyData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security policies global" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ike_gateway.go b/internal/providerfwk/resource_security_ike_gateway.go index da9a42ab..d9bd2279 100644 --- a/internal/providerfwk/resource_security_ike_gateway.go +++ b/internal/providerfwk/resource_security_ike_gateway.go @@ -565,7 +565,7 @@ func (rsc *securityIkeGateway) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("aaa").AtName("access_profile"), tfdiag.ConflictConfigErrSummary, - "only one of access_profile or client_username/client_password must be specifiedin aaa block ", + "only one of access_profile or client_username/client_password must be specifiedin aaa block", ) } if config.Aaa.ClientUsername.IsNull() && !config.Aaa.ClientPassword.IsNull() { @@ -954,9 +954,7 @@ func (rscData *securityIkeGatewayData) set( func (rscData *securityIkeGatewayData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ike gateway \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ike_policy.go b/internal/providerfwk/resource_security_ike_policy.go index f70a23aa..345f5f43 100644 --- a/internal/providerfwk/resource_security_ike_policy.go +++ b/internal/providerfwk/resource_security_ike_policy.go @@ -431,9 +431,7 @@ func (rscData *securityIkePolicyData) set( func (rscData *securityIkePolicyData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ike policy \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ike_proposal.go b/internal/providerfwk/resource_security_ike_proposal.go index c65b650d..a035dd68 100644 --- a/internal/providerfwk/resource_security_ike_proposal.go +++ b/internal/providerfwk/resource_security_ike_proposal.go @@ -361,9 +361,7 @@ func (rscData *securityIkeProposalData) set( func (rscData *securityIkeProposalData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ike proposal \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ipsec_policy.go b/internal/providerfwk/resource_security_ipsec_policy.go index d7612673..c96a9e4d 100644 --- a/internal/providerfwk/resource_security_ipsec_policy.go +++ b/internal/providerfwk/resource_security_ipsec_policy.go @@ -373,9 +373,7 @@ func (rscData *securityIpsecPolicyData) set( func (rscData *securityIpsecPolicyData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ipsec policy " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ipsec_proposal.go b/internal/providerfwk/resource_security_ipsec_proposal.go index 0175ea7b..6cfd629a 100644 --- a/internal/providerfwk/resource_security_ipsec_proposal.go +++ b/internal/providerfwk/resource_security_ipsec_proposal.go @@ -358,9 +358,7 @@ func (rscData *securityIpsecProposalData) set( func (rscData *securityIpsecProposalData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ipsec proposal " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_ipsec_vpn.go b/internal/providerfwk/resource_security_ipsec_vpn.go index 8cff07d2..37eb09e4 100644 --- a/internal/providerfwk/resource_security_ipsec_vpn.go +++ b/internal/providerfwk/resource_security_ipsec_vpn.go @@ -1010,9 +1010,7 @@ func (rscData *securityIpsecVpnData) set( func (rscData *securityIpsecVpnData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security ipsec vpn \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_nat_destination_pool.go b/internal/providerfwk/resource_security_nat_destination_pool.go index 88cb76ac..024a39d9 100644 --- a/internal/providerfwk/resource_security_nat_destination_pool.go +++ b/internal/providerfwk/resource_security_nat_destination_pool.go @@ -341,6 +341,7 @@ func (rscData *securityNatDestinationPoolData) set( path.Path, error, ) { setPrefix := "set security nat destination pool " + rscData.Name.ValueString() + " " + configSet := []string{ setPrefix + "address " + rscData.Address.ValueString(), } diff --git a/internal/providerfwk/resource_security_nat_static.go b/internal/providerfwk/resource_security_nat_static.go index 1b782ce1..b43d4149 100644 --- a/internal/providerfwk/resource_security_nat_static.go +++ b/internal/providerfwk/resource_security_nat_static.go @@ -898,9 +898,7 @@ func (rscData *securityNatStaticData) set( func (rscData *securityNatStaticData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security nat static rule-set " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_nat_static_rule.go b/internal/providerfwk/resource_security_nat_static_rule.go index 045fa464..a251eed7 100644 --- a/internal/providerfwk/resource_security_nat_static_rule.go +++ b/internal/providerfwk/resource_security_nat_static_rule.go @@ -589,6 +589,7 @@ func (rscData *securityNatStaticRuleData) set( setPrefix := "set security nat static " + "rule-set " + rscData.RuleSet.ValueString() + " rule " + rscData.Name.ValueString() + " " + configSet := []string{ setPrefix, } diff --git a/internal/providerfwk/resource_security_policy.go b/internal/providerfwk/resource_security_policy.go index 4c1debb4..601e0c8f 100644 --- a/internal/providerfwk/resource_security_policy.go +++ b/internal/providerfwk/resource_security_policy.go @@ -945,9 +945,7 @@ func (block *securityPolicyBlockPolicyBlockPermitApplicationServices) configSet( func (rscData *securityPolicyData) read( _ context.Context, fromZone, toZone string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security policies from-zone " + fromZone + " to-zone " + toZone + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_policy_tunnel_pair_policy.go b/internal/providerfwk/resource_security_policy_tunnel_pair_policy.go index 3831bd32..221b6353 100644 --- a/internal/providerfwk/resource_security_policy_tunnel_pair_policy.go +++ b/internal/providerfwk/resource_security_policy_tunnel_pair_policy.go @@ -397,9 +397,7 @@ func (rscData *securityPolicyTunnelPairPolicyData) set( func (rscData *securityPolicyTunnelPairPolicyData) read( _ context.Context, zoneA, policyAtoB, zoneB, policyBtoA string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security policies from-zone " + zoneA + " to-zone " + zoneB + " policy " + policyAtoB + " then permit tunnel pair-policy" + junos.PipeDisplaySet) diff --git a/internal/providerfwk/resource_security_zone.go b/internal/providerfwk/resource_security_zone.go index f8d45f8b..cf08bc73 100644 --- a/internal/providerfwk/resource_security_zone.go +++ b/internal/providerfwk/resource_security_zone.go @@ -995,9 +995,7 @@ func (rscData *securityZoneData) set( func (rscData *securityZoneData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security zones security-zone " + name + junos.PipeDisplaySetRelative) if err != nil { @@ -1155,6 +1153,7 @@ func (rscData *securityZoneData) delOpts( listLinesToDelete = append(listLinesToDelete, "address-book") } delPrefix := "delete security zones security-zone " + rscData.Name.ValueString() + " " + configSet := make([]string, len(listLinesToDelete)) for k, line := range listLinesToDelete { configSet[k] = delPrefix + line diff --git a/internal/providerfwk/resource_security_zone_book_address.go b/internal/providerfwk/resource_security_zone_book_address.go index 0428e437..28c424fe 100644 --- a/internal/providerfwk/resource_security_zone_book_address.go +++ b/internal/providerfwk/resource_security_zone_book_address.go @@ -518,9 +518,7 @@ func (rscData *securityZoneBookAddressData) set( func (rscData *securityZoneBookAddressData) read( _ context.Context, zone, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security zones security-zone " + zone + " address-book address " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_security_zone_book_address_set.go b/internal/providerfwk/resource_security_zone_book_address_set.go index df934d47..255df734 100644 --- a/internal/providerfwk/resource_security_zone_book_address_set.go +++ b/internal/providerfwk/resource_security_zone_book_address_set.go @@ -411,9 +411,7 @@ func (rscData *securityZoneBookAddressSetData) set( func (rscData *securityZoneBookAddressSetData) read( _ context.Context, zone, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "security zones security-zone " + zone + " address-book address-set " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_services_flowmonitoring_v9_template.go b/internal/providerfwk/resource_services_flowmonitoring_v9_template.go index 42fe06bf..59492429 100644 --- a/internal/providerfwk/resource_services_flowmonitoring_v9_template.go +++ b/internal/providerfwk/resource_services_flowmonitoring_v9_template.go @@ -624,9 +624,7 @@ func (rscData *servicesFlowMonitoringV9TemplateData) set( func (rscData *servicesFlowMonitoringV9TemplateData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "services flow-monitoring version9 template \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_services_flowmonitoring_vipfix_template.go b/internal/providerfwk/resource_services_flowmonitoring_vipfix_template.go index e22c8101..774f3405 100644 --- a/internal/providerfwk/resource_services_flowmonitoring_vipfix_template.go +++ b/internal/providerfwk/resource_services_flowmonitoring_vipfix_template.go @@ -620,9 +620,7 @@ func (rscData *servicesFlowMonitoringVIPFixTemplateData) set( func (rscData *servicesFlowMonitoringVIPFixTemplateData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "services flow-monitoring version-ipfix template \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_snmp.go b/internal/providerfwk/resource_snmp.go index 44613a76..76b626e4 100644 --- a/internal/providerfwk/resource_snmp.go +++ b/internal/providerfwk/resource_snmp.go @@ -482,8 +482,8 @@ func (rscData *snmpData) set( ) ( path.Path, error, ) { - setPrefix := "set snmp " configSet := make([]string, 0) + setPrefix := "set snmp " if rscData.ARP.ValueBool() { configSet = append(configSet, setPrefix+"arp") @@ -561,9 +561,7 @@ func (rscData *snmpData) set( func (rscData *snmpData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp" + junos.PipeDisplaySetRelative) if err != nil { @@ -662,6 +660,7 @@ func (rscData *snmpData) del( _ context.Context, junSess *junos.Session, ) error { delPrefix := "delete snmp " + configSet := []string{ delPrefix + "arp", delPrefix + "contact", diff --git a/internal/providerfwk/resource_snmp_clientlist.go b/internal/providerfwk/resource_snmp_clientlist.go index 24d182dd..d4186f15 100644 --- a/internal/providerfwk/resource_snmp_clientlist.go +++ b/internal/providerfwk/resource_snmp_clientlist.go @@ -288,6 +288,7 @@ func (rscData *snmpClientlistData) set( path.Path, error, ) { setPrefix := "set snmp client-list \"" + rscData.Name.ValueString() + "\" " + configSet := []string{ setPrefix, } @@ -301,9 +302,7 @@ func (rscData *snmpClientlistData) set( func (rscData *snmpClientlistData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp client-list \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_snmp_community.go b/internal/providerfwk/resource_snmp_community.go index cfeda98e..a18463a7 100644 --- a/internal/providerfwk/resource_snmp_community.go +++ b/internal/providerfwk/resource_snmp_community.go @@ -448,8 +448,8 @@ func (rscData *snmpCommunityData) set( ) ( path.Path, error, ) { - setPrefix := "set snmp community \"" + rscData.Name.ValueString() + "\" " configSet := make([]string, 0) + setPrefix := "set snmp community \"" + rscData.Name.ValueString() + "\" " if rscData.AuthorizationReadOnly.ValueBool() { configSet = append(configSet, setPrefix+"authorization read-only") @@ -498,9 +498,7 @@ func (rscData *snmpCommunityData) set( func (rscData *snmpCommunityData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp community \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_snmp_v3_community.go b/internal/providerfwk/resource_snmp_v3_community.go index 322f46c2..b96c650f 100644 --- a/internal/providerfwk/resource_snmp_v3_community.go +++ b/internal/providerfwk/resource_snmp_v3_community.go @@ -313,6 +313,7 @@ func (rscData *snmpV3CommunityData) set( path.Path, error, ) { setPrefix := "set snmp v3 snmp-community \"" + rscData.CommunityIndex.ValueString() + "\" " + configSet := []string{ setPrefix + "security-name \"" + rscData.SecurityName.ValueString() + "\"", } @@ -332,9 +333,7 @@ func (rscData *snmpV3CommunityData) set( func (rscData *snmpV3CommunityData) read( _ context.Context, communityIndex string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp v3 snmp-community \"" + communityIndex + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_snmp_v3_usm_user.go b/internal/providerfwk/resource_snmp_v3_usm_user.go index 887d420f..96d1c70e 100644 --- a/internal/providerfwk/resource_snmp_v3_usm_user.go +++ b/internal/providerfwk/resource_snmp_v3_usm_user.go @@ -261,7 +261,7 @@ func (rsc *snmpV3UsmUser) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("authentication_type"), tfdiag.MissingConfigErrSummary, - "authentication_key or authentication_password must be specified when authentication_type != authentication-none ", + "authentication_key or authentication_password must be specified when authentication_type != authentication-none", ) } } else if config.AuthenticationType.IsNull() || config.AuthenticationType.ValueString() == "authentication-none" { @@ -294,7 +294,7 @@ func (rsc *snmpV3UsmUser) ValidateConfig( resp.Diagnostics.AddAttributeError( path.Root("privacy_type"), tfdiag.MissingConfigErrSummary, - "privacy_key or privacy_password must be specified when privacy_type != privacy-none ", + "privacy_key or privacy_password must be specified when privacy_type != privacy-none", ) } } else if config.PrivacyType.IsNull() || config.PrivacyType.ValueString() == "privacy-none" { @@ -586,41 +586,36 @@ func checkSnmpV3UsmUserExists( ) ( bool, error, ) { + showPrefix := junos.CmdShowConfig + "snmp v3 usm " switch engineType { case "local": - showConfig, err := junSess.Command(junos.CmdShowConfig + - "snmp v3 usm local-engine user \"" + name + "\"" + junos.PipeDisplaySet) - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } + showPrefix += "local-engine " case "remote": - showConfig, err := junSess.Command(junos.CmdShowConfig + - "snmp v3 usm remote-engine \"" + engineID + "\" user \"" + name + "\"" + junos.PipeDisplaySet) - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } + showPrefix += "remote-engine \"" + engineID + "\" " default: return false, fmt.Errorf("can't check config with engine_type %q", engineType) } + showConfig, err := junSess.Command(showPrefix + + "user \"" + name + "\"" + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } return true, nil } func (rscData *snmpV3UsmUserData) fillID() { - switch rscData.EngineType.ValueString() { - case "remote": + switch v := rscData.EngineType.ValueString(); v { + case "local": rscData.ID = types.StringValue( - "remote" + junos.IDSeparator + rscData.EngineID.ValueString() + junos.IDSeparator + rscData.Name.ValueString(), + v + junos.IDSeparator + rscData.Name.ValueString(), ) - case "local": + case "remote": rscData.ID = types.StringValue( - "local" + junos.IDSeparator + rscData.Name.ValueString(), + v + junos.IDSeparator + rscData.EngineID.ValueString() + junos.IDSeparator + rscData.Name.ValueString(), ) } } @@ -634,16 +629,17 @@ func (rscData *snmpV3UsmUserData) set( ) ( path.Path, error, ) { - setPrefix := "set snmp v3 usm " + - "local-engine user \"" + rscData.Name.ValueString() + "\" " - if v := rscData.EngineType.ValueString(); v == "remote" { - setPrefix = "set snmp v3 usm " + - "remote-engine \"" + rscData.EngineID.ValueString() + "\" " + - "user \"" + rscData.Name.ValueString() + "\" " - } else if v != "local" { + configSet := make([]string, 0) + setPrefix := "set snmp v3 usm " + switch v := rscData.EngineType.ValueString(); v { + case "local": + setPrefix += "local-engine " + case "remote": + setPrefix += "remote-engine \"" + rscData.EngineID.ValueString() + "\" " + default: return path.Root("engine_type"), fmt.Errorf("can't set config with engine_type %q", v) } - configSet := make([]string, 0) + setPrefix += "user \"" + rscData.Name.ValueString() + "\" " if authenticationType := rscData.AuthenticationType.ValueString(); authenticationType != "authentication-none" { if rscData.AuthenticationKey.ValueString() == "" && rscData.AuthenticationPassword.ValueString() == "" { @@ -700,18 +696,19 @@ func (rscData *snmpV3UsmUserData) set( func (rscData *snmpV3UsmUserData) read( _ context.Context, name, engineType, engineID string, junSess *junos.Session, -) ( - err error, -) { - showCommand := junos.CmdShowConfig + - "snmp v3 usm local-engine user \"" + name + "\"" + junos.PipeDisplaySetRelative - if engineType == "remote" { - showCommand = junos.CmdShowConfig + - "snmp v3 usm remote-engine \"" + engineID + "\" user \"" + name + "\"" + junos.PipeDisplaySetRelative - } else { - engineType = "local" +) error { + showPrefix := junos.CmdShowConfig + "snmp v3 usm " + switch engineType { + case "remote": + showPrefix += "remote-engine \"" + engineID + "\" " + default: + if engineType != "local" { + engineType = "local" + } + showPrefix += "local-engine " } - showConfig, err := junSess.Command(showCommand) + showConfig, err := junSess.Command(showPrefix + + "user \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { return err } @@ -759,14 +756,18 @@ func (rscData *snmpV3UsmUserData) read( func (rscData *snmpV3UsmUserData) del( _ context.Context, junSess *junos.Session, ) error { - configSet := []string{ - "delete snmp v3 usm " + - "local-engine user \"" + rscData.Name.ValueString() + "\"", + delPrefix := junos.DeleteLS + "snmp v3 usm " + switch v := rscData.EngineType.ValueString(); v { + case "local": + delPrefix += "local-engine " + case "remote": + delPrefix += "remote-engine \"" + rscData.EngineID.ValueString() + "\" " + default: + return fmt.Errorf("can't del config with engine_type %q", v) } - if rscData.EngineType.ValueString() == "remote" { - configSet[0] = "delete snmp v3 usm " + - "remote-engine \"" + rscData.EngineID.ValueString() + "\" " + - "user \"" + rscData.Name.ValueString() + "\"" + + configSet := []string{ + delPrefix + "user \"" + rscData.Name.ValueString() + "\"", } return junSess.ConfigSet(configSet) diff --git a/internal/providerfwk/resource_snmp_v3_vacm_accessgroup.go b/internal/providerfwk/resource_snmp_v3_vacm_accessgroup.go index 7d1b87ee..10d66b89 100644 --- a/internal/providerfwk/resource_snmp_v3_vacm_accessgroup.go +++ b/internal/providerfwk/resource_snmp_v3_vacm_accessgroup.go @@ -564,8 +564,8 @@ func (rscData *snmpV3VacmAccessgroupData) set( ) ( path.Path, error, ) { - setPrefix := "set snmp v3 vacm access group \"" + rscData.Name.ValueString() + "\" " configSet := make([]string, 0) + setPrefix := "set snmp v3 vacm access group \"" + rscData.Name.ValueString() + "\" " defaultContextPrefixModelLevel := make(map[string]struct{}) for _, block := range rscData.DefaultContextPrefix { @@ -642,9 +642,7 @@ func (rscData *snmpV3VacmAccessgroupData) set( func (rscData *snmpV3VacmAccessgroupData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp v3 vacm access group \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_snmp_v3_vacm_securitytogroup.go b/internal/providerfwk/resource_snmp_v3_vacm_securitytogroup.go index ecd86c2d..37e996f0 100644 --- a/internal/providerfwk/resource_snmp_v3_vacm_securitytogroup.go +++ b/internal/providerfwk/resource_snmp_v3_vacm_securitytogroup.go @@ -333,9 +333,7 @@ func (rscData *snmpV3VacmSecuritytogroupData) set( func (rscData *snmpV3VacmSecuritytogroupData) read( _ context.Context, model, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp v3 vacm security-to-group security-model " + model + " security-name \"" + name + "\"" + junos.PipeDisplaySetRelative) diff --git a/internal/providerfwk/resource_snmp_view.go b/internal/providerfwk/resource_snmp_view.go index 17b8d44c..c67ee36e 100644 --- a/internal/providerfwk/resource_snmp_view.go +++ b/internal/providerfwk/resource_snmp_view.go @@ -329,8 +329,8 @@ func (rscData *snmpViewData) set( ) ( path.Path, error, ) { - setPrefix := "set snmp view \"" + rscData.Name.ValueString() + "\" " configSet := make([]string, 0) + setPrefix := "set snmp view \"" + rscData.Name.ValueString() + "\" " for _, v := range rscData.OIDInclude { configSet = append(configSet, setPrefix+"oid \""+v.ValueString()+"\" include") @@ -344,9 +344,7 @@ func (rscData *snmpViewData) set( func (rscData *snmpViewData) read( _ context.Context, name string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "snmp view \"" + name + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_static_route.go b/internal/providerfwk/resource_static_route.go index 54af5d87..f2c4d951 100644 --- a/internal/providerfwk/resource_static_route.go +++ b/internal/providerfwk/resource_static_route.go @@ -798,9 +798,10 @@ func (rsc *staticRoute) ImportState( ) } -func checkStaticRouteExists(_ context.Context, destination, routingInstance string, junSess *junos.Session, +func checkStaticRouteExists( + _ context.Context, destination, routingInstance string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showPrefix := junos.CmdShowConfig switch routingInstance { @@ -815,11 +816,11 @@ func checkStaticRouteExists(_ context.Context, destination, routingInstance stri showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "static route " + destination + junos.PipeDisplaySet) + showConfig, err := junSess.Command(showPrefix + + "static route " + destination + junos.PipeDisplaySet) if err != nil { return false, err } - if showConfig == junos.EmptyW { return false, nil } @@ -959,9 +960,7 @@ func (rscData *staticRouteData) set( func (rscData *staticRouteData) read( _ context.Context, destination, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig switch routingInstance { case junos.DefaultW, "": @@ -975,16 +974,17 @@ func (rscData *staticRouteData) read( showPrefix += "rib " + routingInstance + ".inet6.0 " } } - showConfig, err := junSess.Command(showPrefix + "static route " + destination + junos.PipeDisplaySetRelative) + showConfig, err := junSess.Command(showPrefix + + "static route " + destination + junos.PipeDisplaySetRelative) if err != nil { return err } - if showConfig != junos.EmptyW { rscData.Destination = types.StringValue(destination) - rscData.RoutingInstance = types.StringValue(routingInstance) - if rscData.RoutingInstance.ValueString() == "" { + if routingInstance == "" { rscData.RoutingInstance = types.StringValue(junos.DefaultW) + } else { + rscData.RoutingInstance = types.StringValue(routingInstance) } rscData.fillID() for _, item := range strings.Split(showConfig, "\n") { @@ -1097,6 +1097,7 @@ func (rscData *staticRouteData) del( delPrefix += "rib " + routingInstance + ".inet6.0 " } } + configSet := []string{ delPrefix + "static route " + rscData.Destination.ValueString(), } diff --git a/internal/providerfwk/resource_switch_options.go b/internal/providerfwk/resource_switch_options.go index 8d7c3ea3..99e75a65 100644 --- a/internal/providerfwk/resource_switch_options.go +++ b/internal/providerfwk/resource_switch_options.go @@ -238,9 +238,7 @@ func (rscData *switchOptionsData) set( func (rscData *switchOptionsData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "switch-options" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system.go b/internal/providerfwk/resource_system.go index 3c7084d7..8b9988b3 100644 --- a/internal/providerfwk/resource_system.go +++ b/internal/providerfwk/resource_system.go @@ -3223,6 +3223,7 @@ func (block *systemBlockAccounting) configSet() ( func (block *systemBlockAccountingBlockDestinationRadiusServer) configSet() []string { setPrefix := "set system accounting destination radius server " + block.Address.ValueString() + " " + configSet := []string{ setPrefix + "secret \"" + block.Secret.ValueString() + "\"", } @@ -3278,6 +3279,7 @@ func (block *systemBlockAccountingBlockDestinationRadiusServer) configSet() []st func (block *systemBlockAccountingBlockDestinationTacplusServer) configSet() []string { setPrefix := "set system accounting destination tacplus server " + block.Address.ValueString() + " " + configSet := []string{ setPrefix, } @@ -3966,9 +3968,7 @@ func (block *systemBlockSyslog) configSet() ( func (rscData *systemData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system_radius_server.go b/internal/providerfwk/resource_system_radius_server.go index cc6b6a6a..7b0bb59a 100644 --- a/internal/providerfwk/resource_system_radius_server.go +++ b/internal/providerfwk/resource_system_radius_server.go @@ -351,7 +351,7 @@ func (rsc *systemRadiusServer) ImportState( func checkSystemRadiusServerExists( _ context.Context, address string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showConfig, err := junSess.Command(junos.CmdShowConfig + "system radius-server " + address + junos.PipeDisplaySet) @@ -379,6 +379,7 @@ func (rscData *systemRadiusServerData) set( path.Path, error, ) { setPrefix := "set system radius-server " + rscData.Address.ValueString() + " " + configSet := []string{ setPrefix + "secret \"" + rscData.Secret.ValueString() + "\"", } @@ -434,9 +435,7 @@ func (rscData *systemRadiusServerData) set( func (rscData *systemRadiusServerData) read( _ context.Context, address string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system radius-server " + address + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system_syslog_file.go b/internal/providerfwk/resource_system_syslog_file.go index d46e5946..45f37995 100644 --- a/internal/providerfwk/resource_system_syslog_file.go +++ b/internal/providerfwk/resource_system_syslog_file.go @@ -810,9 +810,7 @@ func (rscData *systemSyslogFileData) set( func (rscData *systemSyslogFileData) read( _ context.Context, filename string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system syslog file \"" + filename + "\"" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system_syslog_host.go b/internal/providerfwk/resource_system_syslog_host.go index 8ee648fc..99426747 100644 --- a/internal/providerfwk/resource_system_syslog_host.go +++ b/internal/providerfwk/resource_system_syslog_host.go @@ -583,9 +583,7 @@ func (rscData *systemSyslogHostData) set( func (rscData *systemSyslogHostData) read( _ context.Context, host string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system syslog host " + host + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system_syslog_user.go b/internal/providerfwk/resource_system_syslog_user.go index 30f6fc57..53fda0d7 100644 --- a/internal/providerfwk/resource_system_syslog_user.go +++ b/internal/providerfwk/resource_system_syslog_user.go @@ -484,9 +484,7 @@ func (rscData *systemSyslogUserData) set( func (rscData *systemSyslogUserData) read( _ context.Context, username string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system syslog user " + username + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_system_tacplus_server.go b/internal/providerfwk/resource_system_tacplus_server.go index 3b9d01f4..2fc67c3e 100644 --- a/internal/providerfwk/resource_system_tacplus_server.go +++ b/internal/providerfwk/resource_system_tacplus_server.go @@ -293,7 +293,7 @@ func (rsc *systemTacplusServer) ImportState( func checkSystemTacplusServerExists( _ context.Context, address string, junSess *junos.Session, ) ( - _ bool, err error, + bool, error, ) { showConfig, err := junSess.Command(junos.CmdShowConfig + "system tacplus-server " + address + junos.PipeDisplaySet) @@ -321,6 +321,7 @@ func (rscData *systemTacplusServerData) set( path.Path, error, ) { setPrefix := "set system tacplus-server " + rscData.Address.ValueString() + " " + configSet := []string{ setPrefix, } @@ -351,9 +352,7 @@ func (rscData *systemTacplusServerData) set( func (rscData *systemTacplusServerData) read( _ context.Context, address string, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "system tacplus-server " + address + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_virtual_chassis.go b/internal/providerfwk/resource_virtual_chassis.go index c4085f49..b1d06b07 100644 --- a/internal/providerfwk/resource_virtual_chassis.go +++ b/internal/providerfwk/resource_virtual_chassis.go @@ -604,8 +604,8 @@ func (rscData *virtualChassisData) set( ) ( path.Path, error, ) { - setPrefix := "set virtual-chassis " configSet := make([]string, 0) + setPrefix := "set virtual-chassis " if rscData.AutoSWUpdate.ValueBool() { configSet = append(configSet, setPrefix+"auto-sw-update") @@ -689,8 +689,8 @@ func (rscData *virtualChassisData) set( } func (block *virtualChassisBlockMember) configSet() []string { - setPrefix := "set virtual-chassis member " + utils.ConvI64toa(block.ID.ValueInt64()) + " " configSet := make([]string, 0, 1) + setPrefix := "set virtual-chassis member " + utils.ConvI64toa(block.ID.ValueInt64()) + " " if v := block.Location.ValueString(); v != "" { configSet = append(configSet, setPrefix+"location \""+v+"\"") @@ -714,6 +714,7 @@ func (block *virtualChassisBlockMember) configSet() []string { func (block *virtualChassisBlockTraceoptionsBlockFile) configSet() []string { setPrefix := "set virtual-chassis traceoptions file " + configSet := []string{ setPrefix + "\"" + block.Name.ValueString() + "\"", } @@ -744,9 +745,7 @@ func (block *virtualChassisBlockTraceoptionsBlockFile) configSet() []string { func (rscData *virtualChassisData) read( _ context.Context, junSess *junos.Session, -) ( - err error, -) { +) error { showConfig, err := junSess.Command(junos.CmdShowConfig + "virtual-chassis" + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resource_vlan.go b/internal/providerfwk/resource_vlan.go index 71c2e932..b35c096b 100644 --- a/internal/providerfwk/resource_vlan.go +++ b/internal/providerfwk/resource_vlan.go @@ -606,7 +606,6 @@ func checkVlanExists( if routingInstance != "" && routingInstance != junos.DefaultW { showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - showConfig, err := junSess.Command(showPrefix + "vlans " + name + junos.PipeDisplaySet) if err != nil { @@ -716,14 +715,11 @@ func (rscData *vlanData) set( func (rscData *vlanData) read( _ context.Context, name, routingInstance string, junSess *junos.Session, -) ( - err error, -) { +) error { showPrefix := junos.CmdShowConfig if routingInstance != "" && routingInstance != junos.DefaultW { showPrefix += junos.RoutingInstancesWS + routingInstance + " " } - showConfig, err := junSess.Command(showPrefix + "vlans " + name + junos.PipeDisplaySetRelative) if err != nil { diff --git a/internal/providerfwk/resourcedata_bgp.go b/internal/providerfwk/resourcedata_bgp.go index 28143d8e..83109c24 100644 --- a/internal/providerfwk/resourcedata_bgp.go +++ b/internal/providerfwk/resourcedata_bgp.go @@ -116,6 +116,7 @@ func (block *bgpBlockBfdLivenessDetection) configSet(setPrefix string) []string func (block *bgpBlockBgpErrorTolerance) configSet(setPrefix string) []string { setPrefix += "bgp-error-tolerance" + configSet := []string{ setPrefix, } @@ -137,6 +138,7 @@ func (block *bgpBlockBgpErrorTolerance) configSet(setPrefix string) []string { func (block *bgpBlockBgpMultipath) configSet(setPrefix string) []string { setPrefix += "multipath" + configSet := []string{ setPrefix, } @@ -162,6 +164,7 @@ func (block *bgpBlockFamily) configSet( error, // error ) { setPrefix += block.NlriType.ValueString() + " " + configSet := []string{ setPrefix, } @@ -216,6 +219,7 @@ func (block *bgpBlockFamily) configSet( func (block *bgpBlockGracefulRestart) configSet(setPrefix string) []string { setPrefix += "graceful-restart" + configSet := []string{ setPrefix, } diff --git a/internal/providerfwk/upgradestate_bridge_domain.go b/internal/providerfwk/upgradestate_bridge_domain.go index fc7f4d64..3a442876 100644 --- a/internal/providerfwk/upgradestate_bridge_domain.go +++ b/internal/providerfwk/upgradestate_bridge_domain.go @@ -108,8 +108,8 @@ func upgradeBridgeDomainStateV0toV1( VLANID types.Int64 `tfsdk:"vlan_id"` VLANIDList []types.String `tfsdk:"vlan_id_list"` VXLAN []struct { - VNI types.Int64 `tfsdk:"vni"` - VNIExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` + Vni types.Int64 `tfsdk:"vni"` + VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` @@ -140,12 +140,12 @@ func upgradeBridgeDomainStateV0toV1( dataV1.VLANIDList = dataV0.VLANIDList if len(dataV0.VXLAN) > 0 { dataV1.VXLAN = &bridgeDomainBlockVXLAN{ - VNIExtendEvpn: dataV0.VXLAN[0].VNIExtendEvpn, + Vni: dataV0.VXLAN[0].Vni, + VniExtendEvpn: dataV0.VXLAN[0].VniExtendEvpn, DecapsulateAcceptInnerVlan: dataV0.VXLAN[0].DecapsulateAcceptInnerVlan, EncapsulateInnerVlan: dataV0.VXLAN[0].EncapsulateInnerVlan, IngressNodeReplication: dataV0.VXLAN[0].IngressNodeReplication, OvsdbManaged: dataV0.VXLAN[0].OvsdbManaged, - VNI: dataV0.VXLAN[0].VNI, MulticastGroup: dataV0.VXLAN[0].MulticastGroup, UnreachableVtepAgingTimer: dataV0.VXLAN[0].UnreachableVtepAgingTimer, } diff --git a/internal/providersdk/constants.go b/internal/providersdk/constants.go index 4e97290a..1be2e1b0 100644 --- a/internal/providersdk/constants.go +++ b/internal/providersdk/constants.go @@ -1,3 +1,10 @@ package providersdk +import "github.com/jeremmfr/terraform-provider-junos/internal/junos" + const failedConvAtoiError = "failed to convert value from '%s' to integer: %w" + +const ( + setRoutingInstances = junos.SetLS + junos.RoutingInstancesWS + delRoutingInstances = junos.DeleteLS + junos.RoutingInstancesWS +) diff --git a/internal/providersdk/resource_access_address_assignment_pool.go b/internal/providersdk/resource_access_address_assignment_pool.go index 6f7e51e8..01767b0c 100644 --- a/internal/providersdk/resource_access_address_assignment_pool.go +++ b/internal/providersdk/resource_access_address_assignment_pool.go @@ -752,7 +752,7 @@ func setAccessAddressAssignPool(d *schema.ResourceData, junSess *junos.Session) setPrefix := junos.SetLS if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " } setPrefix += "access address-assignment pool " + d.Get("name").(string) + " " @@ -1366,7 +1366,7 @@ func delAccessAddressAssignPool(name, instance string, junSess *junos.Session) e if instance == junos.DefaultW { configSet = append(configSet, "delete access address-assignment pool "+name) } else { - configSet = append(configSet, junos.DelRoutingInstances+instance+" access address-assignment pool "+name) + configSet = append(configSet, delRoutingInstances+instance+" access address-assignment pool "+name) } return junSess.ConfigSet(configSet) diff --git a/internal/providersdk/resource_forwardingoptions_dhcprelay.go b/internal/providersdk/resource_forwardingoptions_dhcprelay.go index d830019a..93621c1d 100644 --- a/internal/providersdk/resource_forwardingoptions_dhcprelay.go +++ b/internal/providersdk/resource_forwardingoptions_dhcprelay.go @@ -788,7 +788,7 @@ func setForwardingOptionsDhcpRelay( //nolint:gocognit,gocyclo setPrefix := junos.SetLS if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " } if d.Get("version").(string) == "v6" { setPrefix += "forwarding-options dhcp-relay dhcpv6 " @@ -1555,9 +1555,9 @@ func delForwardingOptionsDhcpRelay(instance, version string, junSess *junos.Sess case instance == junos.DefaultW && version == "v4": delPrefix = junos.DeleteLS + "forwarding-options dhcp-relay " case instance != junos.DefaultW && version == "v6": - delPrefix = junos.DelRoutingInstances + instance + " forwarding-options dhcp-relay dhcpv6 " + delPrefix = delRoutingInstances + instance + " forwarding-options dhcp-relay dhcpv6 " case instance != junos.DefaultW && version == "v4": - delPrefix = junos.DelRoutingInstances + instance + " forwarding-options dhcp-relay " + delPrefix = delRoutingInstances + instance + " forwarding-options dhcp-relay " } listLinesToDelete := []string{ "access-profile", diff --git a/internal/providersdk/resource_forwardingoptions_dhcprelay_group.go b/internal/providersdk/resource_forwardingoptions_dhcprelay_group.go index 37dc22e1..3eb77847 100644 --- a/internal/providersdk/resource_forwardingoptions_dhcprelay_group.go +++ b/internal/providersdk/resource_forwardingoptions_dhcprelay_group.go @@ -885,7 +885,7 @@ func setForwardingOptionsDhcpRelayGroup(d *schema.ResourceData, junSess *junos.S setPrefix := junos.SetLS if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " } if d.Get("version").(string) == "v6" { setPrefix += "forwarding-options dhcp-relay dhcpv6 group " + d.Get("name").(string) + " " @@ -1675,10 +1675,10 @@ func delForwardingOptionsDhcpRelayGroup(name, instance, version string, junSess case instance == junos.DefaultW && version == "v4": configSet = append(configSet, junos.DeleteLS+"forwarding-options dhcp-relay group "+name) case instance != junos.DefaultW && version == "v6": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "forwarding-options dhcp-relay dhcpv6 group "+name) case instance != junos.DefaultW && version == "v4": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "forwarding-options dhcp-relay group "+name) } diff --git a/internal/providersdk/resource_forwardingoptions_dhcprelay_servergroup.go b/internal/providersdk/resource_forwardingoptions_dhcprelay_servergroup.go index 36d8d438..2ae80539 100644 --- a/internal/providersdk/resource_forwardingoptions_dhcprelay_servergroup.go +++ b/internal/providersdk/resource_forwardingoptions_dhcprelay_servergroup.go @@ -367,7 +367,7 @@ func setForwardingOptionsDhcpRelayServerGroup(d *schema.ResourceData, junSess *j setPrefix := junos.SetLS if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " + 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) + " " @@ -429,10 +429,10 @@ func delForwardingOptionsDhcpRelayServerGroup(name, instance, version string, ju case instance == junos.DefaultW && version == "v4": configSet = append(configSet, junos.DeleteLS+"forwarding-options dhcp-relay server-group "+name) case instance != junos.DefaultW && version == "v6": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "forwarding-options dhcp-relay dhcpv6 server-group "+name) case instance != junos.DefaultW && version == "v4": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "forwarding-options dhcp-relay server-group "+name) } diff --git a/internal/providersdk/resource_igmp_snooping_vlan.go b/internal/providersdk/resource_igmp_snooping_vlan.go index b371611c..d8babbec 100644 --- a/internal/providersdk/resource_igmp_snooping_vlan.go +++ b/internal/providersdk/resource_igmp_snooping_vlan.go @@ -418,7 +418,7 @@ func setIgmpSnoopingVlan(d *schema.ResourceData, junSess *junos.Session) error { setPrefix := junos.SetLS if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " + setPrefix = setRoutingInstances + rI + " " } setPrefix += "protocols igmp-snooping vlan " + d.Get("name").(string) + " " @@ -586,7 +586,7 @@ func delIgmpSnoopingVlan(name, routingInstance string, junSess *junos.Session) e if routingInstance == junos.DefaultW { configSet = append(configSet, "delete protocols igmp-snooping vlan "+name) } else { - configSet = append(configSet, junos.DelRoutingInstances+routingInstance+" protocols igmp-snooping vlan "+name) + configSet = append(configSet, delRoutingInstances+routingInstance+" protocols igmp-snooping vlan "+name) } return junSess.ConfigSet(configSet) diff --git a/internal/providersdk/resource_rip_group.go b/internal/providersdk/resource_rip_group.go index 8ed75da4..85eaceb9 100644 --- a/internal/providersdk/resource_rip_group.go +++ b/internal/providersdk/resource_rip_group.go @@ -498,7 +498,7 @@ func setRipGroup(d *schema.ResourceData, junSess *junos.Session) error { setPrefix := junos.SetLS if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " + setPrefix = setRoutingInstances + rI + " " } if d.Get("ng").(bool) { setPrefix += "protocols ripng group " @@ -726,7 +726,7 @@ func delRipGroup( ) error { delPrefix := junos.DeleteLS if routingInstance != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + routingInstance + " " + delPrefix = delRoutingInstances + routingInstance + " " } if ripNg { delPrefix += "protocols ripng group " diff --git a/internal/providersdk/resource_rip_neighbor.go b/internal/providersdk/resource_rip_neighbor.go index 0425d62a..432c10e3 100644 --- a/internal/providersdk/resource_rip_neighbor.go +++ b/internal/providersdk/resource_rip_neighbor.go @@ -622,7 +622,7 @@ func setRipNeighbor(d *schema.ResourceData, junSess *junos.Session) error { setPrefix := junos.SetLS if rI := d.Get("routing_instance").(string); rI != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + rI + " " + setPrefix = setRoutingInstances + rI + " " } if d.Get("ng").(bool) { setPrefix += "protocols ripng group " @@ -932,7 +932,7 @@ func delRipNeighbor( ) error { delPrefix := junos.DeleteLS if routingInstance != junos.DefaultW { - delPrefix = junos.DelRoutingInstances + routingInstance + " " + delPrefix = delRoutingInstances + routingInstance + " " } if ripNg { delPrefix += "protocols ripng group " diff --git a/internal/providersdk/resource_system_services_dhcp_localserver_group.go b/internal/providersdk/resource_system_services_dhcp_localserver_group.go index 2a0fdd3b..7e46ac6e 100644 --- a/internal/providersdk/resource_system_services_dhcp_localserver_group.go +++ b/internal/providersdk/resource_system_services_dhcp_localserver_group.go @@ -1072,7 +1072,7 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, junSess *juno setPrefix := junos.SetLS if d.Get("routing_instance").(string) != junos.DefaultW { - setPrefix = junos.SetRoutingInstances + d.Get("routing_instance").(string) + " " + setPrefix = setRoutingInstances + d.Get("routing_instance").(string) + " " } if d.Get("version").(string) == "v6" { setPrefix += "system services dhcp-local-server dhcpv6 group " + d.Get("name").(string) + " " @@ -2090,10 +2090,10 @@ func delSystemServicesDhcpLocalServerGroup(name, instance, version string, junSe case instance == junos.DefaultW && version == "v4": configSet = append(configSet, "delete system services dhcp-local-server group "+name) case instance != junos.DefaultW && version == "v6": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "system services dhcp-local-server dhcpv6 group "+name) case instance != junos.DefaultW && version == "v4": - configSet = append(configSet, junos.DelRoutingInstances+instance+" "+ + configSet = append(configSet, delRoutingInstances+instance+" "+ "system services dhcp-local-server group "+name) } From 69a9dba39eab42d8e49ce751dea7eb0314939a1f Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 14 Jun 2024 14:01:35 +0200 Subject: [PATCH 18/30] r/bridge_domain: fix potential wrong True when read vxlan.vni_extend_evpn argument --- internal/providerfwk/resource_bridge_domain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/providerfwk/resource_bridge_domain.go b/internal/providerfwk/resource_bridge_domain.go index a0e1cbc7..2230bdbf 100644 --- a/internal/providerfwk/resource_bridge_domain.go +++ b/internal/providerfwk/resource_bridge_domain.go @@ -746,7 +746,7 @@ func (rscData *bridgeDomainData) read( if strings.Contains(itemEvpn, junos.XMLEndTagConfigOut) { break } - if strings.HasPrefix(itemEvpn, junos.SetLS+"extended-vni-list "+itemTrim) { + if itemEvpn == junos.SetLS+"extended-vni-list "+itemTrim { rscData.VXLAN.VniExtendEvpn = types.BoolValue(true) break From e4914862fd6d1a4cb0309dd970b7f65f9ece2667 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 18 Jun 2024 09:14:04 +0200 Subject: [PATCH 19/30] r/routing_instance: add remote_vtep_list, remote_vtep_v6_list arguments Fix #673 --- .changes/issue-672-673.md | 4 ++ docs/resources/routing_instance.md | 4 ++ .../providerfwk/resource_routing_instance.go | 65 +++++++++++++++---- .../1/main.tf | 2 + .../2/main.tf | 1 + 5 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 .changes/issue-672-673.md diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md new file mode 100644 index 00000000..345a9fd5 --- /dev/null +++ b/.changes/issue-672-673.md @@ -0,0 +1,4 @@ + +ENHANCEMENTS: + +* **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) diff --git a/docs/resources/routing_instance.md b/docs/resources/routing_instance.md index 90cd3f34..a87861ee 100644 --- a/docs/resources/routing_instance.md +++ b/docs/resources/routing_instance.md @@ -40,6 +40,10 @@ The following arguments are supported: Export policy for instance RIBs. - **instance_import** (Optional, List of String) Import policy for instance RIBs. +- **remote_vtep_list** (Optional, Set of String) + Configure static remote VXLAN tunnel endpoints. +- **remote_vtep_v6_list** (Optional, Set of String) + Configure static ipv6 remote VXLAN tunnel endpoints. - **route_distinguisher** (Optional, String) Route distinguisher for this instance. - **router_id** (Optional, String) diff --git a/internal/providerfwk/resource_routing_instance.go b/internal/providerfwk/resource_routing_instance.go index a187d131..618d3cd9 100644 --- a/internal/providerfwk/resource_routing_instance.go +++ b/internal/providerfwk/resource_routing_instance.go @@ -11,6 +11,7 @@ import ( "github.com/jeremmfr/terraform-provider-junos/internal/tfvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -169,6 +170,28 @@ func (rsc *routingInstance) Schema( ), }, }, + "remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure static remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv4Only(), + ), + }, + }, + "remote_vtep_v6_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure static ipv6 remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv6Only(), + ), + }, + }, "route_distinguisher": schema.StringAttribute{ Optional: true, Description: "Route distinguisher for this instance.", @@ -270,6 +293,8 @@ type routingInstanceData struct { Description types.String `tfsdk:"description"` InstanceExport []types.String `tfsdk:"instance_export"` InstanceImport []types.String `tfsdk:"instance_import"` + RemoteVtepList []types.String `tfsdk:"remote_vtep_list"` + RemoteVtepV6List []types.String `tfsdk:"remote_vtep_v6_list"` RouteDistinguisher types.String `tfsdk:"route_distinguisher"` RouterID types.String `tfsdk:"router_id"` VRFExport []types.String `tfsdk:"vrf_export"` @@ -292,6 +317,8 @@ type routingInstanceConfig struct { Description types.String `tfsdk:"description"` InstanceExport types.List `tfsdk:"instance_export"` InstanceImport types.List `tfsdk:"instance_import"` + RemoteVtepList types.Set `tfsdk:"remote_vtep_list"` + RemoteVtepV6List types.Set `tfsdk:"remote_vtep_v6_list"` RouteDistinguisher types.String `tfsdk:"route_distinguisher"` RouterID types.String `tfsdk:"router_id"` VRFExport types.List `tfsdk:"vrf_export"` @@ -586,6 +613,12 @@ func (rscData *routingInstanceData) set( for _, v := range rscData.InstanceImport { configSet = append(configSet, setPrefix+junos.RoutingOptionsWS+"instance-import \""+v.ValueString()+"\"") } + for _, v := range rscData.RemoteVtepList { + configSet = append(configSet, setPrefix+"remote-vtep-list "+v.ValueString()) + } + for _, v := range rscData.RemoteVtepV6List { + configSet = append(configSet, setPrefix+"remote-vtep-v6-list "+v.ValueString()) + } if v := rscData.VTEPSourceInterface.ValueString(); v != "" { configSet = append(configSet, setPrefix+"vtep-source-interface "+v) } @@ -617,6 +650,10 @@ func (rscData *routingInstanceData) read( rscData.Description = types.StringValue(strings.Trim(itemTrim, "\"")) case balt.CutPrefixInString(&itemTrim, "instance-type "): rscData.Type = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "remote-vtep-list "): + rscData.RemoteVtepList = append(rscData.RemoteVtepList, types.StringValue(itemTrim)) + case balt.CutPrefixInString(&itemTrim, "remote-vtep-v6-list "): + rscData.RemoteVtepV6List = append(rscData.RemoteVtepV6List, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "route-distinguisher "): rscData.RouteDistinguisher = types.StringValue(itemTrim) case balt.CutPrefixInString(&itemTrim, junos.RoutingOptionsWS+"autonomous-system "): @@ -658,25 +695,27 @@ func (rscData *routingInstanceData) read( func (rscData *routingInstanceData) delOpts( _ context.Context, junSess *junos.Session, ) error { - configSet := make([]string, 0) - setPrefix := junos.DeleteLS + junos.RoutingInstancesWS + rscData.Name.ValueString() + " " + configSet := make([]string, 0, 50) + delPrefix := junos.DeleteLS + junos.RoutingInstancesWS + rscData.Name.ValueString() + " " configSet = append(configSet, - setPrefix+"description", - setPrefix+junos.RoutingOptionsWS+"autonomous-system", - setPrefix+junos.RoutingOptionsWS+"instance-export", - setPrefix+junos.RoutingOptionsWS+"instance-import", - setPrefix+junos.RoutingOptionsWS+"router-id", - setPrefix+"vtep-source-interface", + delPrefix+"description", + delPrefix+junos.RoutingOptionsWS+"autonomous-system", + delPrefix+junos.RoutingOptionsWS+"instance-export", + delPrefix+junos.RoutingOptionsWS+"instance-import", + delPrefix+"remote-vtep-list", + delPrefix+"remote-vtep-v6-list", + delPrefix+junos.RoutingOptionsWS+"router-id", + delPrefix+"vtep-source-interface", ) if !rscData.ConfigureTypeSingly.ValueBool() { - configSet = append(configSet, setPrefix+"instance-type") + configSet = append(configSet, delPrefix+"instance-type") } if !rscData.ConfigureRDVrfOptSingly.ValueBool() { configSet = append(configSet, - setPrefix+"route-distinguisher", - setPrefix+"vrf-export", - setPrefix+"vrf-import", - setPrefix+"vrf_target", + delPrefix+"route-distinguisher", + delPrefix+"vrf-export", + delPrefix+"vrf-import", + delPrefix+"vrf_target", ) } diff --git a/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/1/main.tf b/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/1/main.tf index 762f2ece..4b0a80ab 100644 --- a/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/1/main.tf @@ -51,4 +51,6 @@ resource "junos_routing_instance" "testacc_routingInst4" { route_distinguisher = "8:9" vrf_export = [junos_policyoptions_policy_statement.testacc_routingInst2.name] vrf_target_auto = true + remote_vtep_list = ["192.0.2.135", "192.0.2.35"] + remote_vtep_v6_list = ["fe80::34"] } diff --git a/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/2/main.tf b/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/2/main.tf index 8fe6060c..b9e776e7 100644 --- a/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/2/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceRoutingInstance_router/2/main.tf @@ -74,4 +74,5 @@ resource "junos_routing_instance" "testacc_routingInst4" { route_distinguisher = "10:11" vrf_export = [junos_policyoptions_policy_statement.testacc_routingInst2.name] vrf_target_auto = true + remote_vtep_list = ["192.0.2.35"] } From 7a3ab40db93db4e0c7d2106cf039c3ed38bb86ee Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 18 Jun 2024 09:19:16 +0200 Subject: [PATCH 20/30] r/switch_options: add remote_vtep_list, remote_vtep_v6_list arguments --- .changes/issue-672-673.md | 1 + docs/resources/switch_options.md | 4 ++ .../providerfwk/resource_switch_options.go | 45 +++++++++++++++++-- .../1/main.tf | 2 + 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md index 345a9fd5..3a64e219 100644 --- a/.changes/issue-672-673.md +++ b/.changes/issue-672-673.md @@ -2,3 +2,4 @@ ENHANCEMENTS: * **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) +* **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments diff --git a/docs/resources/switch_options.md b/docs/resources/switch_options.md index cb19dd99..f1954dbe 100644 --- a/docs/resources/switch_options.md +++ b/docs/resources/switch_options.md @@ -25,6 +25,10 @@ The following arguments are supported: - **clean_on_destroy** (Optional, Boolean) Clean supported lines when destroy this resource. +- **remote_vtep_list** (Optional, Set of String) + Configure static remote VXLAN tunnel endpoints. +- **remote_vtep_v6_list** (Optional, Set of String) + Configure static ipv6 remote VXLAN tunnel endpoints. - **service_id** (Optional, Number) Service ID required if multi-chassis AE is part of a bridge-domain (1..65535). - **vtep_source_interface** (Optional, String) diff --git a/internal/providerfwk/resource_switch_options.go b/internal/providerfwk/resource_switch_options.go index 99e75a65..37a1a5a7 100644 --- a/internal/providerfwk/resource_switch_options.go +++ b/internal/providerfwk/resource_switch_options.go @@ -10,6 +10,7 @@ import ( "github.com/jeremmfr/terraform-provider-junos/internal/utils" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -87,6 +88,28 @@ func (rsc *switchOptions) Schema( Optional: true, Description: "Clean supported lines when destroy this resource.", }, + "remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure static remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv4Only(), + ), + }, + }, + "remote_vtep_v6_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure static ipv6 remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv6Only(), + ), + }, + }, "service_id": schema.Int64Attribute{ Optional: true, Description: "Service ID required if multi-chassis AE is part of a bridge-domain.", @@ -108,10 +131,12 @@ func (rsc *switchOptions) Schema( } type switchOptionsData struct { - ID types.String `tfsdk:"id"` - CleanOnDestroy types.Bool `tfsdk:"clean_on_destroy"` - ServiceID types.Int64 `tfsdk:"service_id"` - VTEPSourceInterface types.String `tfsdk:"vtep_source_interface"` + ID types.String `tfsdk:"id"` + CleanOnDestroy types.Bool `tfsdk:"clean_on_destroy"` + RemoteVtepList []types.String `tfsdk:"remote_vtep_list"` + RemoteVtepV6List []types.String `tfsdk:"remote_vtep_v6_list"` + ServiceID types.Int64 `tfsdk:"service_id"` + VTEPSourceInterface types.String `tfsdk:"vtep_source_interface"` } func (rsc *switchOptions) Create( @@ -225,6 +250,12 @@ func (rscData *switchOptionsData) set( configSet := make([]string, 0) setPrefix := "set switch-options " + for _, v := range rscData.RemoteVtepList { + configSet = append(configSet, setPrefix+"remote-vtep-list "+v.ValueString()) + } + for _, v := range rscData.RemoteVtepV6List { + configSet = append(configSet, setPrefix+"remote-vtep-v6-list "+v.ValueString()) + } if !rscData.ServiceID.IsNull() { configSet = append(configSet, setPrefix+"service-id "+ utils.ConvI64toa(rscData.ServiceID.ValueInt64())) @@ -255,6 +286,10 @@ func (rscData *switchOptionsData) read( } itemTrim := strings.TrimPrefix(item, junos.SetLS) switch { + case balt.CutPrefixInString(&itemTrim, "remote-vtep-list "): + rscData.RemoteVtepList = append(rscData.RemoteVtepList, types.StringValue(itemTrim)) + case balt.CutPrefixInString(&itemTrim, "remote-vtep-v6-list "): + rscData.RemoteVtepV6List = append(rscData.RemoteVtepV6List, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "service-id "): rscData.ServiceID, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { @@ -275,6 +310,8 @@ func (rscData *switchOptionsData) del( delPrefix := "delete switch-options " configSet := []string{ + delPrefix + "remote-vtep-list", + delPrefix + "remote-vtep-v6-list", delPrefix + "service-id", delPrefix + "vtep-source-interface", } diff --git a/internal/providerfwk/testdata/TestAccResourceSwitchOptions_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceSwitchOptions_basic/1/main.tf index d87cf93a..e3089693 100644 --- a/internal/providerfwk/testdata/TestAccResourceSwitchOptions_basic/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceSwitchOptions_basic/1/main.tf @@ -10,6 +10,8 @@ resource "junos_interface_logical" "testacc_switchOpts" { } } resource "junos_switch_options" "testacc_switchOpts" { + remote_vtep_list = ["192.0.2.134", "192.0.2.34"] + remote_vtep_v6_list = ["fe80::34"] service_id = 111 vtep_source_interface = junos_interface_logical.testacc_switchOpts.name } From 4185d3e2a2895ebe2df49aad86c62e91598733d3 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 18 Jun 2024 13:55:24 +0200 Subject: [PATCH 21/30] r/bridge_domain: add static_remote_vtep_list argument inside vxlan block Fix #672 and use same way as vlan to vxlan/vlan-id field names on struct --- .changes/issue-672-673.md | 1 + docs/resources/bridge_domain.md | 6 +- .../providerfwk/resource_bridge_domain.go | 137 +++++++++++------- .../1/main.tf | 1 + .../4/main.tf | 4 +- .../5/main.tf | 4 +- .../providerfwk/upgradestate_bridge_domain.go | 30 ++-- 7 files changed, 110 insertions(+), 73 deletions(-) diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md index 3a64e219..6709e8ed 100644 --- a/.changes/issue-672-673.md +++ b/.changes/issue-672-673.md @@ -1,5 +1,6 @@ ENHANCEMENTS: +* **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) * **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) * **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments diff --git a/docs/resources/bridge_domain.md b/docs/resources/bridge_domain.md index 722a32dc..8a983661 100644 --- a/docs/resources/bridge_domain.md +++ b/docs/resources/bridge_domain.md @@ -55,6 +55,8 @@ The following arguments are supported: Declare vxlan options. - **vni** (Required, Number) VXLAN identifier (0..16777214). + - **vni_extend_evpn** (Optional, Boolean) + Extend VNI to EVPN. - **decapsulate_accept_inner_vlan** (Optional, Boolean) Accept VXLAN packets with inner VLAN. - **encapsulate_inner_vlan** (Optional, Boolean) @@ -65,10 +67,10 @@ The following arguments are supported: CIDR for Multicast group registered for VXLAN segment. - **ovsdb_managed** (Optional, Boolean) Bridge-domain is managed remotely via VXLAN OVSDB Controller. + - **static_remote_vtep_list** (Optional, Set of String) + Configure bridge domain specific static remote VXLAN tunnel endpoints. - **unreachable_vtep_aging_timer** (Optional, Number) Unreachable VXLAN tunnel endpoint removal timer (300..1800 seconds). - - **vni_extend_evpn** (Optional, Boolean) - Extend VNI to EVPN. ## Attribute Reference diff --git a/internal/providerfwk/resource_bridge_domain.go b/internal/providerfwk/resource_bridge_domain.go index 2230bdbf..6957d743 100644 --- a/internal/providerfwk/resource_bridge_domain.go +++ b/internal/providerfwk/resource_bridge_domain.go @@ -261,6 +261,17 @@ func (rsc *bridgeDomain) Schema( tfvalidator.BoolTrue(), }, }, + "static_remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure bridge domain specific static remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv4Only(), + ), + }, + }, "unreachable_vtep_aging_timer": schema.Int64Attribute{ Optional: true, Description: "Unreachable VXLAN tunnel endpoint removal timer.", @@ -289,33 +300,45 @@ type bridgeDomainData struct { IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` RoutingInterface types.String `tfsdk:"routing_interface"` ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList []types.String `tfsdk:"vlan_id_list"` - VXLAN *bridgeDomainBlockVXLAN `tfsdk:"vxlan"` + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList []types.String `tfsdk:"vlan_id_list"` + Vxlan *bridgeDomainBlockVxlan `tfsdk:"vxlan"` } type bridgeDomainConfig struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - RoutingInstance types.String `tfsdk:"routing_instance"` - CommunityVlans types.Set `tfsdk:"community_vlans"` - Description types.String `tfsdk:"description"` - DomainID types.Int64 `tfsdk:"domain_id"` - DomainTypeBridge types.Bool `tfsdk:"domain_type_bridge"` - Interface types.Set `tfsdk:"interface"` - IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` - RoutingInterface types.String `tfsdk:"routing_interface"` - ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList types.Set `tfsdk:"vlan_id_list"` - VXLAN *bridgeDomainBlockVXLAN `tfsdk:"vxlan"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + CommunityVlans types.Set `tfsdk:"community_vlans"` + Description types.String `tfsdk:"description"` + DomainID types.Int64 `tfsdk:"domain_id"` + DomainTypeBridge types.Bool `tfsdk:"domain_type_bridge"` + Interface types.Set `tfsdk:"interface"` + IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` + RoutingInterface types.String `tfsdk:"routing_interface"` + ServiceID types.Int64 `tfsdk:"service_id"` + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList types.Set `tfsdk:"vlan_id_list"` + Vxlan *bridgeDomainBlockVxlanConfig `tfsdk:"vxlan"` } func (rscConfig *bridgeDomainConfig) isEmpty() bool { return tfdata.CheckBlockIsEmpty(rscConfig, "ID", "Name", "RoutingInstance") } -type bridgeDomainBlockVXLAN struct { +type bridgeDomainBlockVxlan struct { + Vni types.Int64 `tfsdk:"vni"` + VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` + DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` + EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` + IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` + MulticastGroup types.String `tfsdk:"multicast_group"` + OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList []types.String `tfsdk:"static_remote_vtep_list"` + UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` +} + +type bridgeDomainBlockVxlanConfig struct { Vni types.Int64 `tfsdk:"vni"` VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` @@ -323,6 +346,7 @@ type bridgeDomainBlockVXLAN struct { IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` MulticastGroup types.String `tfsdk:"multicast_group"` OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList types.Set `tfsdk:"static_remote_vtep_list"` UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` } @@ -343,16 +367,16 @@ func (rsc *bridgeDomain) ValidateConfig( ) } - if !config.VLANID.IsNull() && !config.VLANID.IsUnknown() && - !config.VLANIDList.IsNull() && !config.VLANIDList.IsUnknown() { + if !config.VlanID.IsNull() && !config.VlanID.IsUnknown() && + !config.VlanIDList.IsNull() && !config.VlanIDList.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("vlan_id"), tfdiag.ConflictConfigErrSummary, "vlan_id and vlan_id_list cannot be configured together", ) } - if config.VXLAN != nil { - if config.VXLAN.Vni.IsNull() { + if config.Vxlan != nil { + if config.Vxlan.Vni.IsNull() { resp.Diagnostics.AddAttributeError( path.Root("vxlan").AtName("vni"), tfdiag.MissingConfigErrSummary, @@ -624,39 +648,42 @@ func (rscData *bridgeDomainData) set( configSet = append(configSet, setPrefix+"service-id "+ utils.ConvI64toa(rscData.ServiceID.ValueInt64())) } - if !rscData.VLANID.IsNull() { + if !rscData.VlanID.IsNull() { configSet = append(configSet, setPrefix+"vlan-id "+ - utils.ConvI64toa(rscData.VLANID.ValueInt64())) + utils.ConvI64toa(rscData.VlanID.ValueInt64())) } - for _, v := range rscData.VLANIDList { + for _, v := range rscData.VlanIDList { configSet = append(configSet, setPrefix+"vlan-id-list "+v.ValueString()) } - if rscData.VXLAN != nil { + if rscData.Vxlan != nil { configSet = append(configSet, setPrefix+"vxlan vni "+ - utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, setPrefixProtocolsEvpn+"extended-vni-list "+ - utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } - if rscData.VXLAN.DecapsulateAcceptInnerVlan.ValueBool() { + if rscData.Vxlan.DecapsulateAcceptInnerVlan.ValueBool() { configSet = append(configSet, setPrefix+"vxlan decapsulate-accept-inner-vlan") } - if rscData.VXLAN.EncapsulateInnerVlan.ValueBool() { + if rscData.Vxlan.EncapsulateInnerVlan.ValueBool() { configSet = append(configSet, setPrefix+"vxlan encapsulate-inner-vlan") } - if rscData.VXLAN.IngressNodeReplication.ValueBool() { + if rscData.Vxlan.IngressNodeReplication.ValueBool() { configSet = append(configSet, setPrefix+"vxlan ingress-node-replication") } - if v := rscData.VXLAN.MulticastGroup.ValueString(); v != "" { + if v := rscData.Vxlan.MulticastGroup.ValueString(); v != "" { configSet = append(configSet, setPrefix+"vxlan multicast-group "+v) } - if rscData.VXLAN.OvsdbManaged.ValueBool() { + if rscData.Vxlan.OvsdbManaged.ValueBool() { configSet = append(configSet, setPrefix+"vxlan ovsdb-managed") } - if !rscData.VXLAN.UnreachableVtepAgingTimer.IsNull() { + for _, v := range rscData.Vxlan.StaticRemoteVtepList { + configSet = append(configSet, setPrefix+"vxlan static-remote-vtep-list "+v.ValueString()) + } + if !rscData.Vxlan.UnreachableVtepAgingTimer.IsNull() { configSet = append(configSet, setPrefix+"vxlan unreachable-vtep-aging-timer "+ - utils.ConvI64toa(rscData.VXLAN.UnreachableVtepAgingTimer.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.UnreachableVtepAgingTimer.ValueInt64())) } } @@ -718,19 +745,19 @@ func (rscData *bridgeDomainData) read( return err } case balt.CutPrefixInString(&itemTrim, "vlan-id "): - rscData.VLANID, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.VlanID, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } case balt.CutPrefixInString(&itemTrim, "vlan-id-list "): - rscData.VLANIDList = append(rscData.VLANIDList, types.StringValue(itemTrim)) + rscData.VlanIDList = append(rscData.VlanIDList, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "vxlan "): - if rscData.VXLAN == nil { - rscData.VXLAN = &bridgeDomainBlockVXLAN{} + if rscData.Vxlan == nil { + rscData.Vxlan = &bridgeDomainBlockVxlan{} } switch { case balt.CutPrefixInString(&itemTrim, "vni "): - rscData.VXLAN.Vni, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.Vxlan.Vni, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } @@ -747,24 +774,26 @@ func (rscData *bridgeDomainData) read( break } if itemEvpn == junos.SetLS+"extended-vni-list "+itemTrim { - rscData.VXLAN.VniExtendEvpn = types.BoolValue(true) + rscData.Vxlan.VniExtendEvpn = types.BoolValue(true) break } } } case itemTrim == "decapsulate-accept-inner-vlan": - rscData.VXLAN.DecapsulateAcceptInnerVlan = types.BoolValue(true) + rscData.Vxlan.DecapsulateAcceptInnerVlan = types.BoolValue(true) case itemTrim == "encapsulate-inner-vlan": - rscData.VXLAN.EncapsulateInnerVlan = types.BoolValue(true) + rscData.Vxlan.EncapsulateInnerVlan = types.BoolValue(true) case itemTrim == "ingress-node-replication": - rscData.VXLAN.IngressNodeReplication = types.BoolValue(true) + rscData.Vxlan.IngressNodeReplication = types.BoolValue(true) case balt.CutPrefixInString(&itemTrim, "multicast-group "): - rscData.VXLAN.MulticastGroup = types.StringValue(itemTrim) + rscData.Vxlan.MulticastGroup = types.StringValue(itemTrim) case itemTrim == "ovsdb-managed": - rscData.VXLAN.OvsdbManaged = types.BoolValue(true) + rscData.Vxlan.OvsdbManaged = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "static-remote-vtep-list "): + rscData.Vxlan.StaticRemoteVtepList = append(rscData.Vxlan.StaticRemoteVtepList, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "unreachable-vtep-aging-timer "): - rscData.VXLAN.UnreachableVtepAgingTimer, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.Vxlan.UnreachableVtepAgingTimer, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } @@ -801,10 +830,10 @@ func (rscData *bridgeDomainData) delOpts( for _, v := range rscData.Interface { configSet = append(configSet, delPrefix+"interface "+v.ValueString()) } - if rscData.VXLAN != nil { - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan != nil { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, delPrefixProtocolsEvpn+ - "extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + "extended-vni-list "+utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } } @@ -822,10 +851,10 @@ func (rscData *bridgeDomainData) del( configSet := []string{ delPrefix + "bridge-domains \"" + rscData.Name.ValueString() + "\"", } - if rscData.VXLAN != nil { - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan != nil { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, delPrefix+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } } diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf index 033694e3..2aafcff9 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf @@ -32,6 +32,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf index 702d952d..5b912618 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf @@ -27,6 +27,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name @@ -48,6 +49,7 @@ resource "junos_bridge_domain" "testacc_bridge_ri" { service_id = 12 vlan_id = 13 vxlan { - vni = 15 + vni = 15 + static_remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } } diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf index 07cefaba..7a44871f 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf @@ -27,6 +27,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name @@ -47,6 +48,7 @@ resource "junos_bridge_domain" "testacc_bridge_ri" { service_id = 12 vlan_id = 13 vxlan { - vni = 15 + vni = 15 + static_remote_vtep_list = ["192.0.2.36"] } } diff --git a/internal/providerfwk/upgradestate_bridge_domain.go b/internal/providerfwk/upgradestate_bridge_domain.go index 3a442876..5e6357d4 100644 --- a/internal/providerfwk/upgradestate_bridge_domain.go +++ b/internal/providerfwk/upgradestate_bridge_domain.go @@ -105,9 +105,9 @@ func upgradeBridgeDomainStateV0toV1( IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` RoutingInterface types.String `tfsdk:"routing_interface"` ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList []types.String `tfsdk:"vlan_id_list"` - VXLAN []struct { + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList []types.String `tfsdk:"vlan_id_list"` + Vxlan []struct { Vni types.Int64 `tfsdk:"vni"` VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` @@ -136,18 +136,18 @@ func upgradeBridgeDomainStateV0toV1( dataV1.IsolatedVLAN = dataV0.IsolatedVLAN dataV1.RoutingInterface = dataV0.RoutingInterface dataV1.ServiceID = dataV0.ServiceID - dataV1.VLANID = dataV0.VLANID - dataV1.VLANIDList = dataV0.VLANIDList - if len(dataV0.VXLAN) > 0 { - dataV1.VXLAN = &bridgeDomainBlockVXLAN{ - Vni: dataV0.VXLAN[0].Vni, - VniExtendEvpn: dataV0.VXLAN[0].VniExtendEvpn, - DecapsulateAcceptInnerVlan: dataV0.VXLAN[0].DecapsulateAcceptInnerVlan, - EncapsulateInnerVlan: dataV0.VXLAN[0].EncapsulateInnerVlan, - IngressNodeReplication: dataV0.VXLAN[0].IngressNodeReplication, - OvsdbManaged: dataV0.VXLAN[0].OvsdbManaged, - MulticastGroup: dataV0.VXLAN[0].MulticastGroup, - UnreachableVtepAgingTimer: dataV0.VXLAN[0].UnreachableVtepAgingTimer, + dataV1.VlanID = dataV0.VlanID + dataV1.VlanIDList = dataV0.VlanIDList + if len(dataV0.Vxlan) > 0 { + dataV1.Vxlan = &bridgeDomainBlockVxlan{ + Vni: dataV0.Vxlan[0].Vni, + VniExtendEvpn: dataV0.Vxlan[0].VniExtendEvpn, + DecapsulateAcceptInnerVlan: dataV0.Vxlan[0].DecapsulateAcceptInnerVlan, + EncapsulateInnerVlan: dataV0.Vxlan[0].EncapsulateInnerVlan, + IngressNodeReplication: dataV0.Vxlan[0].IngressNodeReplication, + OvsdbManaged: dataV0.Vxlan[0].OvsdbManaged, + MulticastGroup: dataV0.Vxlan[0].MulticastGroup, + UnreachableVtepAgingTimer: dataV0.Vxlan[0].UnreachableVtepAgingTimer, } } From 6b4379e6b66fe84b4f83ac9b95eaf4372b813901 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 18 Jun 2024 20:41:39 +0200 Subject: [PATCH 22/30] r/vlan: add static_remote_vtep_list argument inside vxlan block argument --- .changes/issue-672-673.md | 1 + docs/resources/vlan.md | 6 +- internal/providerfwk/resource_vlan.go | 61 +++++++++++++----- internal/providerfwk/resource_vlan_test.go | 17 +++++ .../TestAccResourceVlan_router/1/main.tf | 64 +++++++++++++++++++ .../TestAccResourceVlan_router/2/main.tf | 63 ++++++++++++++++++ 6 files changed, 194 insertions(+), 18 deletions(-) create mode 100644 internal/providerfwk/testdata/TestAccResourceVlan_router/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceVlan_router/2/main.tf diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md index 6709e8ed..6f556987 100644 --- a/.changes/issue-672-673.md +++ b/.changes/issue-672-673.md @@ -4,3 +4,4 @@ ENHANCEMENTS: * **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) * **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) * **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments +* **resource/junos_vlan**: add `static_remote_vtep_list` argument inside `vxlan` block argument diff --git a/docs/resources/vlan.md b/docs/resources/vlan.md index 4a64771c..8a7047c8 100644 --- a/docs/resources/vlan.md +++ b/docs/resources/vlan.md @@ -60,6 +60,8 @@ The following arguments are supported: Declare vxlan configuration. - **vni** (Required, Number) VXLAN identifier (0..16777214). + - **vni_extend_evpn** (Optional, Boolean) + Extend VNI to EVPN. - **encapsulate_inner_vlan** (Optional, Boolean) Retain inner VLAN in the packet. - **ingress_node_replication** (Optional, Boolean) @@ -68,10 +70,10 @@ The following arguments are supported: Multicast group registered for VXLAN segment. - **ovsdb_managed** (Optional, Boolean) Bridge-domain is managed remotely via VXLAN OVSDB Controller. + - **static_remote_vtep_list** (Optional, Set of String) + Configure vlan specific static remote VXLAN tunnel endpoints. - **translation_vni** (Optional, Number) Translated VXLAN identifier (1..16777214). - - **vni_extend_evpn** (Optional, Boolean) - Extend VNI to EVPN. - **unreachable_vtep_aging_timer** (Optional, Number) Unreachable VXLAN tunnel endpoint removal timer (300..1800 seconds). diff --git a/internal/providerfwk/resource_vlan.go b/internal/providerfwk/resource_vlan.go index b35c096b..c554d648 100644 --- a/internal/providerfwk/resource_vlan.go +++ b/internal/providerfwk/resource_vlan.go @@ -270,6 +270,17 @@ func (rsc *vlan) Schema( tfvalidator.BoolTrue(), }, }, + "static_remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure vlan specific static remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv4Only(), + ), + }, + }, "translation_vni": schema.Int64Attribute{ Optional: true, Description: "Translated VXLAN identifier.", @@ -313,22 +324,22 @@ type vlanData struct { } type vlanConfig struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - RoutingInstance types.String `tfsdk:"routing_instance"` - CommunityVlans types.Set `tfsdk:"community_vlans"` - Description types.String `tfsdk:"description"` - ForwardFilterInput types.String `tfsdk:"forward_filter_input"` - ForwardFilterOutput types.String `tfsdk:"forward_filter_output"` - ForwardFloodInput types.String `tfsdk:"forward_flood_input"` - IsolatedVlan types.String `tfsdk:"isolated_vlan"` - L3Interface types.String `tfsdk:"l3_interface"` - NoARPSuppression types.Bool `tfsdk:"no_arp_suppression"` - PrivateVlan types.String `tfsdk:"private_vlan"` - ServiceID types.Int64 `tfsdk:"service_id"` - VlanID types.String `tfsdk:"vlan_id"` - VlanIDList types.Set `tfsdk:"vlan_id_list"` - Vxlan *vlanBlockVxlan `tfsdk:"vxlan"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + CommunityVlans types.Set `tfsdk:"community_vlans"` + Description types.String `tfsdk:"description"` + ForwardFilterInput types.String `tfsdk:"forward_filter_input"` + ForwardFilterOutput types.String `tfsdk:"forward_filter_output"` + ForwardFloodInput types.String `tfsdk:"forward_flood_input"` + IsolatedVlan types.String `tfsdk:"isolated_vlan"` + L3Interface types.String `tfsdk:"l3_interface"` + NoARPSuppression types.Bool `tfsdk:"no_arp_suppression"` + PrivateVlan types.String `tfsdk:"private_vlan"` + ServiceID types.Int64 `tfsdk:"service_id"` + VlanID types.String `tfsdk:"vlan_id"` + VlanIDList types.Set `tfsdk:"vlan_id_list"` + Vxlan *vlanBlockVxlanConfig `tfsdk:"vxlan"` } func (rscConfig *vlanConfig) isEmpty() bool { @@ -336,12 +347,25 @@ func (rscConfig *vlanConfig) isEmpty() bool { } type vlanBlockVxlan struct { + Vni types.Int64 `tfsdk:"vni"` + VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` + EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` + IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` + MulticastGroup types.String `tfsdk:"multicast_group"` + OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList []types.String `tfsdk:"static_remote_vtep_list"` + TranslationVni types.Int64 `tfsdk:"translation_vni"` + UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` +} + +type vlanBlockVxlanConfig struct { Vni types.Int64 `tfsdk:"vni"` VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` MulticastGroup types.String `tfsdk:"multicast_group"` OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList types.Set `tfsdk:"static_remote_vtep_list"` TranslationVni types.Int64 `tfsdk:"translation_vni"` UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` } @@ -700,6 +724,9 @@ func (rscData *vlanData) set( if rscData.Vxlan.OvsdbManaged.ValueBool() { configSet = append(configSet, setPrefix+"vxlan ovsdb-managed") } + for _, v := range rscData.Vxlan.StaticRemoteVtepList { + configSet = append(configSet, setPrefix+"vxlan static-remote-vtep-list "+v.ValueString()) + } if !rscData.Vxlan.TranslationVni.IsNull() { configSet = append(configSet, setPrefix+"vxlan translation-vni "+ utils.ConvI64toa(rscData.Vxlan.TranslationVni.ValueInt64())) @@ -804,6 +831,8 @@ func (rscData *vlanData) read( rscData.Vxlan.MulticastGroup = types.StringValue(itemTrim) case itemTrim == "ovsdb-managed": rscData.Vxlan.OvsdbManaged = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "static-remote-vtep-list "): + rscData.Vxlan.StaticRemoteVtepList = append(rscData.Vxlan.StaticRemoteVtepList, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "translation-vni "): rscData.Vxlan.TranslationVni, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { diff --git a/internal/providerfwk/resource_vlan_test.go b/internal/providerfwk/resource_vlan_test.go index 70a22baa..59b02f65 100644 --- a/internal/providerfwk/resource_vlan_test.go +++ b/internal/providerfwk/resource_vlan_test.go @@ -59,3 +59,20 @@ func TestAccResourceVlan_basic(t *testing.T) { }) } } + +func TestAccResourceVlan_router(t *testing.T) { + if os.Getenv("TESTACC_ROUTER") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVlan_router/1/main.tf b/internal/providerfwk/testdata/TestAccResourceVlan_router/1/main.tf new file mode 100644 index 00000000..b14c2291 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVlan_router/1/main.tf @@ -0,0 +1,64 @@ + +resource "junos_routing_options" "testacc_vlan_vxlan" { + clean_on_destroy = true + router_id = "192.0.2.18" +} +resource "junos_interface_logical" "testacc_vlan_vxlan" { + depends_on = [ + junos_routing_options.testacc_vlan_vxlan, + ] + name = "lo0.0" + description = "testacc_vlan_vxlan" + family_inet { + address { + cidr_ip = "192.0.2.18/32" + } + } +} +resource "junos_switch_options" "testacc_vlan_vxlan" { + clean_on_destroy = true + vtep_source_interface = junos_interface_logical.testacc_vlan_vxlan.name +} +resource "junos_policyoptions_community" "testacc_vlan_vxlan" { + lifecycle { + create_before_destroy = true + } + name = "testacc_vlan_vxlan" + members = ["target:65000:100"] +} +resource "junos_policyoptions_policy_statement" "testacc_vlan_vxlan" { + lifecycle { + create_before_destroy = true + } + name = "testacc_vlan_vxlan" + from { + bgp_community = [junos_policyoptions_community.testacc_vlan_vxlan.name] + } + then { + action = "accept" + } +} +resource "junos_evpn" "testacc_vlan_vxlan" { + depends_on = [ + junos_switch_options.testacc_vlan_vxlan, + ] + encapsulation = "vxlan" + switch_or_ri_options { + route_distinguisher = "20:1" + vrf_target = "target:20:2" + vrf_import = [junos_policyoptions_policy_statement.testacc_vlan_vxlan.name] + vrf_export = [junos_policyoptions_policy_statement.testacc_vlan_vxlan.name] + } +} + +resource "junos_vlan" "testacc_vlan_vxlan" { + depends_on = [ + junos_evpn.testacc_vlan_vxlan, + ] + name = "testacc_vlan_vxlan" + vlan_id = 1020 + vxlan { + vni = 102010 + static_remote_vtep_list = ["192.0.2.136", "192.0.2.36"] + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceVlan_router/2/main.tf b/internal/providerfwk/testdata/TestAccResourceVlan_router/2/main.tf new file mode 100644 index 00000000..43a38464 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceVlan_router/2/main.tf @@ -0,0 +1,63 @@ +resource "junos_routing_options" "testacc_vlan_vxlan" { + clean_on_destroy = true + router_id = "192.0.2.18" +} +resource "junos_interface_logical" "testacc_vlan_vxlan" { + depends_on = [ + junos_routing_options.testacc_vlan_vxlan, + ] + name = "lo0.0" + description = "testacc_vlan_vxlan" + family_inet { + address { + cidr_ip = "192.0.2.18/32" + } + } +} +resource "junos_switch_options" "testacc_vlan_vxlan" { + clean_on_destroy = true + vtep_source_interface = junos_interface_logical.testacc_vlan_vxlan.name +} +resource "junos_policyoptions_community" "testacc_vlan_vxlan" { + lifecycle { + create_before_destroy = true + } + name = "testacc_vlan_vxlan" + members = ["target:65000:100"] +} +resource "junos_policyoptions_policy_statement" "testacc_vlan_vxlan" { + lifecycle { + create_before_destroy = true + } + name = "testacc_vlan_vxlan" + from { + bgp_community = [junos_policyoptions_community.testacc_vlan_vxlan.name] + } + then { + action = "accept" + } +} +resource "junos_evpn" "testacc_vlan_vxlan" { + depends_on = [ + junos_switch_options.testacc_vlan_vxlan, + ] + encapsulation = "vxlan" + switch_or_ri_options { + route_distinguisher = "20:1" + vrf_target = "target:20:2" + vrf_import = [junos_policyoptions_policy_statement.testacc_vlan_vxlan.name] + vrf_export = [junos_policyoptions_policy_statement.testacc_vlan_vxlan.name] + } +} + +resource "junos_vlan" "testacc_vlan_vxlan" { + depends_on = [ + junos_evpn.testacc_vlan_vxlan, + ] + name = "testacc_vlan_vxlan" + vlan_id = 1020 + vxlan { + vni = 102010 + static_remote_vtep_list = ["192.0.2.136"] + } +} From 443f59fea3158fa60ab561901e02a9d60781533b Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 18 Jun 2024 20:49:11 +0200 Subject: [PATCH 23/30] d/routing_instance: add remote_vtep_list and remote_vtep_v6_list attributes like resource --- .changes/issue-672-673.md | 1 + docs/data-sources/routing_instance.md | 4 ++++ .../providerfwk/data_source_routing_instance.go | 14 ++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md index 6f556987..afcac119 100644 --- a/.changes/issue-672-673.md +++ b/.changes/issue-672-673.md @@ -3,5 +3,6 @@ ENHANCEMENTS: * **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) * **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) +* **data-source/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` attributes like resource * **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments * **resource/junos_vlan**: add `static_remote_vtep_list` argument inside `vxlan` block argument diff --git a/docs/data-sources/routing_instance.md b/docs/data-sources/routing_instance.md index 168eef66..9b5fe954 100644 --- a/docs/data-sources/routing_instance.md +++ b/docs/data-sources/routing_instance.md @@ -40,6 +40,10 @@ The following attributes are exported: Import policy for instance RIBs. - **interface** (Set of String) List of interfaces in routing instance. +- **remote_vtep_list** (Set of String) + Static remote VXLAN tunnel endpoints. +- **remote_vtep_v6_list** (Set of String) + Static ipv6 remote VXLAN tunnel endpoints. - **route_distinguisher** (String) Route distinguisher for this instance. - **router_id** (String) diff --git a/internal/providerfwk/data_source_routing_instance.go b/internal/providerfwk/data_source_routing_instance.go index b5ba674c..693236d3 100644 --- a/internal/providerfwk/data_source_routing_instance.go +++ b/internal/providerfwk/data_source_routing_instance.go @@ -109,6 +109,16 @@ func (dsc *routingInstanceDataSource) Schema( Computed: true, Description: "List of interfaces in routing instance.", }, + "remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Static remote VXLAN tunnel endpoints.", + }, + "remote_vtep_v6_list": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + Description: "Static ipv6 remote VXLAN tunnel endpoints.", + }, "route_distinguisher": schema.StringAttribute{ Computed: true, Description: "Route distinguisher for this instance.", @@ -160,6 +170,8 @@ type routingInstanceDataSourceData struct { InstanceExport []types.String `tfsdk:"instance_export"` InstanceImport []types.String `tfsdk:"instance_import"` Interface []types.String `tfsdk:"interface"` + RemoteVtepList []types.String `tfsdk:"remote_vtep_list"` + RemoteVtepV6List []types.String `tfsdk:"remote_vtep_v6_list"` RouteDistinguisher types.String `tfsdk:"route_distinguisher"` RouterID types.String `tfsdk:"router_id"` VRFExport []types.String `tfsdk:"vrf_export"` @@ -207,6 +219,8 @@ func (dscData *routingInstanceDataSourceData) copyFromResourceData(data any) { dscData.InstanceExport = rscData.InstanceExport dscData.InstanceImport = rscData.InstanceImport dscData.Interface = rscData.Interface + dscData.RemoteVtepList = rscData.RemoteVtepList + dscData.RemoteVtepV6List = rscData.RemoteVtepV6List dscData.RouteDistinguisher = rscData.RouteDistinguisher dscData.RouterID = rscData.RouterID dscData.VRFExport = rscData.VRFExport From 288dfa669bd1f91f77efb881684b95e2ae6b00e5 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 20 Jun 2024 14:08:08 +0200 Subject: [PATCH 24/30] r/interface_logical: add encapsulation argument Fix #674 --- .changes/issue-674.md | 4 ++++ docs/resources/interface_logical.md | 2 ++ .../providerfwk/resource_interface_logical.go | 16 ++++++++++++++++ .../5/main.tf | 3 ++- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changes/issue-674.md diff --git a/.changes/issue-674.md b/.changes/issue-674.md new file mode 100644 index 00000000..ee15455e --- /dev/null +++ b/.changes/issue-674.md @@ -0,0 +1,4 @@ + +ENHANCEMENTS: + +* **resource/junos_interface_logical**: add `encapsulation` argument (Fix [#674](https://github.com/jeremmfr/terraform-provider-junos/issues/674)) diff --git a/docs/resources/interface_logical.md b/docs/resources/interface_logical.md index fc22e2ba..d8099e21 100644 --- a/docs/resources/interface_logical.md +++ b/docs/resources/interface_logical.md @@ -35,6 +35,8 @@ The following arguments are supported: Description for interface. - **disable** (Optional, Boolean) Disable this logical interface. +- **encapsulation** (Optional, String) + Logical link-layer encapsulation. - **family_inet** (Optional, Block) Enable family inet and add configurations if specified. - **address** (Optional, Block List) diff --git a/internal/providerfwk/resource_interface_logical.go b/internal/providerfwk/resource_interface_logical.go index b95a46de..74291047 100644 --- a/internal/providerfwk/resource_interface_logical.go +++ b/internal/providerfwk/resource_interface_logical.go @@ -130,6 +130,14 @@ func (rsc *interfaceLogical) Schema( tfvalidator.BoolTrue(), }, }, + "encapsulation": schema.StringAttribute{ + Optional: true, + Description: "Logical link-layer encapsulation.", + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, "routing_instance": schema.StringAttribute{ Optional: true, Description: "Add this interface in routing_instance.", @@ -1017,6 +1025,7 @@ type interfaceLogicalData struct { St0AlsoOnDestroy types.Bool `tfsdk:"st0_also_on_destroy"` Description types.String `tfsdk:"description"` Disable types.Bool `tfsdk:"disable"` + Encapsulation types.String `tfsdk:"encapsulation"` RoutingInstance types.String `tfsdk:"routing_instance"` SecurityInboundProtocols []types.String `tfsdk:"security_inbound_protocols"` SecurityInboundServices []types.String `tfsdk:"security_inbound_services"` @@ -1034,6 +1043,7 @@ type interfaceLogicalConfig struct { St0AlsoOnDestroy types.Bool `tfsdk:"st0_also_on_destroy"` Description types.String `tfsdk:"description"` Disable types.Bool `tfsdk:"disable"` + Encapsulation types.String `tfsdk:"encapsulation"` RoutingInstance types.String `tfsdk:"routing_instance"` SecurityInboundProtocols types.Set `tfsdk:"security_inbound_protocols"` SecurityInboundServices types.Set `tfsdk:"security_inbound_services"` @@ -2306,6 +2316,9 @@ func (rscData *interfaceLogicalData) set( } configSet = append(configSet, setPrefix+"disable") } + if v := rscData.Encapsulation.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"encapsulation "+v) + } if rscData.FamilyInet != nil { configSet = append(configSet, setPrefix+"family inet") @@ -2811,6 +2824,8 @@ func (rscData *interfaceLogicalData) read( rscData.Description = types.StringValue(strings.Trim(itemTrim, "\"")) case itemTrim == "disable": rscData.Disable = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "encapsulation "): + rscData.Encapsulation = types.StringValue(itemTrim) case balt.CutPrefixInString(&itemTrim, "family inet6"): if rscData.FamilyInet6 == nil { rscData.FamilyInet6 = &interfaceLogicalBlockFamilyInet6{} @@ -3352,6 +3367,7 @@ func (rscData *interfaceLogicalData) delOpts( configSet := []string{ delPrefix + "description", delPrefix + "disable", + delPrefix + "encapsulation", delPrefix + "family inet", delPrefix + "family inet6", delPrefix + "tunnel", diff --git a/internal/providerfwk/testdata/TestAccResourceInterfaceLogical_basic/5/main.tf b/internal/providerfwk/testdata/TestAccResourceInterfaceLogical_basic/5/main.tf index fa6c2e23..eebcdfc1 100644 --- a/internal/providerfwk/testdata/TestAccResourceInterfaceLogical_basic/5/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceInterfaceLogical_basic/5/main.tf @@ -44,5 +44,6 @@ resource "junos_interface_logical" "testacc_interface_logical" { } } resource "junos_interface_logical" "testacc_interface_logical2" { - name = "${junos_interface_physical.testacc_interface_logical_phy.name}.101" + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.101" + encapsulation = "dix" } From 4912d26204d3d230127f5e67c823d7f219dfe961 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 20 Jun 2024 14:09:23 +0200 Subject: [PATCH 25/30] d/interface_logical: add encapsulation attribute like resource --- .changes/issue-674.md | 1 + docs/data-sources/interface_logical.md | 2 ++ internal/providerfwk/data_source_interface_logical.go | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/.changes/issue-674.md b/.changes/issue-674.md index ee15455e..7db95bc6 100644 --- a/.changes/issue-674.md +++ b/.changes/issue-674.md @@ -2,3 +2,4 @@ ENHANCEMENTS: * **resource/junos_interface_logical**: add `encapsulation` argument (Fix [#674](https://github.com/jeremmfr/terraform-provider-junos/issues/674)) +* **data-source/junos_interface_logical**: add `encapsulation` attribute like resource diff --git a/docs/data-sources/interface_logical.md b/docs/data-sources/interface_logical.md index d3e0c478..457556f2 100644 --- a/docs/data-sources/interface_logical.md +++ b/docs/data-sources/interface_logical.md @@ -44,6 +44,8 @@ The following attributes are exported: Description for interface. - **disable** (Boolean) Interface disabled. +- **encapsulation** (String) + Logical link-layer encapsulation. - **family_inet** (Block) Family inet enabled and possible configuration. - **address** (Block List) diff --git a/internal/providerfwk/data_source_interface_logical.go b/internal/providerfwk/data_source_interface_logical.go index 3e2ed9da..e6b52f5d 100644 --- a/internal/providerfwk/data_source_interface_logical.go +++ b/internal/providerfwk/data_source_interface_logical.go @@ -103,6 +103,10 @@ func (dsc *interfaceLogicalDataSource) Schema( Computed: true, Description: "Interface disabled.", }, + "encapsulation": schema.StringAttribute{ + Computed: true, + Description: "Logical link-layer encapsulation.", + }, "routing_instance": schema.StringAttribute{ Computed: true, Description: "Routing_instance where the interface is.", @@ -271,6 +275,7 @@ type interfaceLogicalDataSourceData struct { Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` Disable types.Bool `tfsdk:"disable"` + Encapsulation types.String `tfsdk:"encapsulation"` RoutingInstance types.String `tfsdk:"routing_instance"` SecurityInboundProtocols []types.String `tfsdk:"security_inbound_protocols"` SecurityInboundServices []types.String `tfsdk:"security_inbound_services"` @@ -414,6 +419,7 @@ func (dscData *interfaceLogicalDataSourceData) copyFromResourceData(rscData inte dscData.Name = rscData.Name dscData.Description = rscData.Description dscData.Disable = rscData.Disable + dscData.Encapsulation = rscData.Encapsulation dscData.FamilyInet = rscData.FamilyInet dscData.FamilyInet6 = rscData.FamilyInet6 dscData.RoutingInstance = rscData.RoutingInstance From 2e5607ec72a0202e9dd4da21b7c0cbde2dfea735 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 21 Jun 2024 14:13:26 +0200 Subject: [PATCH 26/30] r/security_log_stream: use new provider via framework --- .changes/issue-675.md | 9 + internal/providerfwk/provider.go | 1 + .../resource_security_log_stream.go | 615 ++++++++++++++++++ .../resource_security_log_stream_test.go | 34 + .../1/main.tf | 10 + .../2/main.tf | 18 + .../3/main.tf | 10 + .../1/main.tf | 10 + .../1/provider.tf | 1 + .../2/main.tf | 29 + .../2/provider.tf | 10 + .../3/main.tf | 1 + .../upgradestate_security_log_stream.go | 136 ++++ .../upgradestate_security_log_stream_test.go | 37 ++ internal/providersdk/provider.go | 1 - .../resource_security_log_stream.go | 503 -------------- .../resource_security_log_stream_test.go | 116 ---- 17 files changed, 921 insertions(+), 620 deletions(-) create mode 100644 .changes/issue-675.md create mode 100644 internal/providerfwk/resource_security_log_stream.go create mode 100644 internal/providerfwk/resource_security_log_stream_test.go create mode 100644 internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/1/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf create mode 100644 internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/main.tf create mode 120000 internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/provider.tf create mode 100644 internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/main.tf create mode 100644 internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/provider.tf create mode 120000 internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/3/main.tf create mode 100644 internal/providerfwk/upgradestate_security_log_stream.go create mode 100644 internal/providerfwk/upgradestate_security_log_stream_test.go delete mode 100644 internal/providersdk/resource_security_log_stream.go delete mode 100644 internal/providersdk/resource_security_log_stream_test.go diff --git a/.changes/issue-675.md b/.changes/issue-675.md new file mode 100644 index 00000000..51b1efc9 --- /dev/null +++ b/.changes/issue-675.md @@ -0,0 +1,9 @@ + +ENHANCEMENTS: + +* **resource/junos_security_log_stream**: + * resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* + optional string attributes doesn't accept *empty* value + the resource schema has been upgraded to have one-blocks in single mode instead of list diff --git a/internal/providerfwk/provider.go b/internal/providerfwk/provider.go index b0be3398..19a940bf 100644 --- a/internal/providerfwk/provider.go +++ b/internal/providerfwk/provider.go @@ -272,6 +272,7 @@ func (p *junosProvider) Resources(_ context.Context) []func() resource.Resource newSecurityIpsecPolicyResource, newSecurityIpsecProposalResource, newSecurityIpsecVpnResource, + newSecurityLogStreamResource, newSecurityNatDestinationResource, newSecurityNatDestinationPoolResource, newSecurityNatSourceResource, diff --git a/internal/providerfwk/resource_security_log_stream.go b/internal/providerfwk/resource_security_log_stream.go new file mode 100644 index 00000000..fb8f90cf --- /dev/null +++ b/internal/providerfwk/resource_security_log_stream.go @@ -0,0 +1,615 @@ +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/tfdiag" + "github.com/jeremmfr/terraform-provider-junos/internal/tfplanmodifier" + "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-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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 = &securityLogStream{} + _ resource.ResourceWithConfigure = &securityLogStream{} + _ resource.ResourceWithValidateConfig = &securityLogStream{} + _ resource.ResourceWithImportState = &securityLogStream{} + _ resource.ResourceWithUpgradeState = &securityLogStream{} +) + +type securityLogStream struct { + client *junos.Client +} + +func newSecurityLogStreamResource() resource.Resource { + return &securityLogStream{} +} + +func (rsc *securityLogStream) typeName() string { + return providerName + "_security_log_stream" +} + +func (rsc *securityLogStream) junosName() string { + return "security log stream" +} + +func (rsc *securityLogStream) junosClient() *junos.Client { + return rsc.client +} + +func (rsc *securityLogStream) Metadata( + _ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse, +) { + resp.TypeName = rsc.typeName() +} + +func (rsc *securityLogStream) 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 *securityLogStream) Schema( + _ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse, +) { + resp.Schema = schema.Schema{ + Version: 1, + Description: defaultResourceSchemaDescription(rsc), + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "An identifier for the resource with format ``.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "Name of security log stream.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + "category": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Selects the type of events that may be logged.", + Validators: []validator.List{ + listvalidator.SizeAtLeast(1), + listvalidator.ValueStringsAre( + stringvalidator.LengthAtLeast(1), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + ), + }, + }, + "filter_threat_attack": schema.BoolAttribute{ + Optional: true, + Description: "Threat-attack security events are logged.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "format": schema.StringAttribute{ + Optional: true, + Description: "Specify the log stream format.", + Validators: []validator.String{ + stringvalidator.OneOf("binary", "sd-syslog", "syslog", "welf"), + }, + }, + "rate_limit": schema.Int64Attribute{ + Optional: true, + Description: "Rate-limit for security logs.", + Validators: []validator.Int64{ + int64validator.Between(1, 65535), + }, + }, + "severity": schema.StringAttribute{ + Optional: true, + Description: "Severity threshold for security logs.", + Validators: []validator.String{ + stringvalidator.OneOf(junos.SyslogSeverity()...), + }, + }, + }, + Blocks: map[string]schema.Block{ + "file": schema.SingleNestedBlock{ + Description: "Configure log file options for logs in local file.", + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: false, // true when SingleNestedBlock is specified + Optional: true, + Description: "Name of local log file.", + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 250), + tfvalidator.StringDoubleQuoteExclusion(), + tfvalidator.StringSpaceExclusion(), + tfvalidator.StringRuneExclusion('/', '%'), + }, + }, + "allow_duplicates": schema.BoolAttribute{ + Optional: true, + Description: "To disable log consolidation.", + Validators: []validator.Bool{ + tfvalidator.BoolTrue(), + }, + }, + "rotation": schema.Int64Attribute{ + Optional: true, + Description: "Maximum number of rotate files.", + Validators: []validator.Int64{ + int64validator.Between(2, 19), + }, + }, + "size": schema.Int64Attribute{ + Optional: true, + Description: "Maximum size of local log file in megabytes.", + Validators: []validator.Int64{ + int64validator.Between(1, 3), + }, + }, + }, + PlanModifiers: []planmodifier.Object{ + tfplanmodifier.BlockRemoveNull(), + }, + }, + "host": schema.SingleNestedBlock{ + Description: "Configure destination to send security logs to.", + Attributes: map[string]schema.Attribute{ + "ip_address": schema.StringAttribute{ + Required: false, // true when SingleNestedBlock is specified + Optional: true, + Description: "IP address.", + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 250), + tfvalidator.StringDoubleQuoteExclusion(), + tfvalidator.StringSpaceExclusion(), + }, + }, + "port": schema.Int64Attribute{ + Optional: true, + Description: "Host port number.", + Validators: []validator.Int64{ + int64validator.Between(1, 65535), + }, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + Description: "Routing instance name.", + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 63), + tfvalidator.StringFormat(tfvalidator.DefaultFormat), + }, + }, + }, + PlanModifiers: []planmodifier.Object{ + tfplanmodifier.BlockRemoveNull(), + }, + }, + }, + } +} + +type securityLogStreamData struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Category []types.String `tfsdk:"category"` + FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` + Format types.String `tfsdk:"format"` + RateLimit types.Int64 `tfsdk:"rate_limit"` + Severity types.String `tfsdk:"severity"` + File *securityLogStreamBlockFile `tfsdk:"file"` + Host *securityLogStreamBlockHost `tfsdk:"host"` +} + +type securityLogStreamConfig struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Category types.List `tfsdk:"category"` + FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` + Format types.String `tfsdk:"format"` + RateLimit types.Int64 `tfsdk:"rate_limit"` + Severity types.String `tfsdk:"severity"` + File *securityLogStreamBlockFile `tfsdk:"file"` + Host *securityLogStreamBlockHost `tfsdk:"host"` +} + +type securityLogStreamBlockFile struct { + Name types.String `tfsdk:"name"` + AllowDuplicates types.Bool `tfsdk:"allow_duplicates"` + Rotation types.Int64 `tfsdk:"rotation"` + Size types.Int64 `tfsdk:"size"` +} + +func (block *securityLogStreamBlockFile) hasKnownValue() bool { + return tfdata.CheckBlockHasKnownValue(block) +} + +type securityLogStreamBlockHost struct { + IPAddress types.String `tfsdk:"ip_address"` + Port types.Int64 `tfsdk:"port"` + RoutingInstance types.String `tfsdk:"routing_instance"` +} + +func (block *securityLogStreamBlockHost) hasKnownValue() bool { + return tfdata.CheckBlockHasKnownValue(block) +} + +func (rsc *securityLogStream) ValidateConfig( + ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, +) { + var config securityLogStreamConfig + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + if resp.Diagnostics.HasError() { + return + } + + if !config.Category.IsNull() && !config.Category.IsUnknown() && + !config.FilterThreatAttack.IsNull() && !config.FilterThreatAttack.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("category"), + tfdiag.ConflictConfigErrSummary, + "category and filter_threat_attack cannot be configured together", + ) + } + if config.File != nil && config.File.hasKnownValue() && + config.Host != nil && config.Host.hasKnownValue() { + resp.Diagnostics.AddAttributeError( + path.Root("file"), + tfdiag.ConflictConfigErrSummary, + "file and host cannot be configured together", + ) + } + if config.File != nil && config.File.Name.IsNull() { + resp.Diagnostics.AddAttributeError( + path.Root("file").AtName("name"), + tfdiag.MissingConfigErrSummary, + "name must be specified in file block", + ) + } + if config.Host != nil && config.Host.IPAddress.IsNull() { + resp.Diagnostics.AddAttributeError( + path.Root("host").AtName("ip_address"), + tfdiag.MissingConfigErrSummary, + "ip_address must be specified in host block", + ) + } +} + +func (rsc *securityLogStream) Create( + ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse, +) { + var plan securityLogStreamData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + if plan.Name.ValueString() == "" { + resp.Diagnostics.AddAttributeError( + path.Root("name"), + "Empty Name", + defaultResourceCouldNotCreateWithEmptyMessage(rsc, "name"), + ) + + return + } + + defaultResourceCreate( + ctx, + rsc, + func(fnCtx context.Context, junSess *junos.Session) bool { + if !junSess.CheckCompatibilitySecurity() { + resp.Diagnostics.AddError( + tfdiag.CompatibilityErrSummary, + rsc.junosName()+junSess.SystemInformation.NotCompatibleMsg(), + ) + + return false + } + logStreamExists, err := checkSecurityLogStreamExists(fnCtx, plan.Name.ValueString(), junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if logStreamExists { + resp.Diagnostics.AddError( + tfdiag.DuplicateConfigErrSummary, + defaultResourceAlreadyExistsMessage(rsc, plan.Name), + ) + + return false + } + + return true + }, + func(fnCtx context.Context, junSess *junos.Session) bool { + logStreamExists, err := checkSecurityLogStreamExists(fnCtx, plan.Name.ValueString(), junSess) + if err != nil { + resp.Diagnostics.AddError(tfdiag.PreCheckErrSummary, err.Error()) + + return false + } + if !logStreamExists { + resp.Diagnostics.AddError( + tfdiag.NotFoundErrSummary, + defaultResourceDoesNotExistsAfterCommitMessage(rsc, plan.Name), + ) + + return false + } + + return true + }, + &plan, + resp, + ) +} + +func (rsc *securityLogStream) Read( + ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse, +) { + var state, data securityLogStreamData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var _ resourceDataReadFrom1String = &data + defaultResourceRead( + ctx, + rsc, + []string{ + state.Name.ValueString(), + }, + &data, + nil, + resp, + ) +} + +func (rsc *securityLogStream) Update( + ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse, +) { + var plan, state securityLogStreamData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceUpdate( + ctx, + rsc, + &state, + &plan, + resp, + ) +} + +func (rsc *securityLogStream) Delete( + ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse, +) { + var state securityLogStreamData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + defaultResourceDelete( + ctx, + rsc, + &state, + resp, + ) +} + +func (rsc *securityLogStream) ImportState( + ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, +) { + var data securityLogStreamData + + var _ resourceDataReadFrom1String = &data + defaultResourceImportState( + ctx, + rsc, + &data, + req, + resp, + defaultResourceImportDontFindIDStrMessage(rsc, req.ID, "name"), + ) +} + +func checkSecurityLogStreamExists( + _ context.Context, name string, junSess *junos.Session, +) ( + bool, error, +) { + showConfig, err := junSess.Command(junos.CmdShowConfig + + "security log stream " + name + junos.PipeDisplaySet) + if err != nil { + return false, err + } + if showConfig == junos.EmptyW { + return false, nil + } + + return true, nil +} + +func (rscData *securityLogStreamData) fillID() { + rscData.ID = types.StringValue(rscData.Name.ValueString()) +} + +func (rscData *securityLogStreamData) nullID() bool { + return rscData.ID.IsNull() +} + +func (rscData *securityLogStreamData) set( + _ context.Context, junSess *junos.Session, +) ( + path.Path, error, +) { + setPrefix := "set security log stream " + rscData.Name.ValueString() + " " + + configSet := []string{ + setPrefix, + } + + for _, v := range rscData.Category { + configSet = append(configSet, setPrefix+"category "+v.ValueString()) + } + if rscData.FilterThreatAttack.ValueBool() { + configSet = append(configSet, setPrefix+"filter threat-attack") + } + if v := rscData.Format.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"format "+v) + } + if !rscData.RateLimit.IsNull() { + configSet = append(configSet, setPrefix+"rate-limit "+ + utils.ConvI64toa(rscData.RateLimit.ValueInt64())) + } + if v := rscData.Severity.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"severity "+v) + } + if rscData.File != nil { + configSet = append(configSet, setPrefix+"file name \""+rscData.File.Name.ValueString()+"\"") + if rscData.File.AllowDuplicates.ValueBool() { + configSet = append(configSet, setPrefix+"file allow-duplicates") + } + if !rscData.File.Rotation.IsNull() { + configSet = append(configSet, setPrefix+"file rotation "+ + utils.ConvI64toa(rscData.File.Rotation.ValueInt64())) + } + if !rscData.File.Size.IsNull() { + configSet = append(configSet, setPrefix+"file size "+ + utils.ConvI64toa(rscData.File.Size.ValueInt64())) + } + } + if rscData.Host != nil { + configSet = append(configSet, setPrefix+"host \""+rscData.Host.IPAddress.ValueString()+"\"") + if !rscData.Host.Port.IsNull() { + configSet = append(configSet, setPrefix+"host port "+ + utils.ConvI64toa(rscData.Host.Port.ValueInt64())) + } + if v := rscData.Host.RoutingInstance.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"host routing-instance "+v) + } + } + + return path.Empty(), junSess.ConfigSet(configSet) +} + +func (rscData *securityLogStreamData) read( + _ context.Context, name string, junSess *junos.Session, +) error { + showConfig, err := junSess.Command(junos.CmdShowConfig + + "security log stream " + name + junos.PipeDisplaySetRelative) + if err != nil { + return err + } + if showConfig != junos.EmptyW { + rscData.Name = types.StringValue(name) + 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) + switch { + case balt.CutPrefixInString(&itemTrim, "category "): + rscData.Category = append(rscData.Category, types.StringValue(itemTrim)) + case itemTrim == "filter threat-attack": + rscData.FilterThreatAttack = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "format "): + rscData.Format = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "rate-limit "): + rscData.RateLimit, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "severity "): + rscData.Severity = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, "file "): + if rscData.File == nil { + rscData.File = &securityLogStreamBlockFile{} + } + switch { + case balt.CutPrefixInString(&itemTrim, "name "): + rscData.File.Name = types.StringValue(strings.Trim(itemTrim, "\"")) + case itemTrim == "allow-duplicates": + rscData.File.AllowDuplicates = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "rotation "): + rscData.File.Rotation, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "size "): + rscData.File.Size, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + } + case balt.CutPrefixInString(&itemTrim, "host "): + if rscData.Host == nil { + rscData.Host = &securityLogStreamBlockHost{} + } + switch { + case balt.CutPrefixInString(&itemTrim, "port "): + rscData.Host.Port, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, "routing-instance "): + rscData.Host.RoutingInstance = types.StringValue(itemTrim) + default: + rscData.Host.IPAddress = types.StringValue(strings.Trim(itemTrim, "\"")) + } + } + } + } + + return nil +} + +func (rscData *securityLogStreamData) del( + _ context.Context, junSess *junos.Session, +) error { + configSet := []string{ + "delete security log stream " + rscData.Name.ValueString(), + } + + return junSess.ConfigSet(configSet) +} diff --git a/internal/providerfwk/resource_security_log_stream_test.go b/internal/providerfwk/resource_security_log_stream_test.go new file mode 100644 index 00000000..1430738f --- /dev/null +++ b/internal/providerfwk/resource_security_log_stream_test.go @@ -0,0 +1,34 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccResourceSecurityLogStream_basic(t *testing.T) { + if os.Getenv("TESTACC_SRX") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ResourceName: "junos_security_log_stream.testacc_logstream", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/1/main.tf new file mode 100644 index 00000000..40a675a3 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/1/main.tf @@ -0,0 +1,10 @@ +import { + to = junos_security.security + id = "security" +} + +resource "junos_security" "security" { + log { + source_address = "192.0.2.2" + } +} diff --git a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf new file mode 100644 index 00000000..9d9000b2 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf @@ -0,0 +1,18 @@ +resource "junos_routing_instance" "testacc_logstream" { + lifecycle { + create_before_destroy = true + } + name = "testacclogstream" +} +resource "junos_security_log_stream" "testacc_logstream" { + name = "testacc_logstream" + category = ["idp"] + format = "syslog" + host { + ip_address = "AHostN@me" + port = 514 + routing_instance = junos_routing_instance.testacc_logstream.name + } + rate_limit = 50 + severity = "error" +} diff --git a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf new file mode 100644 index 00000000..e1b71ba2 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf @@ -0,0 +1,10 @@ +resource "junos_security_log_stream" "testacc_logstream" { + name = "testacc_logstream" + file { + name = "#File@test.txt" + allow_duplicates = true + size = 3 + rotation = 3 + } + filter_threat_attack = true +} diff --git a/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/main.tf b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/main.tf new file mode 100644 index 00000000..40a675a3 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/main.tf @@ -0,0 +1,10 @@ +import { + to = junos_security.security + id = "security" +} + +resource "junos_security" "security" { + log { + source_address = "192.0.2.2" + } +} diff --git a/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/provider.tf b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/provider.tf new file mode 120000 index 00000000..472a1114 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/1/provider.tf @@ -0,0 +1 @@ +../2/provider.tf \ No newline at end of file diff --git a/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/main.tf b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/main.tf new file mode 100644 index 00000000..e575dcf9 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/main.tf @@ -0,0 +1,29 @@ +resource "junos_routing_instance" "testacc_logstream" { + lifecycle { + create_before_destroy = true + } + name = "testacclogstream" +} + +resource "junos_security_log_stream" "testacc_logstream" { + name = "testacc_logstream" + category = ["idp"] + format = "syslog" + host { + ip_address = "192.0.2.1" + port = 514 + routing_instance = junos_routing_instance.testacc_logstream.name + } + rate_limit = 50 + severity = "error" +} +resource "junos_security_log_stream" "testacc_logstream2" { + name = "testacc_logstream2" + file { + name = "test" + allow_duplicates = true + size = 3 + rotation = 3 + } + filter_threat_attack = true +} diff --git a/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/provider.tf b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/provider.tf new file mode 100644 index 00000000..713fa309 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/2/provider.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + junos = { + source = "registry.terraform.io/jeremmfr/junos" + version = "1.33.0" + } + } +} + +provider "junos" {} diff --git a/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/3/main.tf b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/3/main.tf new file mode 120000 index 00000000..19701121 --- /dev/null +++ b/internal/providerfwk/testdata/TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic/3/main.tf @@ -0,0 +1 @@ +../2/main.tf \ No newline at end of file diff --git a/internal/providerfwk/upgradestate_security_log_stream.go b/internal/providerfwk/upgradestate_security_log_stream.go new file mode 100644 index 00000000..c34c5579 --- /dev/null +++ b/internal/providerfwk/upgradestate_security_log_stream.go @@ -0,0 +1,136 @@ +package providerfwk + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func (rsc *securityLogStream) UpgradeState(_ context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "name": schema.StringAttribute{ + Required: true, + }, + "category": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + }, + "filter_threat_attack": schema.BoolAttribute{ + Optional: true, + }, + "format": schema.StringAttribute{ + Optional: true, + }, + "rate_limit": schema.Int64Attribute{ + Optional: true, + }, + "severity": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "file": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Required: true, + }, + "allow_duplicates": schema.BoolAttribute{ + Optional: true, + }, + "rotation": schema.Int64Attribute{ + Optional: true, + }, + "size": schema.Int64Attribute{ + Optional: true, + }, + }, + }, + }, + "host": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "ip_address": schema.StringAttribute{ + Required: true, + }, + "port": schema.Int64Attribute{ + Optional: true, + }, + "routing_instance": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + }, + StateUpgrader: upgradeSecurityLogStreamV0toV1, + }, + } +} + +func upgradeSecurityLogStreamV0toV1( + ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse, +) { + type modelV0 struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Category []types.String `tfsdk:"category"` + FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` + Format types.String `tfsdk:"format"` + RateLimit types.Int64 `tfsdk:"rate_limit"` + Severity types.String `tfsdk:"severity"` + File []struct { + Name types.String `tfsdk:"name"` + AllowDuplicates types.Bool `tfsdk:"allow_duplicates"` + Rotation types.Int64 `tfsdk:"rotation"` + Size types.Int64 `tfsdk:"size"` + } `tfsdk:"file"` + Host []struct { + IPAddress types.String `tfsdk:"ip_address"` + Port types.Int64 `tfsdk:"port"` + RoutingInstance types.String `tfsdk:"routing_instance"` + } `tfsdk:"host"` + } + + var dataV0 modelV0 + resp.Diagnostics.Append(req.State.Get(ctx, &dataV0)...) + if resp.Diagnostics.HasError() { + return + } + + var dataV1 securityLogStreamData + dataV1.ID = dataV0.ID + dataV1.Name = dataV0.Name + dataV1.Category = dataV0.Category + dataV1.FilterThreatAttack = dataV0.FilterThreatAttack + dataV1.Format = dataV0.Format + dataV1.RateLimit = dataV0.RateLimit + dataV1.Severity = dataV0.Severity + if len(dataV0.File) > 0 { + dataV1.File = &securityLogStreamBlockFile{ + Name: dataV0.File[0].Name, + AllowDuplicates: dataV0.File[0].AllowDuplicates, + Rotation: dataV0.File[0].Rotation, + Size: dataV0.File[0].Size, + } + } + + if len(dataV0.Host) > 0 { + dataV1.Host = &securityLogStreamBlockHost{ + IPAddress: dataV0.Host[0].IPAddress, + Port: dataV0.Host[0].Port, + RoutingInstance: dataV0.Host[0].RoutingInstance, + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, dataV1)...) +} diff --git a/internal/providerfwk/upgradestate_security_log_stream_test.go b/internal/providerfwk/upgradestate_security_log_stream_test.go new file mode 100644 index 00000000..714541d4 --- /dev/null +++ b/internal/providerfwk/upgradestate_security_log_stream_test.go @@ -0,0 +1,37 @@ +package providerfwk_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func TestAccUpgradeStateResourceSecurityLogStream_V0toV1_basic(t *testing.T) { + if os.Getenv("TESTACC_UPGRADE_STATE") == "" { + return + } + if os.Getenv("TESTACC_SRX") != "" { + resource.Test(t, resource.TestCase{ + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ConfigDirectory: config.TestStepDirectory(), + }, + { + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + ConfigDirectory: config.TestStepDirectory(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) + } +} diff --git a/internal/providersdk/provider.go b/internal/providersdk/provider.go index bc931ca5..a10a2639 100644 --- a/internal/providersdk/provider.go +++ b/internal/providersdk/provider.go @@ -199,7 +199,6 @@ func Provider() *schema.Provider { "junos_security_idp_custom_attack": resourceSecurityIdpCustomAttack(), "junos_security_idp_custom_attack_group": resourceSecurityIdpCustomAttackGroup(), "junos_security_idp_policy": resourceSecurityIdpPolicy(), - "junos_security_log_stream": resourceSecurityLogStream(), "junos_security_screen": resourceSecurityScreen(), "junos_security_screen_whitelist": resourceSecurityScreenWhiteList(), "junos_security_utm_custom_url_category": resourceSecurityUtmCustomURLCategory(), diff --git a/internal/providersdk/resource_security_log_stream.go b/internal/providersdk/resource_security_log_stream.go deleted file mode 100644 index a0fa87f4..00000000 --- a/internal/providersdk/resource_security_log_stream.go +++ /dev/null @@ -1,503 +0,0 @@ -package providersdk - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/jeremmfr/terraform-provider-junos/internal/junos" - - "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" - balt "github.com/jeremmfr/go-utils/basicalter" -) - -type securityLogStreamOptions struct { - filterThreatAttack bool - rateLimit int - name string - format string - severity string - category []string - file []map[string]interface{} - host []map[string]interface{} -} - -func resourceSecurityLogStream() *schema.Resource { - return &schema.Resource{ - CreateWithoutTimeout: resourceSecurityLogStreamCreate, - ReadWithoutTimeout: resourceSecurityLogStreamRead, - UpdateWithoutTimeout: resourceSecurityLogStreamUpdate, - DeleteWithoutTimeout: resourceSecurityLogStreamDelete, - Importer: &schema.ResourceImporter{ - StateContext: resourceSecurityLogStreamImport, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - ForceNew: true, - Required: true, - }, - "category": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - Elem: &schema.Schema{Type: schema.TypeString}, - ConflictsWith: []string{"filter_threat_attack"}, - }, - "file": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - ConflictsWith: []string{"host"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "allow_duplicates": { - Type: schema.TypeBool, - Optional: true, - }, - "rotation": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(2, 19), - }, - "size": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 3), - }, - }, - }, - }, - "filter_threat_attack": { - Type: schema.TypeBool, - Optional: true, - ConflictsWith: []string{"category"}, - }, - "format": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - "binary", "sd-syslog", "syslog", "welf", - }, false), - }, - "host": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - ConflictsWith: []string{"file"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "ip_address": { - Type: schema.TypeString, - Required: true, - }, - "port": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 65535), - }, - "routing_instance": { - Type: schema.TypeString, - Optional: true, - ValidateDiagFunc: validateNameObjectJunos([]string{"default"}, 64, formatDefault), - }, - }, - }, - }, - "rate_limit": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(1, 65535), - }, - "severity": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(junos.SyslogSeverity(), false), - }, - }, - } -} - -func resourceSecurityLogStreamCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeCreateSetFile() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := setSecurityLogStream(d, junSess); err != nil { - return diag.FromErr(err) - } - d.SetId(d.Get("name").(string)) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if !junSess.CheckCompatibilitySecurity() { - return diag.FromErr(fmt.Errorf("security log stream "+ - "not compatible with Junos device %s", junSess.SystemInformation.HardwareModel)) - } - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - securityLogStreamExists, err := checkSecurityLogStreamExists(d.Get("name").(string), junSess) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if securityLogStreamExists { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(fmt.Errorf("security log stream %v already exists", d.Get("name").(string)))...) - } - - if err := setSecurityLogStream(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "create resource junos_security_log_stream") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - securityLogStreamExists, err = checkSecurityLogStreamExists(d.Get("name").(string), junSess) - if err != nil { - return append(diagWarns, diag.FromErr(err)...) - } - if securityLogStreamExists { - d.SetId(d.Get("name").(string)) - } else { - return append(diagWarns, diag.FromErr(fmt.Errorf("security log stream %v "+ - "not exists after commit => check your config", d.Get("name").(string)))...) - } - - return append(diagWarns, resourceSecurityLogStreamReadWJunSess(d, junSess)...) -} - -func resourceSecurityLogStreamRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - - return resourceSecurityLogStreamReadWJunSess(d, junSess) -} - -func resourceSecurityLogStreamReadWJunSess(d *schema.ResourceData, junSess *junos.Session, -) diag.Diagnostics { - junos.MutexLock() - securityLogStreamOptions, err := readSecurityLogStream(d.Get("name").(string), junSess) - junos.MutexUnlock() - if err != nil { - return diag.FromErr(err) - } - if securityLogStreamOptions.name == "" { - d.SetId("") - } else { - fillSecurityLogStreamData(d, securityLogStreamOptions) - } - - return nil -} - -func resourceSecurityLogStreamUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - d.Partial(true) - clt := m.(*junos.Client) - if clt.FakeUpdateAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delLogStream(d.Get("name").(string), junSess); err != nil { - return diag.FromErr(err) - } - if err := setSecurityLogStream(d, junSess); err != nil { - return diag.FromErr(err) - } - d.Partial(false) - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delLogStream(d.Get("name").(string), junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - if err := setSecurityLogStream(d, junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "update resource junos_security_log_stream") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - d.Partial(false) - - return append(diagWarns, resourceSecurityLogStreamReadWJunSess(d, junSess)...) -} - -func resourceSecurityLogStreamDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - clt := m.(*junos.Client) - if clt.FakeDeleteAlso() { - junSess := clt.NewSessionWithoutNetconf(ctx) - if err := delLogStream(d.Get("name").(string), junSess); err != nil { - return diag.FromErr(err) - } - - return nil - } - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return diag.FromErr(err) - } - defer junSess.Close() - if err := junSess.ConfigLock(ctx); err != nil { - return diag.FromErr(err) - } - var diagWarns diag.Diagnostics - if err := delLogStream(d.Get("name").(string), junSess); err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - warns, err := junSess.CommitConf(ctx, "delete resource junos_security_log_stream") - appendDiagWarns(&diagWarns, warns) - if err != nil { - appendDiagWarns(&diagWarns, junSess.ConfigClear()) - - return append(diagWarns, diag.FromErr(err)...) - } - - return diagWarns -} - -func resourceSecurityLogStreamImport(ctx context.Context, d *schema.ResourceData, m interface{}, -) ([]*schema.ResourceData, error) { - clt := m.(*junos.Client) - junSess, err := clt.StartNewSession(ctx) - if err != nil { - return nil, err - } - defer junSess.Close() - result := make([]*schema.ResourceData, 1) - securityLogStreamExists, err := checkSecurityLogStreamExists(d.Id(), junSess) - if err != nil { - return nil, err - } - if !securityLogStreamExists { - return nil, fmt.Errorf("don't find security log stream with id '%v' (id must be )", d.Id()) - } - securityLogStreamOptions, err := readSecurityLogStream(d.Id(), junSess) - if err != nil { - return nil, err - } - fillSecurityLogStreamData(d, securityLogStreamOptions) - - result[0] = d - - return result, nil -} - -func checkSecurityLogStreamExists(securityLogStream string, junSess *junos.Session) (bool, error) { - showConfig, err := junSess.Command(junos.CmdShowConfig + - "security log stream \"" + securityLogStream + "\"" + junos.PipeDisplaySet) - if err != nil { - return false, err - } - if showConfig == junos.EmptyW { - return false, nil - } - - return true, nil -} - -func setSecurityLogStream(d *schema.ResourceData, junSess *junos.Session) error { - configSet := make([]string, 0) - - setPrefix := "set security log stream \"" + d.Get("name").(string) + "\" " - for _, v := range d.Get("category").([]interface{}) { - configSet = append(configSet, setPrefix+"category "+v.(string)) - } - for _, v := range d.Get("file").([]interface{}) { - file := v.(map[string]interface{}) - configSet = append(configSet, setPrefix+"file name "+file["name"].(string)) - if file["allow_duplicates"].(bool) { - configSet = append(configSet, setPrefix+"file allow-duplicates") - } - if file["rotation"].(int) != 0 { - configSet = append(configSet, setPrefix+"file rotation "+strconv.Itoa(file["rotation"].(int))) - } - if file["size"].(int) != 0 { - configSet = append(configSet, setPrefix+"file size "+strconv.Itoa(file["size"].(int))) - } - } - if d.Get("filter_threat_attack").(bool) { - configSet = append(configSet, setPrefix+"filter threat-attack") - } - if d.Get("format").(string) != "" { - configSet = append(configSet, setPrefix+"format "+d.Get("format").(string)) - } - for _, v := range d.Get("host").([]interface{}) { - host := v.(map[string]interface{}) - configSet = append(configSet, setPrefix+"host "+host["ip_address"].(string)) - if host["port"].(int) != 0 { - configSet = append(configSet, setPrefix+"host port "+strconv.Itoa( - host["port"].(int))) - } - if host["routing_instance"].(string) != "" { - configSet = append(configSet, setPrefix+"host routing-instance "+ - host["routing_instance"].(string)) - } - } - if d.Get("rate_limit").(int) != 0 { - configSet = append(configSet, setPrefix+"rate-limit "+ - strconv.Itoa(d.Get("rate_limit").(int))) - } - if d.Get("severity").(string) != "" { - configSet = append(configSet, setPrefix+"severity "+ - d.Get("severity").(string)) - } - - return junSess.ConfigSet(configSet) -} - -func readSecurityLogStream(securityLogStream string, junSess *junos.Session, -) (confRead securityLogStreamOptions, err error) { - showConfig, err := junSess.Command(junos.CmdShowConfig + - "security log stream \"" + securityLogStream + "\"" + junos.PipeDisplaySetRelative) - if err != nil { - return confRead, err - } - if showConfig != junos.EmptyW { - confRead.name = securityLogStream - 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) - switch { - case balt.CutPrefixInString(&itemTrim, "category "): - confRead.category = append(confRead.category, itemTrim) - case balt.CutPrefixInString(&itemTrim, "file "): - if len(confRead.file) == 0 { - confRead.file = append(confRead.file, map[string]interface{}{ - "name": "", - "allow_duplicates": false, - "rotation": 0, - "size": 0, - }) - } - switch { - case balt.CutPrefixInString(&itemTrim, "name "): - confRead.file[0]["name"] = itemTrim - case itemTrim == "allow-duplicates": - confRead.file[0]["allow_duplicates"] = true - case balt.CutPrefixInString(&itemTrim, "rotation "): - confRead.file[0]["rotation"], err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "size "): - confRead.file[0]["size"], err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - } - case itemTrim == "filter threat-attack": - confRead.filterThreatAttack = true - case balt.CutPrefixInString(&itemTrim, "format "): - confRead.format = itemTrim - case balt.CutPrefixInString(&itemTrim, "host "): - if len(confRead.host) == 0 { - confRead.host = append(confRead.host, map[string]interface{}{ - "ip_address": "", - "port": 0, - "routing_instance": "", - }) - } - switch { - case balt.CutPrefixInString(&itemTrim, "port "): - confRead.host[0]["port"], err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "routing-instance "): - confRead.host[0]["routing_instance"] = itemTrim - default: - confRead.host[0]["ip_address"] = itemTrim - } - case balt.CutPrefixInString(&itemTrim, "rate-limit "): - confRead.rateLimit, err = strconv.Atoi(itemTrim) - if err != nil { - return confRead, fmt.Errorf(failedConvAtoiError, itemTrim, err) - } - case balt.CutPrefixInString(&itemTrim, "severity "): - confRead.severity = itemTrim - } - } - } - - return confRead, nil -} - -func delLogStream(securityLogStream string, junSess *junos.Session) error { - configSet := make([]string, 0, 1) - configSet = append(configSet, "delete security log stream \""+securityLogStream+"\"") - - return junSess.ConfigSet(configSet) -} - -func fillSecurityLogStreamData(d *schema.ResourceData, securityLogStreamOptions securityLogStreamOptions) { - if tfErr := d.Set("name", securityLogStreamOptions.name); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("category", securityLogStreamOptions.category); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("file", securityLogStreamOptions.file); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("filter_threat_attack", securityLogStreamOptions.filterThreatAttack); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("format", securityLogStreamOptions.format); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("host", securityLogStreamOptions.host); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("rate_limit", securityLogStreamOptions.rateLimit); tfErr != nil { - panic(tfErr) - } - if tfErr := d.Set("severity", securityLogStreamOptions.severity); tfErr != nil { - panic(tfErr) - } -} diff --git a/internal/providersdk/resource_security_log_stream_test.go b/internal/providersdk/resource_security_log_stream_test.go deleted file mode 100644 index 463da0db..00000000 --- a/internal/providersdk/resource_security_log_stream_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package providersdk_test - -import ( - "os" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccResourceSecurityLogStream_basic(t *testing.T) { - if os.Getenv("TESTACC_SRX") != "" { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: testAccResourceSecurityLogStreamConfigPreCreate(), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccResourceSecurityLogStreamConfigCreate(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "category.#", "1"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "category.0", "idp"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "format", "syslog"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "host.#", "1"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "host.0.ip_address", "192.0.2.1"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "host.0.port", "514"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "host.0.routing_instance", "testacclogstream"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "rate_limit", "50"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "severity", "error"), - ), - }, - { - Config: testAccResourceSecurityLogStreamConfigUpdate(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "file.#", "1"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "file.0.name", "test"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "file.0.allow_duplicates", "true"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "file.0.size", "3"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "file.0.rotation", "3"), - resource.TestCheckResourceAttr("junos_security_log_stream.testacc_logstream", - "filter_threat_attack", "true"), - ), - }, - { - ResourceName: "junos_security_log_stream.testacc_logstream", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) - } -} - -func testAccResourceSecurityLogStreamConfigPreCreate() string { - return ` -resource "junos_security" "security" { - log { - source_address = "192.0.2.2" - } -} -` -} - -func testAccResourceSecurityLogStreamConfigCreate() string { - return ` -resource "junos_routing_instance" "testacc_logstream" { - lifecycle { - create_before_destroy = true - } - name = "testacclogstream" -} -resource "junos_security_log_stream" "testacc_logstream" { - name = "testacc_logstream" - category = ["idp"] - format = "syslog" - host { - ip_address = "192.0.2.1" - port = 514 - routing_instance = junos_routing_instance.testacc_logstream.name - } - rate_limit = 50 - severity = "error" -} -` -} - -func testAccResourceSecurityLogStreamConfigUpdate() string { - return ` -resource "junos_security_log_stream" "testacc_logstream" { - name = "testacc_logstream" - file { - name = "test" - allow_duplicates = true - size = 3 - rotation = 3 - } - filter_threat_attack = true -} -` -} From 082c78fe041838e5d72958694ec890cad33e09eb Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 21 Jun 2024 14:16:14 +0200 Subject: [PATCH 27/30] r/security: tests: add import of junos_system in first step to avoid undesired errors in special cases --- internal/providerfwk/resource_security_test.go | 3 +-- .../testdata/TestAccResourceSecurity_basic/1/main.tf | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/providerfwk/resource_security_test.go b/internal/providerfwk/resource_security_test.go index 303f65b2..d32e89d1 100644 --- a/internal/providerfwk/resource_security_test.go +++ b/internal/providerfwk/resource_security_test.go @@ -22,8 +22,7 @@ func TestAccResourceSecurity_basic(t *testing.T) { ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, Steps: []resource.TestStep{ { - ConfigDirectory: config.TestStepDirectory(), - ExpectNonEmptyPlan: true, + ConfigDirectory: config.TestStepDirectory(), }, { ConfigDirectory: config.TestStepDirectory(), diff --git a/internal/providerfwk/testdata/TestAccResourceSecurity_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurity_basic/1/main.tf index 66ad5141..412bd264 100644 --- a/internal/providerfwk/testdata/TestAccResourceSecurity_basic/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceSecurity_basic/1/main.tf @@ -1,3 +1,8 @@ +import { + to = junos_system.system + id = "system" +} + resource "junos_system" "system" { tracing_dest_override_syslog_host = "192.0.2.13" } From 57cf6f23974610eb375547ca2ff3f8bcb2597b70 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 21 Jun 2024 15:52:35 +0200 Subject: [PATCH 28/30] r/security_log_stream: add transport block argument Fix #675 --- .changes/issue-675.md | 1 + docs/resources/security_log_stream.md | 9 ++ .../resource_security_log_stream.go | 119 +++++++++++++++--- .../2/main.tf | 24 ++++ .../3/main.tf | 4 + 5 files changed, 139 insertions(+), 18 deletions(-) diff --git a/.changes/issue-675.md b/.changes/issue-675.md index 51b1efc9..b3b1d03f 100644 --- a/.changes/issue-675.md +++ b/.changes/issue-675.md @@ -7,3 +7,4 @@ ENHANCEMENTS: optional boolean attributes doesn't accept value *false* optional string attributes doesn't accept *empty* value the resource schema has been upgraded to have one-blocks in single mode instead of list + * add `transport` block argument (Fix [#675](https://github.com/jeremmfr/terraform-provider-junos/issues/675)) diff --git a/docs/resources/security_log_stream.md b/docs/resources/security_log_stream.md index 0e0cfaa9..1835aa57 100644 --- a/docs/resources/security_log_stream.md +++ b/docs/resources/security_log_stream.md @@ -61,6 +61,15 @@ The following arguments are supported: Rate-limit for security logs. - **severity** (Optional, String) Severity threshold for security logs. +- **transport** (Optional, Block) + Set security log transport settings. + - **protocol** (Optional, String) + Set security log transport protocol for the device. + Need to be `tcp`, `tls` or `udp`. + - **tcp_connections** (Optional, Number) + Set tcp connection number per-stream (1..5). + - **tls_profile** (Optional, String) + TLS profile. ## Attribute Reference diff --git a/internal/providerfwk/resource_security_log_stream.go b/internal/providerfwk/resource_security_log_stream.go index fb8f90cf..8acdb527 100644 --- a/internal/providerfwk/resource_security_log_stream.go +++ b/internal/providerfwk/resource_security_log_stream.go @@ -2,6 +2,7 @@ package providerfwk import ( "context" + "errors" "strings" "github.com/jeremmfr/terraform-provider-junos/internal/junos" @@ -215,32 +216,64 @@ func (rsc *securityLogStream) Schema( tfplanmodifier.BlockRemoveNull(), }, }, + "transport": schema.SingleNestedBlock{ + Description: "Set security log transport settings.", + Attributes: map[string]schema.Attribute{ + "protocol": schema.StringAttribute{ + Optional: true, + Description: "Set security log transport protocol for the device.", + Validators: []validator.String{ + stringvalidator.OneOf("tcp", "tls", "udp"), + }, + }, + "tcp_connections": schema.Int64Attribute{ + Optional: true, + Description: "Set tcp connection number per-stream.", + Validators: []validator.Int64{ + int64validator.Between(1, 5), + }, + }, + "tls_profile": schema.StringAttribute{ + Optional: true, + Description: "TLS profile.", + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + tfvalidator.StringDoubleQuoteExclusion(), + }, + }, + }, + PlanModifiers: []planmodifier.Object{ + tfplanmodifier.BlockRemoveNull(), + }, + }, }, } } type securityLogStreamData struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Category []types.String `tfsdk:"category"` - FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` - Format types.String `tfsdk:"format"` - RateLimit types.Int64 `tfsdk:"rate_limit"` - Severity types.String `tfsdk:"severity"` - File *securityLogStreamBlockFile `tfsdk:"file"` - Host *securityLogStreamBlockHost `tfsdk:"host"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Category []types.String `tfsdk:"category"` + FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` + Format types.String `tfsdk:"format"` + RateLimit types.Int64 `tfsdk:"rate_limit"` + Severity types.String `tfsdk:"severity"` + File *securityLogStreamBlockFile `tfsdk:"file"` + Host *securityLogStreamBlockHost `tfsdk:"host"` + Transport *securityLogStreamBlockTransport `tfsdk:"transport"` } type securityLogStreamConfig struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Category types.List `tfsdk:"category"` - FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` - Format types.String `tfsdk:"format"` - RateLimit types.Int64 `tfsdk:"rate_limit"` - Severity types.String `tfsdk:"severity"` - File *securityLogStreamBlockFile `tfsdk:"file"` - Host *securityLogStreamBlockHost `tfsdk:"host"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Category types.List `tfsdk:"category"` + FilterThreatAttack types.Bool `tfsdk:"filter_threat_attack"` + Format types.String `tfsdk:"format"` + RateLimit types.Int64 `tfsdk:"rate_limit"` + Severity types.String `tfsdk:"severity"` + File *securityLogStreamBlockFile `tfsdk:"file"` + Host *securityLogStreamBlockHost `tfsdk:"host"` + Transport *securityLogStreamBlockTransport `tfsdk:"transport"` } type securityLogStreamBlockFile struct { @@ -264,6 +297,12 @@ func (block *securityLogStreamBlockHost) hasKnownValue() bool { return tfdata.CheckBlockHasKnownValue(block) } +type securityLogStreamBlockTransport struct { + Protocol types.String `tfsdk:"protocol"` + TCPConnections types.Int64 `tfsdk:"tcp_connections"` + TLSProfile types.String `tfsdk:"tls_profile"` +} + func (rsc *securityLogStream) ValidateConfig( ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse, ) { @@ -303,6 +342,17 @@ func (rsc *securityLogStream) ValidateConfig( "ip_address must be specified in host block", ) } + if config.Transport != nil && + !config.Transport.Protocol.IsNull() && !config.Transport.Protocol.IsUnknown() && + config.Transport.Protocol.ValueString() != "tls" && + !config.Transport.TLSProfile.IsNull() && !config.Transport.TLSProfile.IsUnknown() { + resp.Diagnostics.AddAttributeError( + path.Root("transport").AtName("protocol"), + tfdiag.ConflictConfigErrSummary, + "protocol must be 'tls' with tls_profile"+ + " in transport block", + ) + } } func (rsc *securityLogStream) Create( @@ -525,6 +575,24 @@ func (rscData *securityLogStreamData) set( configSet = append(configSet, setPrefix+"host routing-instance "+v) } } + if rscData.Transport != nil { + configSet = append(configSet, setPrefix+"transport") + if v := rscData.Transport.Protocol.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"transport protocol "+v) + if v != "tls" && rscData.Transport.TLSProfile.ValueString() != "" { + return path.Root("transport").AtName("protocol"), + errors.New("protocol must be 'tls' with tls_profile" + + " in transport block") + } + } + if !rscData.Transport.TCPConnections.IsNull() { + configSet = append(configSet, setPrefix+"transport tcp-connections "+ + utils.ConvI64toa(rscData.Transport.TCPConnections.ValueInt64())) + } + if v := rscData.Transport.TLSProfile.ValueString(); v != "" { + configSet = append(configSet, setPrefix+"transport tls-profile \""+v+"\"") + } + } return path.Empty(), junSess.ConfigSet(configSet) } @@ -597,6 +665,21 @@ func (rscData *securityLogStreamData) read( default: rscData.Host.IPAddress = types.StringValue(strings.Trim(itemTrim, "\"")) } + case balt.CutPrefixInString(&itemTrim, "transport"): + if rscData.Transport == nil { + rscData.Transport = &securityLogStreamBlockTransport{} + } + switch { + case balt.CutPrefixInString(&itemTrim, " protocol "): + rscData.Transport.Protocol = types.StringValue(itemTrim) + case balt.CutPrefixInString(&itemTrim, " tcp-connections "): + rscData.Transport.TCPConnections, err = tfdata.ConvAtoi64Value(itemTrim) + if err != nil { + return err + } + case balt.CutPrefixInString(&itemTrim, " tls-profile "): + rscData.Transport.TLSProfile = types.StringValue(strings.Trim(itemTrim, "\"")) + } } } } diff --git a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf index 9d9000b2..6f0a9bd5 100644 --- a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/2/main.tf @@ -4,6 +4,13 @@ resource "junos_routing_instance" "testacc_logstream" { } name = "testacclogstream" } +resource "junos_services_ssl_initiation_profile" "testacc_logstream" { + lifecycle { + create_before_destroy = true + } + name = "test@cc logstream" +} + resource "junos_security_log_stream" "testacc_logstream" { name = "testacc_logstream" category = ["idp"] @@ -15,4 +22,21 @@ resource "junos_security_log_stream" "testacc_logstream" { } rate_limit = 50 severity = "error" + transport { + protocol = "tls" + tcp_connections = 3 + tls_profile = junos_services_ssl_initiation_profile.testacc_logstream.name + } +} + + +resource "junos_security_log_stream" "testacc_logstream2" { + name = "testacc_logstream2" + format = "syslog" + host { + ip_address = "192.0.2.1" + } + transport { + protocol = "udp" + } } diff --git a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf index e1b71ba2..2c6be6b4 100644 --- a/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceSecurityLogStream_basic/3/main.tf @@ -8,3 +8,7 @@ resource "junos_security_log_stream" "testacc_logstream" { } filter_threat_attack = true } +resource "junos_security_log_stream" "testacc_logstream2" { + name = "testacc_logstream2" + transport {} +} From c746554417ebfc46024732795ca52d63ceaa9185 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 27 Jun 2024 08:55:37 +0200 Subject: [PATCH 29/30] tests: bump golangci-lint to v1.59.1 --- .github/workflows/linters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index c1cd7468..e644517a 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -19,7 +19,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.59.0 + version: v1.59.1 args: -c .golangci.yml -v markdown-lint: From 190e6edf81865ecf5d8e9471e17582ffa0a901b3 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Thu, 27 Jun 2024 09:04:46 +0200 Subject: [PATCH 30/30] Release v2.8.0 --- .changes/issue-672-673.md | 8 -------- .changes/issue-674.md | 5 ----- .changes/issue-675.md | 10 ---------- .changes/rstp-with-fwk.md | 9 --------- .changes/vstp-with-fwk.md | 15 --------------- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 37 insertions(+), 47 deletions(-) delete mode 100644 .changes/issue-672-673.md delete mode 100644 .changes/issue-674.md delete mode 100644 .changes/issue-675.md delete mode 100644 .changes/rstp-with-fwk.md delete mode 100644 .changes/vstp-with-fwk.md diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md deleted file mode 100644 index afcac119..00000000 --- a/.changes/issue-672-673.md +++ /dev/null @@ -1,8 +0,0 @@ - -ENHANCEMENTS: - -* **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) -* **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) -* **data-source/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` attributes like resource -* **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments -* **resource/junos_vlan**: add `static_remote_vtep_list` argument inside `vxlan` block argument diff --git a/.changes/issue-674.md b/.changes/issue-674.md deleted file mode 100644 index 7db95bc6..00000000 --- a/.changes/issue-674.md +++ /dev/null @@ -1,5 +0,0 @@ - -ENHANCEMENTS: - -* **resource/junos_interface_logical**: add `encapsulation` argument (Fix [#674](https://github.com/jeremmfr/terraform-provider-junos/issues/674)) -* **data-source/junos_interface_logical**: add `encapsulation` attribute like resource diff --git a/.changes/issue-675.md b/.changes/issue-675.md deleted file mode 100644 index b3b1d03f..00000000 --- a/.changes/issue-675.md +++ /dev/null @@ -1,10 +0,0 @@ - -ENHANCEMENTS: - -* **resource/junos_security_log_stream**: - * resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* - optional string attributes doesn't accept *empty* value - the resource schema has been upgraded to have one-blocks in single mode instead of list - * add `transport` block argument (Fix [#675](https://github.com/jeremmfr/terraform-provider-junos/issues/675)) diff --git a/.changes/rstp-with-fwk.md b/.changes/rstp-with-fwk.md deleted file mode 100644 index f20483e1..00000000 --- a/.changes/rstp-with-fwk.md +++ /dev/null @@ -1,9 +0,0 @@ - -ENHANCEMENTS: - -* **resource/junos_rstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* -* **resource/junos_rstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* diff --git a/.changes/vstp-with-fwk.md b/.changes/vstp-with-fwk.md deleted file mode 100644 index 952c50b1..00000000 --- a/.changes/vstp-with-fwk.md +++ /dev/null @@ -1,15 +0,0 @@ - -ENHANCEMENTS: - -* **resource/junos_vstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* -* **resource/junos_vstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* -* **resource/junos_vstp_vlan**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* -* **resource/junos_vstp_vlan_group**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) - some of config errors are now sent during Plan instead of during Apply - optional boolean attributes doesn't accept value *false* diff --git a/CHANGELOG.md b/CHANGELOG.md index 22a38b9c..92f82e5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,43 @@ # changelog +## v2.8.0 (2024-06-27) + +ENHANCEMENTS: + +* **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) +* **resource/junos_interface_logical**: add `encapsulation` argument (Fix [#674](https://github.com/jeremmfr/terraform-provider-junos/issues/674)) +* **data-source/junos_interface_logical**: add `encapsulation` attribute like resource +* **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) +* **data-source/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` attributes like resource +* **resource/junos_rstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* +* **resource/junos_rstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* +* **resource/junos_security_log_stream**: + * resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* + optional string attributes doesn't accept *empty* value + the resource schema has been upgraded to have one-blocks in single mode instead of list + * add `transport` block argument (Fix [#675](https://github.com/jeremmfr/terraform-provider-junos/issues/675)) +* **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments +* **resource/junos_vlan**: add `static_remote_vtep_list` argument inside `vxlan` block argument +* **resource/junos_vstp**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_interface**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_vlan**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* +* **resource/junos_vstp_vlan_group**: resource now use new [terraform-plugin-framework](https://github.com/hashicorp/terraform-plugin-framework) + some of config errors are now sent during Plan instead of during Apply + optional boolean attributes doesn't accept value *false* + ## v2.7.0 (2024-05-03) FEATURES: