diff --git a/.github/workflows/infra.yml b/.github/workflows/infra.yml index d5cf764b..123bbafd 100644 --- a/.github/workflows/infra.yml +++ b/.github/workflows/infra.yml @@ -36,6 +36,13 @@ name: Infra (Terraform) # set (both default off). A pure-Terraform change to either stack thus applies # without reconverging the nodes — and the ceph overlay join is skipped with it. # +# The fabric (switch) apply is gated the same way: on push it applies only when the +# fabric surface changed (tf/deployment/prod/htz-fsn1/fabric/**, the fabric shared +# modules, tf/providers/**, .mise/tasks/{fabric,infra}/**), and on workflow_dispatch +# only when the run_fabric toggle is set (default off). So an unrelated prod change +# (or a bare dispatch) no longer re-applies the switches; its overlay join is skipped +# too unless the mgmt converge needs it. +# # Environment gates rekey to - (one per stack's region): # staging-austin, staging-global, prod-global, prod-htz-fsn1. Each matrix apply # entry references its own gate, so an unprovisioned Environment hangs the apply. @@ -80,6 +87,10 @@ on: description: 'Run the mgmt Ansible converge after the fabric apply' type: boolean default: false + run_fabric: + description: 'Apply the fabric (switch) stack' + type: boolean + default: false # Serialize: the OVH S3 backend has no state locking (single-operator model), # so never let two infra runs apply concurrently. @@ -106,6 +117,9 @@ jobs: # change to the ceph/fabric stack applies without reconverging the nodes. ansible_ceph: ${{ steps.filter.outputs.ansible_ceph }} ansible_mgmt: ${{ steps.filter.outputs.ansible_mgmt }} + # Fabric-apply gate: the fabric (switch) stack applies only when its own + # surface changed — not on every prod change. + fabric: ${{ steps.filter.outputs.fabric }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -145,6 +159,20 @@ jobs: - 'ansible/mgmt/**' - 'tf/render/ansible-mgmt/**' - '.mise/tasks/mgmt/**' + # ── Fabric-apply gate (stack-step level) ── + # The fabric stack + the shared modules/providers/tasks it consumes. + # A hit means the switch config (or its plumbing) changed → apply it. + fabric: + - 'tf/deployment/prod/htz-fsn1/fabric/**' + - 'tf/shared/modules/core-fabric/**' + - 'tf/shared/modules/cluster-fabric/**' + - 'tf/shared/modules/fabric-login/**' + - 'tf/shared/modules/fabric-addressing/**' + - 'tf/shared/modules/fabric-netbox/**' + - 'tf/shared/modules/identity/**' + - 'tf/providers/**' + - '.mise/tasks/fabric/**' + - '.mise/tasks/infra/**' # ── Discover the stack matrix from the deployment tree ─────────────────────── discover: @@ -270,8 +298,8 @@ jobs: setup-key-ref: ${{ env.NB_CI_KEY_REF }} hostname: gha-plan-${{ matrix.partition }}-${{ matrix.region }}-${{ matrix.stack }}-${{ github.run_id }} - # Prod fabric goes through the mise task (builds the junos-qfx/hetzner - # providers, renders the NETCONF key, -parallelism=1). Every other stack is a + # Prod fabric goes through the mise task (builds the hetzner provider, renders + # the NETCONF key; junos comes from the registry). Every other stack is a # plain registry-provider terragrunt plan. - name: Terragrunt plan (fabric) if: matrix.stack == 'fabric' @@ -341,12 +369,28 @@ jobs: echo "mgmt=${mgmt:-false}" >> "$GITHUB_OUTPUT" echo "Ansible converge gates → ceph=${ceph:-false} mgmt=${mgmt:-false}" + # Decide whether the fabric (switch) apply runs. Same model as the Ansible + # gates: on push, gate on the paths-filter (did the fabric surface change?); + # on manual dispatch, gate on the run_fabric toggle (default off). + - name: Resolve fabric-apply gate + id: fabric_gate + if: matrix.stack == 'fabric' + env: + DISPATCH: ${{ github.event_name == 'workflow_dispatch' }} + DISPATCH_FABRIC: ${{ inputs.run_fabric }} + CHANGED_FABRIC: ${{ needs.changes.outputs.fabric }} + run: | + set -euo pipefail + if [ "$DISPATCH" = "true" ]; then run=$DISPATCH_FABRIC; else run=$CHANGED_FABRIC; fi + echo "run=${run:-false}" >> "$GITHUB_OUTPUT" + echo "fabric apply gate → ${run:-false}" + # Node-touching stacks join the overlay: talos (provisions over the LAN), # ceph (the Ansible convergence below), fabric (the switch vme + the mgmt # converge below). NetBird stacks themselves are pure api.netbird.io. The # key was minted by an earlier (lower-order) netbird apply in this same run. - name: Resolve NetBird CI setup-key ref - if: matrix.stack == 'talos' || (matrix.stack == 'ceph' && steps.ansible_gate.outputs.ceph == 'true') || matrix.stack == 'fabric' + if: matrix.stack == 'talos' || (matrix.stack == 'ceph' && steps.ansible_gate.outputs.ceph == 'true') || (matrix.stack == 'fabric' && (steps.fabric_gate.outputs.run == 'true' || steps.ansible_gate.outputs.mgmt == 'true')) run: | set -euo pipefail if [ "$PARTITION" = "prod" ]; then @@ -356,15 +400,16 @@ jobs: echo "NB_CI_KEY_REF=op://yucca_tf_staging/NETBIRD_YUCCA_STAGING_CI_SETUP_KEY/password" >> "$GITHUB_ENV" fi - name: Connect to NetBird - if: matrix.stack == 'talos' || (matrix.stack == 'ceph' && steps.ansible_gate.outputs.ceph == 'true') || matrix.stack == 'fabric' + if: matrix.stack == 'talos' || (matrix.stack == 'ceph' && steps.ansible_gate.outputs.ceph == 'true') || (matrix.stack == 'fabric' && (steps.fabric_gate.outputs.run == 'true' || steps.ansible_gate.outputs.mgmt == 'true')) uses: ./.github/actions/netbird-connect with: setup-key-ref: ${{ env.NB_CI_KEY_REF }} hostname: gha-apply-${{ matrix.partition }}-${{ matrix.region }}-${{ matrix.stack }}-${{ github.run_id }} # Prod fabric: mise task escalates to the write SA + builds providers. + # Gated: only when the fabric surface changed (push) or run_fabric (dispatch). - name: Terragrunt apply (fabric) - if: matrix.stack == 'fabric' + if: matrix.stack == 'fabric' && steps.fabric_gate.outputs.run == 'true' run: mise run infra:apply -- --non-interactive -auto-approve # Everything else: direct terragrunt apply with the partition's write SA. - name: Terragrunt apply diff --git a/.gitignore b/.gitignore index 8bcfada0..c3186092 100644 --- a/.gitignore +++ b/.gitignore @@ -66,7 +66,7 @@ tf/.terraformrc.local .mise/.provider-bin/ .mise/.provider-mirror/ .mise/.hetzner-provider-src/ -# self-built junos-qfx (mirror) makes this lock platform-specific; regenerated by init +# self-built hetzner (mirror) makes this lock platform-specific; regenerated by init tf/deployment/prod/htz-fsn1/fabric/.terraform.lock.hcl # ansible/mgmt inventory is TF-generated at run time (tf/render/ansible-mgmt) — diff --git a/.mise/tasks/fabric/provider-build b/.mise/tasks/fabric/provider-build deleted file mode 100755 index 60891c38..00000000 --- a/.mise/tasks/fabric/provider-build +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Build the vendored JTAF junos-qfx provider binary into the shared provider mirror" -set -euo pipefail -ROOT=$(git rev-parse --show-toplevel) -SRC="$ROOT/tf/providers/terraform-provider-junos-qfx" -MIRROR="${PROVIDER_MIRROR:-$ROOT/.mise/.provider-mirror}" -VER="${JUNOS_PROVIDER_VERSION:-0.0.1}" - -GOOS=$(go env GOOS); GOARCH=$(go env GOARCH) -BIN="terraform-provider-junos-qfx_v${VER}" - -# Build into the unpacked filesystem-mirror layout for both registry hosts -# (OpenTofu defaults to registry.opentofu.org; Terraform to registry.terraform.io). -# The shared terraformrc that wires this mirror up is written by `infra:providers`. -for HOST in registry.opentofu.org registry.terraform.io; do - DEST="$MIRROR/$HOST/hashicorp/junos-qfx/$VER/${GOOS}_${GOARCH}" - mkdir -p "$DEST" - ( cd "$SRC" && go build -o "$DEST/$BIN" . ) -done -echo "built junos-qfx v$VER (${GOOS}_${GOARCH}) -> $MIRROR" diff --git a/.mise/tasks/fabric/provider-gen b/.mise/tasks/fabric/provider-gen deleted file mode 100755 index 5ee3ec99..00000000 --- a/.mise/tasks/fabric/provider-gen +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Regenerate the vendored JTAF junos-qfx provider from device YANG + live config" -# Run this only when adding new config hierarchies (the provider's resource -# coverage is driven by the device XML). Then commit tf/providers/ and run -# `mise run fabric:provider-build`. -set -euo pipefail -ROOT=$(git rev-parse --show-toplevel) -CACHE="${FABRIC_JTAF_CACHE:-$HOME/.cache/yucca-jtaf}" -YREL="${FABRIC_JUNOS_YANG_RELEASE:-24.4/24.4R2}" -KEY="${FABRIC_NETCONF_KEY:-$HOME/.ssh/yucca-junos-tf}" -SPINE_IP="${FABRIC_SPINE_IP:-10.40.5.115}" -LEAF_IP="${FABRIC_LEAF_IP:-10.40.5.125}" -mkdir -p "$CACHE" - -# 1. JTAF tooling -[ -d "$CACHE/junos-terraform" ] || git clone --depth 1 https://github.com/Juniper/junos-terraform "$CACHE/junos-terraform" -cd "$CACHE/junos-terraform" -[ -d venv ] || python3 -m venv venv -./venv/bin/pip -q install -e . >/dev/null - -# 2. YANG (sparse: common + junos-qfx for the target release) -if [ ! -d "$CACHE/yang/$YREL" ]; then - [ -d "$CACHE/yang/.git" ] || git clone --no-checkout --depth 1 --filter=blob:none https://github.com/Juniper/yang "$CACHE/yang" - ( cd "$CACHE/yang" && git sparse-checkout init --cone \ - && git sparse-checkout set "$YREL/native/conf-and-rpcs/common" "$YREL/native/conf-and-rpcs/junos-qfx" \ - && git checkout ) -fi -Y="$CACHE/yang/$YREL/native/conf-and-rpcs" - -# 3. live device config XML (spine + leaf) via the dedicated terraform key -SSHOPTS="-i $KEY -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=$CACHE/known_hosts -o ConnectTimeout=15" -ssh $SSHOPTS terraform@"$SPINE_IP" "show configuration | display xml | no-more" > "$CACHE/spine.xml" -ssh $SSHOPTS terraform@"$LEAF_IP" "show configuration | display xml | no-more" > "$CACHE/leaf.xml" - -# 4. generate provider from both device types' config -source venv/bin/activate -jtaf-yang2go -p "$Y/common/models" "$Y"/junos-qfx/conf/models/*.yang -x "$CACHE/spine.xml" "$CACHE/leaf.xml" -t qfx - -# 5. vendor into the repo -rm -rf "$ROOT/tf/providers/terraform-provider-junos-qfx" -cp -R terraform-provider-junos-qfx "$ROOT/tf/providers/terraform-provider-junos-qfx" -rm -f "$ROOT/tf/providers/terraform-provider-junos-qfx/__init__.py" -echo "regenerated + vendored -> tf/providers/terraform-provider-junos-qfx" -echo "next: mise run fabric:provider-build" - -# Re-apply local provider patches (overwritten by regeneration). -python3 "$ROOT/tf/providers/apply_patches.py" -echo "next: mise run fabric:provider-build" diff --git a/.mise/tasks/infra/apply b/.mise/tasks/infra/apply index 6db5a527..d6897619 100755 --- a/.mise/tasks/infra/apply +++ b/.mise/tasks/infra/apply @@ -33,6 +33,31 @@ SITE="${SITE:-htz-fsn1}" # With a service-account token in the env, drop OP_ACCOUNT — the onepassword # provider rejects having both set ("service_account_token and account are set"). unset OP_ACCOUNT -# -parallelism=1: the JTAF junos-qfx provider isn't concurrency-safe — parallel -# ApplyResourceChange calls across the VCs crash the plugin ("Plugin did not respond"). -OP_ENV_FILE=tf/.env.prod "$ROOT/tf/op-run.sh" terragrunt --working-dir "tf/deployment/prod/$SITE/fabric" apply -parallelism=1 "$@" +STACK_DIR="tf/deployment/prod/$SITE/fabric" +# jeremmfr/junos is concurrency-safe (per-resource CRUD); reads/refresh run in +# parallel and commits serialize on the per-device config lock (handled by the +# provider). The device NETCONF connection-limit is raised to 250. +OP_ENV_FILE=tf/.env.prod "$ROOT/tf/op-run.sh" terragrunt --working-dir "$STACK_DIR" apply -parallelism=4 "$@" + +# Confirm the dangling `commit confirmed` jeremmfr leaves on the last commit per +# device: its per-resource "confirm" is `commit check`, which does NOT cancel the +# rollback on our Junos, so without this the last change auto-reverts. A plain +# `commit` confirms it. This runs ONLY after a successful apply (set -e): a +# mgmt-breaking apply errors above and skips this, so the dangling commit rolls +# back and restores management. Nothing pending → `commit` is a harmless no-op. +echo "==> confirming commit-confirmed on managed switches" +IPS=$(OP_ENV_FILE=tf/.env.prod "$ROOT/tf/op-run.sh" terragrunt --working-dir "$STACK_DIR" output -json switch_mgmt_ips | tr -d '[]" ' | tr ',' '\n') +KH=$(mktemp); trap 'rm -f "$KEYF" "$KH"' EXIT +for ip in $IPS; do + [ -n "$ip" ] || continue + ok= + for attempt in 1 2 3; do + if ssh -i "$KEYF" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new \ + -o UserKnownHostsFile="$KH" -o ConnectTimeout=15 -o NumberOfPasswordPrompts=0 \ + -o BatchMode=yes "terraform@$ip" "configure; commit; exit" >/dev/null 2>&1; then + ok=1; echo " confirmed $ip"; break + fi + echo " confirm attempt $attempt failed on $ip; retrying..."; sleep 5 + done + [ -n "$ok" ] || { echo "ERROR: could not confirm commit on $ip — its last change will roll back (commit_confirmed). Investigate." >&2; exit 1; } +done diff --git a/.mise/tasks/infra/plan b/.mise/tasks/infra/plan index b45977c1..0957a243 100755 --- a/.mise/tasks/infra/plan +++ b/.mise/tasks/infra/plan @@ -33,5 +33,5 @@ SITE="${SITE:-htz-fsn1}" # With a service-account token in the env, drop OP_ACCOUNT — the onepassword # provider rejects having both set ("service_account_token and account are set"). unset OP_ACCOUNT -# -parallelism=1: the JTAF junos-qfx provider isn't concurrency-safe (see apply). -OP_ENV_FILE=tf/.env.prod "$ROOT/tf/op-run.sh" terragrunt --working-dir "tf/deployment/prod/$SITE/fabric" plan -parallelism=1 "$@" +# jeremmfr/junos is concurrency-safe; the device NETCONF connection-limit is 250. +OP_ENV_FILE=tf/.env.prod "$ROOT/tf/op-run.sh" terragrunt --working-dir "tf/deployment/prod/$SITE/fabric" plan -parallelism=4 "$@" diff --git a/.mise/tasks/infra/providers b/.mise/tasks/infra/providers index dfc34393..b80a2111 100755 --- a/.mise/tasks/infra/providers +++ b/.mise/tasks/infra/providers @@ -1,29 +1,28 @@ #!/usr/bin/env bash -#MISE description="Build all of the deployment stack's local providers (junos-qfx + hetzner) into a filesystem mirror + write its terraformrc" +#MISE description="Build the deployment stack's locally-built providers (hetzner) into a filesystem mirror + write its terraformrc" set -euo pipefail ROOT=$(git rev-parse --show-toplevel) MIRROR="${PROVIDER_MIRROR:-$ROOT/.mise/.provider-mirror}" -# Per-provider component builders (each builds one binary into the mirror). -mise run fabric:provider-build # junos-qfx (switch fabric) -mise run mgmt:provider-build # hetzner (mgmt-host reprovisioning) +# hetzner (mgmt-host reprovisioning) is the only provider not on a public registry, +# so it's built locally into the mirror. junos (switch fabric) now comes from the +# registry (jeremmfr/junos); everything else is a normal registry provider. +mise run mgmt:provider-build # hetzner -# A single terraformrc points TF/tofu at the mirror for every locally-built -# provider, for both registry hosts (OpenTofu -> registry.opentofu.org, -# Terraform -> registry.terraform.io). Consumed via TF_CLI_CONFIG_FILE. +# Point TF/tofu at the mirror for the locally-built provider only, for both registry +# hosts (OpenTofu -> registry.opentofu.org, Terraform -> registry.terraform.io). +# Everything else resolves `direct` from the registry. Consumed via TF_CLI_CONFIG_FILE. cat > "$ROOT/tf/.terraformrc.local" <<'EOF' # Generated by 'mise run infra:providers' — do not edit. provider_installation { filesystem_mirror { path = "__MIRROR__" include = [ - "registry.opentofu.org/hashicorp/junos-qfx", "registry.terraform.io/hashicorp/junos-qfx", "registry.opentofu.org/zack/hetzner", "registry.terraform.io/zack/hetzner", ] } direct { exclude = [ - "registry.opentofu.org/hashicorp/junos-qfx", "registry.terraform.io/hashicorp/junos-qfx", "registry.opentofu.org/zack/hetzner", "registry.terraform.io/zack/hetzner", ] } diff --git a/tf/.terraformrc.fabric b/tf/.terraformrc.fabric deleted file mode 100644 index dfb02eec..00000000 --- a/tf/.terraformrc.fabric +++ /dev/null @@ -1,10 +0,0 @@ -# Generated by 'mise run fabric:provider-build' — do not edit. -provider_installation { - filesystem_mirror { - path = "/Users/leca/src/yucca/.mise/.fabric-provider-mirror" - include = ["registry.opentofu.org/hashicorp/junos-qfx", "registry.terraform.io/hashicorp/junos-qfx"] - } - direct { - exclude = ["registry.opentofu.org/hashicorp/junos-qfx", "registry.terraform.io/hashicorp/junos-qfx"] - } -} diff --git a/tf/deployment/prod/htz-fsn1/fabric/README.md b/tf/deployment/prod/htz-fsn1/fabric/README.md index ac82bd94..505cee27 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/README.md +++ b/tf/deployment/prod/htz-fsn1/fabric/README.md @@ -1,4 +1,4 @@ -# htz-fsn1 — production switch fabric (Junos via JTAF + NetBox) +# htz-fsn1 — production switch fabric (Junos via jeremmfr/junos + NetBox) Manages the Falkenstein (site 40) switch fabric as code: @@ -24,9 +24,10 @@ VLAN id == the network's third octet; gateway = `.1` (IRB on the leaf). ## Layout - `modules/fabric-addressing` — IDs → CIDRs/VLANs/gateways (single source of truth). -- `modules/core-fabric` / `modules/cluster-fabric` — the spine / leaf config (one - JTAF `junos-qfx` resource each), generated from the live config and parameterized - on the addressing. **Secrets (`root-authentication`) are stripped.** +- `modules/core-fabric` / `modules/cluster-fabric` — the spine / leaf config as typed + `junos_*` resources (interfaces, vlans, bgp, firewall, policy, …), parameterized on + the addressing. `system services` + `root-authentication` + `vme` are deliberately + NOT managed, so no apply can break the management path. - `modules/identity` — the central user + group registry (single source of truth for who has access and what they're a member of). Edit it to add people. Members of fabric-mapped groups (e.g. `fabric-admins` → super-user) are synthesized into @@ -37,16 +38,15 @@ VLAN id == the network's third octet; gateway = `.1` (IRB on the leaf). ## The providers -This stack builds two providers locally (neither is on a registry) into a shared -filesystem mirror via `mise run infra:providers`: - -- `junos-qfx` — **JTAF-generated and vendored** in `tf/providers/` (the switch fabric). +- `junos` (`jeremmfr/junos`) — the switch fabric: a typed, per-resource provider from + the registry (pinned in `versions.tf`). It drives each VC over NETCONF (port 830) as + the `terraform` user; commits are `commit confirmed` (auto-rollback) and + `infra:apply` confirms each switch after a successful apply. - `hetzner` (`zack/hetzner`) — the Hetzner Robot API, for mgmt-host reprovisioning - (`mgmt.tf`); cloned + built (pinned tag) by `mise run mgmt:provider-build`. + (`mgmt.tf`); the one provider not on a registry, cloned + built (pinned tag) into a + filesystem mirror by `mise run mgmt:provider-build`. -- `mise run fabric:provider-gen` — regenerate the junos-qfx provider from device YANG - + live config (only when adding new config hierarchies), then commit `tf/providers/`. -- `mise run infra:providers` — build both providers into the mirror and write +- `mise run infra:providers` — build the hetzner provider into the mirror and write `tf/.terraformrc.local` (consumed via `TF_CLI_CONFIG_FILE`). ## Running @@ -61,10 +61,11 @@ mise run infra:apply # ... apply CI: `.github/workflows/infra.yml` — plan on PR, gated apply on merge behind the site-scoped `prod-htz-fsn1` GitHub Environment (required reviewers). -## Adoption caveat (first run) +## Adopting existing config -The JTAF provider has **no `terraform import`** and pushes config with `action="merge"` -(additive). NetBox objects + the existing direct switch config already exist (manually -seeded), so the first `apply` *asserts* matching config (idempotent on the switches) -and **NetBox prefixes/VLANs must be `import`ed** first or they'll clash. Run a `plan` -and review before the first `apply`. +`jeremmfr/junos` supports `terraform import`. To bring config that already exists on a +switch under management (a new resource, or a new VC against pre-seeded config), add an +`import {}` block — id = the config name, e.g. `et-0/0/0`, `lo0.0`, `PROTECT-RE_-_inet`, +`_-_` (static route), `_-__-_` (bgp neighbor) — run `plan`, +review (**expect 0 destroys**), `apply`, then remove the block. NetBox prefixes/VLANs +likewise import before first apply or they clash. The whole fabric was adopted this way. diff --git a/tf/deployment/prod/htz-fsn1/fabric/fabric.tf b/tf/deployment/prod/htz-fsn1/fabric/fabric.tf index ff836104..070678c2 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/fabric.tf +++ b/tf/deployment/prod/htz-fsn1/fabric/fabric.tf @@ -12,7 +12,7 @@ module "identity" { module "core" { source = "../../../../shared/modules/core-fabric" - providers = { junos-qfx = junos-qfx.spine } + providers = { junos = junos.spine } public_vlan_id = module.addr_cls1.public_vlan_id private_vlan_id = module.addr_cls1.private_vlan_id @@ -42,7 +42,7 @@ module "core" { module "cluster_cls1" { source = "../../../../shared/modules/cluster-fabric" - providers = { junos-qfx = junos-qfx.leaf_cls1 } + providers = { junos = junos.leaf_cls1 } public_cidr = module.addr_cls1.public_cidr private_cidr = module.addr_cls1.private_cidr @@ -65,20 +65,18 @@ locals { module "login_spine" { source = "../../../../shared/modules/fabric-login" - providers = { junos-qfx = junos-qfx.spine } + providers = { junos = junos.spine } - resource_name = "login" - users = module.identity.fabric_login.users - classes = module.identity.fabric_login.classes - name_servers = local.fabric_name_servers + users = module.identity.fabric_login.users + classes = module.identity.fabric_login.classes + name_servers = local.fabric_name_servers } module "login_leaf_cls1" { source = "../../../../shared/modules/fabric-login" - providers = { junos-qfx = junos-qfx.leaf_cls1 } + providers = { junos = junos.leaf_cls1 } - resource_name = "login" - users = module.identity.fabric_login.users - classes = module.identity.fabric_login.classes - name_servers = local.fabric_name_servers + users = module.identity.fabric_login.users + classes = module.identity.fabric_login.classes + name_servers = local.fabric_name_servers } diff --git a/tf/deployment/prod/htz-fsn1/fabric/main.tf b/tf/deployment/prod/htz-fsn1/fabric/main.tf index f775ea60..6d36b313 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/main.tf +++ b/tf/deployment/prod/htz-fsn1/fabric/main.tf @@ -6,7 +6,7 @@ # addressing.tf — IP plan (fabric-addressing: site/cluster IDs -> CIDRs/VLANs) # fabric.tf — switch config: spine core + cluster leaves + login # netbox.tf — NetBox IPAM mirrored from the addressing -# providers.tf — per-VC junos-qfx providers (+ netbox) +# providers.tf — per-VC junos providers (+ netbox) # versions.tf / variables.tf / terraform.auto.tfvars / terragrunt.hcl # # Add a cluster: a leaf provider in providers.tf, addr_clsN in addressing.tf, diff --git a/tf/deployment/prod/htz-fsn1/fabric/outputs.tf b/tf/deployment/prod/htz-fsn1/fabric/outputs.tf new file mode 100644 index 00000000..1e85ca21 --- /dev/null +++ b/tf/deployment/prod/htz-fsn1/fabric/outputs.tf @@ -0,0 +1,7 @@ +# Mgmt IPs of every switch VC this stack manages. Consumed by `infra:apply` to +# confirm the dangling `commit confirmed` jeremmfr leaves on each device after a +# successful apply (extend with each cluster leaf as clusters are added). +output "switch_mgmt_ips" { + description = "Switch VC management IPs (spine + cluster leaves) to confirm post-apply." + value = [module.addr_site.spine_mgmt_ip, module.addr_cls1.leaf_mgmt_ip] +} diff --git a/tf/deployment/prod/htz-fsn1/fabric/providers.tf b/tf/deployment/prod/htz-fsn1/fabric/providers.tf index 5d53d0a4..e618409f 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/providers.tf +++ b/tf/deployment/prod/htz-fsn1/fabric/providers.tf @@ -1,22 +1,37 @@ -# One junos-qfx provider instance per switch VC, host derived from the addressing +# One junos provider instance per switch VC, host derived from the addressing # module (spine = site .115; each cluster leaf = .125 + (n-1)*10). Auth is the # dedicated `terraform` NETCONF user with an SSH key (path from var; rendered from # 1Password by the mise/CI runner — never committed). -provider "junos-qfx" { - alias = "spine" - host = module.addr_site.spine_mgmt_ip # 10.40.5.115 - port = 830 - username = "terraform" - sshkey = var.netconf_ssh_key_path +# commit_confirmed = N: every commit is a `commit confirmed N`, auto-rolled-back +# after N minutes unless the provider re-confirms. If an apply severs the NETCONF +# session (bad filter, interface, services change), the switch reverts itself — +# the management lifeline can't be permanently broken. +# +# wait_percent = 0: confirm IMMEDIATELY (no dead-wait between commit + confirm). +# Protection still holds — the confirm runs over a fresh connection, so a +# mgmt-breaking commit fails the confirm and rolls back. A non-zero wait only +# guards the rare "break manifests a few seconds late" case, and isn't worth the +# serialized dead-time (Junos won't accept a new commit while a confirmed one is +# pending, so the wait multiplies across every committed resource). +provider "junos" { + alias = "spine" + ip = module.addr_site.spine_mgmt_ip # 10.40.5.115 + port = 830 + username = "terraform" + sshkeyfile = var.netconf_ssh_key_path + commit_confirmed = 3 + commit_confirmed_wait_percent = 0 } -provider "junos-qfx" { - alias = "leaf_cls1" - host = module.addr_cls1.leaf_mgmt_ip # 10.40.5.125 - port = 830 - username = "terraform" - sshkey = var.netconf_ssh_key_path +provider "junos" { + alias = "leaf_cls1" + ip = module.addr_cls1.leaf_mgmt_ip # 10.40.5.125 + port = 830 + username = "terraform" + sshkeyfile = var.netconf_ssh_key_path + commit_confirmed = 3 + commit_confirmed_wait_percent = 0 } provider "netbox" { diff --git a/tf/deployment/prod/htz-fsn1/fabric/terragrunt.hcl b/tf/deployment/prod/htz-fsn1/fabric/terragrunt.hcl index e53e76a1..e58ac0fc 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/terragrunt.hcl +++ b/tf/deployment/prod/htz-fsn1/fabric/terragrunt.hcl @@ -3,6 +3,7 @@ include "root" { } # State key derives from the path: yucca/prod/htz-fsn1/fabric/terraform.tfstate -# Providers come from versions.tf; the junos-qfx provider is supplied via -# dev_overrides (TF_CLI_CONFIG_FILE), set by the `fabric:*` mise tasks and CI. +# Providers come from versions.tf (junos = jeremmfr/junos from the registry); the +# hetzner provider is supplied via a filesystem mirror (TF_CLI_CONFIG_FILE), set +# by the `infra:*` mise tasks and CI. # terraform.auto.tfvars is loaded automatically. diff --git a/tf/deployment/prod/htz-fsn1/fabric/versions.tf b/tf/deployment/prod/htz-fsn1/fabric/versions.tf index 05433c09..26695d75 100644 --- a/tf/deployment/prod/htz-fsn1/fabric/versions.tf +++ b/tf/deployment/prod/htz-fsn1/fabric/versions.tf @@ -1,18 +1,18 @@ terraform { required_version = "~> 1.11" required_providers { - # JTAF-generated, vendored in tf/providers/terraform-provider-junos-qfx and - # supplied via dev_overrides (built by `infra:providers`; supplied via TF_CLI_CONFIG_FILE). - junos-qfx = { - source = "hashicorp/junos-qfx" + # Community Junos provider (typed per-resource CRUD) from the registry. + junos = { + source = "jeremmfr/junos" + version = "~> 2.19" } netbox = { source = "e-breuninger/netbox" version = "~> 4.0" } # Hetzner Robot (dedicated-server) API — mgmt-host reprovisioning (mgmt.tf). - # Built locally + supplied via the same filesystem_mirror as junos-qfx - # (mise `mgmt:provider-build`, invoked by `infra:providers`). + # The only non-registry provider: built locally + supplied via a filesystem + # mirror (mise `mgmt:provider-build`, invoked by `infra:providers`). hetzner = { source = "zack/hetzner" } diff --git a/tf/providers/apply_patches.py b/tf/providers/apply_patches.py deleted file mode 100644 index 57976cea..00000000 --- a/tf/providers/apply_patches.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python3 -"""Re-apply local patches to the JTAF-generated junos-qfx provider. - -JTAF regenerates the provider from device YANG + config (mise fabric:provider-gen), -overwriting our edits. This re-applies them; it's idempotent and run automatically -at the end of fabric:provider-gen. - -Patches: - 1. readStateFromDevice — trust the apply: return the reference (plan/prior state) - instead of reading the WHOLE device back and reconciling. The provider manages - only a slice; the device always carries config outside it (the built-in - `default` VLAN, em0/vme, the `system` block, ...), so a full read-back trips - Terraform's "provider produced inconsistent result". Trade-off: out-of-band - drift isn't detected; every apply re-asserts the merge. - 2. publicKeyFile / NewClient — fail with a clear error on an unreadable/unparseable - SSH key instead of returning a nil AuthMethod, which made the SSH handshake - panic ("Plugin did not respond"). - 3. execute / netconfReplyError — surface NETCONF (at any depth, incl. - nested under ) as a real error. The generated client ignored - reply errors, so a rejected load/commit silently no-op'd while reporting success. - 4. patch/CreateDiffPatch — emit nc:operation="merge" instead of "create" for - ADDED nodes, so applies are idempotent over pre-existing device config - (brownfield / empty-state diffs, e.g. after a state-key change re-pushes the - whole config). replace/delete unchanged. Consistent with the edit-config - default-operation=merge envelope and patch #1 (trust-the-apply). -""" -import sys -import pathlib - -root = pathlib.Path(__file__).parent / "terraform-provider-junos-qfx" -errors = [] - - -def patch_readstate(): - src = root / "resource_config_provider.go" - t = src.read_text() - sig = ("func (r *configResource) readStateFromDevice(ctx context.Context, " - "reference ConfigResourceModel, diags *diag.Diagnostics) (ConfigResourceModel, bool) {") - if sig not in t: - errors.append("readStateFromDevice signature not found") - return - i = t.index(sig) - j = t.index("\n}\n", i) - body = (sig + - "\n\t// PATCHED (fabric): trust the apply — return the plan/prior state as the" - "\n\t// resource state instead of reading the whole device back (avoids" - "\n\t// \"provider produced inconsistent result\"). See apply_patches.py.\n" - "\treturn reference, true\n") - new = t[:i] + body + t[j + 1:] - if new != t: - src.write_text(new) - print(" patched readStateFromDevice") - else: - print(" readStateFromDevice already patched") - - -def replace_once(src, old, new, label): - t = src.read_text() - if new in t: - print(f" {label} already patched") - return - if old not in t: - errors.append(f"{label}: target not found") - return - src.write_text(t.replace(old, new, 1)) - print(f" patched {label}") - - -NETCONF_REPLY_ERROR_FN = ''' - -// netconfReplyError returns a non-nil error if the rpc-reply contains any -// error-severity at ANY depth. Junos nests them under -// (optionally per ) and -// , not just directly under . -func netconfReplyError(raw []byte) error { - dec := xml.NewDecoder(bytes.NewReader(raw)) - var msgs []string - for { - tok, err := dec.Token() - if err != nil { - break - } - se, ok := tok.(xml.StartElement) - if !ok || se.Name.Local != "rpc-error" { - continue - } - var e struct { - Severity string `xml:"error-severity"` - Message string `xml:"error-message"` - Path string `xml:"error-path"` - } - if dec.DecodeElement(&e, &se) != nil { - continue - } - if strings.EqualFold(strings.TrimSpace(e.Severity), "warning") { - continue - } - m := strings.TrimSpace(e.Message) - if p := strings.TrimSpace(e.Path); p != "" { - m = strings.TrimSpace(p) + ": " + m - } - if m != "" { - msgs = append(msgs, m) - } - } - if len(msgs) > 0 { - return fmt.Errorf("device rejected the change: %s", strings.Join(msgs, "; ")) - } - return nil -} -''' - - -def patch_client(): - src = root / "netconf" / "client.go" - - # 1. SSH key: clear error instead of nil AuthMethod -> panic. - replace_once( - src, - "func publicKeyFile(file string) ssh.AuthMethod {\n" - "\tbuffer, err := os.ReadFile(file)\n" - "\tif err != nil {\n\t\treturn nil\n\t}\n\n" - "\tkey, err := ssh.ParsePrivateKey(buffer)\n" - "\tif err != nil {\n\t\treturn nil\n\t}\n" - "\treturn ssh.PublicKeys(key)\n}", - "func publicKeyFile(file string) (ssh.AuthMethod, error) {\n" - "\tbuffer, err := os.ReadFile(file)\n" - "\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading SSH key %q: %w\", file, err)\n\t}\n\n" - "\tkey, err := ssh.ParsePrivateKey(buffer)\n" - "\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing SSH key %q (is it a valid private key?): %w\", file, err)\n\t}\n" - "\treturn ssh.PublicKeys(key), nil\n}", - "publicKeyFile", - ) - replace_once( - src, - "\tif sshKey != \"\" {\n" - "\t\tauthMethod := publicKeyFile(sshKey)\n" - "\t\tcfg.Auth = []ssh.AuthMethod{authMethod}\n" - "\t} else {", - "\tif sshKey != \"\" {\n" - "\t\tauthMethod, err := publicKeyFile(sshKey)\n" - "\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n" - "\t\tcfg.Auth = []ssh.AuthMethod{authMethod}\n" - "\t} else {", - "NewClient", - ) - - # 2. Surface NETCONF rpc-error: bytes import, execute() check, helper fn. - replace_once( - src, - "import (\n\t\"context\"", - "import (\n\t\"bytes\"\n\t\"context\"", - "bytes import", - ) - replace_once( - src, - "\tdebugRPC(\"rpc reply\", string(rawReply))\n\n\treply := struct {", - "\tdebugRPC(\"rpc reply\", string(rawReply))\n\n" - "\tif rerr := netconfReplyError(rawReply); rerr != nil {\n\t\treturn \"\", rerr\n\t}\n\n" - "\treply := struct {", - "execute rpc-error check", - ) - t = src.read_text() - if "func netconfReplyError(" not in t: - src.write_text(t.rstrip() + "\n" + NETCONF_REPLY_ERROR_FN) - print(" appended netconfReplyError") - else: - print(" netconfReplyError already present") - - -def patch_diff_merge(): - src = root / "patch" / "patch.go" - # Positional leaf-list adds (ordered Create + Replace-reorder's new value). - replace_once( - src, - '\t\t\t\tleaf := &Node{Tag: p.tag, Parent: p.parent, Operation: "create", Text: p.change.NewVal}', - '\t\t\t\tleaf := &Node{Tag: p.tag, Parent: p.parent, Operation: "merge", Text: p.change.NewVal}', - "diff positional create (leaf)", - ) - replace_once( - src, - '\t\t\t\tcre := &Node{Tag: p.tag, Parent: p.parent, Operation: "create", Text: p.change.NewVal}', - '\t\t\t\tcre := &Node{Tag: p.tag, Parent: p.parent, Operation: "merge", Text: p.change.NewVal}', - "diff positional create (cre)", - ) - # Regular leaf add. - replace_once( - src, - '\t\tcase Create:\n\t\t\tleaf.Operation = "create"\n\t\t\tleaf.Text = p.change.NewVal', - '\t\tcase Create:\n' - '\t\t\t// merge, not create: idempotent over pre-existing device config\n' - '\t\t\t// (brownfield/empty-state applies), consistent with default-operation=merge.\n' - '\t\t\tleaf.Operation = "merge"\n\t\t\tleaf.Text = p.change.NewVal', - "diff leaf create->merge", - ) - # Keyed-list entry parent add. - replace_once( - src, - '\tswitch change.Op {\n\tcase Create:\n\t\tparent.Operation = "create"', - '\tswitch change.Op {\n\tcase Create:\n' - '\t\t// merge (see Create case above): idempotent over existing config.\n' - '\t\tparent.Operation = "merge"', - "diff keyed-entry create->merge", - ) - - -patch_readstate() -patch_client() -patch_diff_merge() - -if errors: - print("apply_patches: FAILED:\n - " + "\n - ".join(errors), file=sys.stderr) - sys.exit(1) -print("apply_patches: done") diff --git a/tf/providers/terraform-provider-junos-qfx/.gitignore b/tf/providers/terraform-provider-junos-qfx/.gitignore deleted file mode 100644 index 6a0b8272..00000000 --- a/tf/providers/terraform-provider-junos-qfx/.gitignore +++ /dev/null @@ -1 +0,0 @@ -terraform_provider diff --git a/tf/providers/terraform-provider-junos-qfx/README.md b/tf/providers/terraform-provider-junos-qfx/README.md deleted file mode 100644 index fc5a5315..00000000 --- a/tf/providers/terraform-provider-junos-qfx/README.md +++ /dev/null @@ -1,6 +0,0 @@ -## README - -The files in here are automatically copied to the new provider. - -DO NOT FIDDLE WITH THEM! - diff --git a/tf/providers/terraform-provider-junos-qfx/config.go b/tf/providers/terraform-provider-junos-qfx/config.go deleted file mode 100644 index 1546c2b3..00000000 --- a/tf/providers/terraform-provider-junos-qfx/config.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2017-2022, Juniper Networks Inc. All rights reserved. -// -// License: Apache 2.0 -// -// THIS SOFTWARE IS PROVIDED BY Juniper Networks, Inc. ''AS IS'' AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL Juniper Networks, Inc. BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -package main - -import ( - "terraform-provider-junos-qfx/netconf" -) - -// Config is the configuration structure used to instantiate the Netconf provider. -type Config struct { - Host string - Port int - Username string - Password string - SSHKey string -} - -// Client returns a new client for the provider to use -func (c *Config) Client() (netconf.Client, error) { - return newClient(c) -} - -func newClient(c *Config) (netconf.Client, error) { - - client, err := netconf.NewClient(c.Username, c.Password, c.SSHKey, c.Host, c.Port) - return client, err -} \ No newline at end of file diff --git a/tf/providers/terraform-provider-junos-qfx/config_test.go b/tf/providers/terraform-provider-junos-qfx/config_test.go deleted file mode 100644 index 04b3ae33..00000000 --- a/tf/providers/terraform-provider-junos-qfx/config_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package main - -import ( - "testing" -) - -// TestConfigClientWithPassword tests creating a client with password authentication -func TestConfigClientWithPassword(t *testing.T) { - config := &Config{ - Host: "localhost", - Port: 830, - Username: "testuser", - Password: "testpass", - SSHKey: "", - } - - client, err := config.Client() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if client == nil { - t.Error("expected non-nil client, got nil") - } -} - -// TestConfigClientWithSSHKey tests creating a client with SSH key authentication -func TestConfigClientWithSSHKey(t *testing.T) { - config := &Config{ - Host: "localhost", - Port: 830, - Username: "testuser", - Password: "", - SSHKey: "/path/to/key", - } - - client, err := config.Client() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if client == nil { - t.Error("expected non-nil client, got nil") - } -} - -// TestConfigClientWithEmptyHost tests client creation with empty host -func TestConfigClientWithEmptyHost(t *testing.T) { - config := &Config{ - Host: "", - Port: 830, - Username: "testuser", - Password: "testpass", - SSHKey: "", - } - - client, err := config.Client() - if err != nil { - // Expected error when host is empty - t.Logf("got expected error: %v", err) - } - - if client != nil { - // Client creation may still succeed, so we just validate - t.Logf("client created with empty host: %v", client) - } -} - -// TestConfigClientWithValidParams tests client creation with all valid parameters -func TestConfigClientWithValidParams(t *testing.T) { - testCases := []struct { - name string - config *Config - wantErr bool - desc string - }{ - { - name: "valid with password", - config: &Config{ - Host: "192.168.1.1", - Port: 830, - Username: "admin", - Password: "admin123", - SSHKey: "", - }, - wantErr: false, - desc: "Should create client with password authentication", - }, - { - name: "valid with ssh key", - config: &Config{ - Host: "192.168.1.1", - Port: 830, - Username: "admin", - Password: "", - SSHKey: "/home/user/.ssh/id_rsa", - }, - wantErr: false, - desc: "Should create client with SSH key authentication", - }, - { - name: "custom port", - config: &Config{ - Host: "10.0.0.1", - Port: 2222, - Username: "operator", - Password: "pass", - SSHKey: "", - }, - wantErr: false, - desc: "Should create client with custom SSH port", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - client, err := tc.config.Client() - - if (err != nil) != tc.wantErr { - t.Errorf("unexpected error state: %v (expected error: %v)", err, tc.wantErr) - } - - if !tc.wantErr && client == nil { - t.Error("expected non-nil client") - } - - t.Logf("%s", tc.desc) - }) - } -} - -// TestNewClientDirectly tests the newClient helper function -func TestNewClientDirectly(t *testing.T) { - config := &Config{ - Host: "localhost", - Port: 830, - Username: "user", - Password: "pass", - SSHKey: "", - } - - client, err := newClient(config) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if client == nil { - t.Error("expected non-nil client") - } -} - -// TestConfigValues tests that config values are properly set -func TestConfigValues(t *testing.T) { - expectedHost := "example.com" - expectedPort := 2222 - expectedUsername := "admin" - expectedPassword := "secret" - expectedSSHKey := "/path/to/key" - - config := &Config{ - Host: expectedHost, - Port: expectedPort, - Username: expectedUsername, - Password: expectedPassword, - SSHKey: expectedSSHKey, - } - - if config.Host != expectedHost { - t.Errorf("host mismatch: expected %s, got %s", expectedHost, config.Host) - } - - if config.Port != expectedPort { - t.Errorf("port mismatch: expected %d, got %d", expectedPort, config.Port) - } - - if config.Username != expectedUsername { - t.Errorf("username mismatch: expected %s, got %s", expectedUsername, config.Username) - } - - if config.Password != expectedPassword { - t.Errorf("password mismatch: expected %s, got %s", expectedPassword, config.Password) - } - - if config.SSHKey != expectedSSHKey { - t.Errorf("ssh key mismatch: expected %s, got %s", expectedSSHKey, config.SSHKey) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/go.mod b/tf/providers/terraform-provider-junos-qfx/go.mod deleted file mode 100644 index ede8d23f..00000000 --- a/tf/providers/terraform-provider-junos-qfx/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module terraform-provider-junos-qfx - -go 1.25.6 - -require ( - github.com/hashicorp/terraform-plugin-framework v1.18.0 - github.com/hashicorp/terraform-plugin-go v0.30.0 - golang.org/x/crypto v0.48.0 - nemith.io/netconf v0.0.4 -) - -require ( - github.com/fatih/color v1.18.0 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/hashicorp/go-hclog v1.6.3 // indirect - github.com/hashicorp/go-plugin v1.7.0 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/terraform-plugin-log v0.10.0 // indirect - github.com/hashicorp/terraform-registry-address v0.4.0 // indirect - github.com/hashicorp/terraform-svchost v0.2.0 // indirect - github.com/hashicorp/yamux v0.1.2 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/oklog/run v1.2.0 // indirect - github.com/stretchr/testify v1.11.1 // indirect - github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.34.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect - google.golang.org/grpc v1.81.1 // indirect - google.golang.org/protobuf v1.36.11 // indirect -) \ No newline at end of file diff --git a/tf/providers/terraform-provider-junos-qfx/go.sum b/tf/providers/terraform-provider-junos-qfx/go.sum deleted file mode 100644 index 09ebe8ee..00000000 --- a/tf/providers/terraform-provider-junos-qfx/go.sum +++ /dev/null @@ -1,119 +0,0 @@ -github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= -github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/carlmjohnson/be v0.25.2 h1:EPTT7qCF5xJjcgrV5yX/muP5HTqSJR2VOjO6O4l9cYE= -github.com/carlmjohnson/be v0.25.2/go.mod h1:2P+bH/INocW7e411OYCCIwT3nnJneZyveVav0WBBM1U= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= -github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/terraform-plugin-framework v1.18.0 h1:Xy6OfqSTZfAAKXSlJ810lYvuQvYkOpSUoNMQ9l2L1RA= -github.com/hashicorp/terraform-plugin-framework v1.18.0/go.mod h1:eeFIf68PME+kenJeqSrIcpHhYQK0TOyv7ocKdN4Z35E= -github.com/hashicorp/terraform-plugin-framework v1.19.0 h1:q0bwyhxAOR3vfdgbk9iplv3MlTv/dhBHTXjQOtQDoBA= -github.com/hashicorp/terraform-plugin-framework v1.19.0/go.mod h1:YRXOBu0jvs7xp4AThBbX4mAzYaMJ1JgtFH//oGKxwLc= -github.com/hashicorp/terraform-plugin-go v0.30.0 h1:VmEiD0n/ewxbvV5VI/bYwNtlSEAXtHaZlSnyUUuQK6k= -github.com/hashicorp/terraform-plugin-go v0.30.0/go.mod h1:8d523ORAW8OHgA9e8JKg0ezL3XUO84H0A25o4NY/jRo= -github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8= -github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8= -github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= -github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= -github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= -github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= -github.com/hashicorp/terraform-svchost v0.2.0 h1:wVc2vMiodOHvNZcQw/3y9af1XSomgjGSv+rv3BMCk7I= -github.com/hashicorp/terraform-svchost v0.2.0/go.mod h1:/98rrS2yZsbppi4VGVCjwYmh8dqsKzISqK7Hli+0rcQ= -github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= -github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= -github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= -github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= -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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= -github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= -github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= -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= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nemith.io/netconf v0.0.4 h1:v1i05GAypUTYRrA1gwt6bZCWhDeDkB7sL7BO8JwkMWY= -nemith.io/netconf v0.0.4/go.mod h1:VisEiVJJ+W4NgTZ4QPKJb70RttJN2Ky6vvxzs8X5Dpg= diff --git a/tf/providers/terraform-provider-junos-qfx/main.go b/tf/providers/terraform-provider-junos-qfx/main.go deleted file mode 100644 index 01cb2687..00000000 --- a/tf/providers/terraform-provider-junos-qfx/main.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2017-2022, Juniper Networks Inc. All rights reserved. -// -// License: Apache 2.0 -// -// THIS SOFTWARE IS PROVIDED BY Juniper Networks, Inc. ''AS IS'' AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL Juniper Networks, Inc. BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -package main - -import ( - "context" - "flag" - "log" - - "github.com/hashicorp/terraform-plugin-framework/providerserver" -) - -var ( - parseFlags = func(debug *bool) { - flag.BoolVar(debug, "debug", false, "set to true to run the provider with support for debuggers like delve") - flag.Parse() - } - serveProvider = providerserver.Serve - fatalLogger = func(v ...interface{}) { - log.Fatal(v...) - } -) - -// run parses runtime flags and starts the provider server. -func run() error { - var debug bool - parseFlags(&debug) - - ctx := context.Background() - opts := providerserver.ServeOpts{ - Address: "tf-registry.click/juniper/jtaf670ffa332c26b46b", - Debug: debug, - } - - return serveProvider(ctx, newProvider, opts) -} - -// main executes the provider process and exits on startup errors. -func main() { - if err := run(); err != nil { - fatalLogger(err) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/main_test.go b/tf/providers/terraform-provider-junos-qfx/main_test.go deleted file mode 100644 index 7040bd9d..00000000 --- a/tf/providers/terraform-provider-junos-qfx/main_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "context" - "errors" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/provider" - "github.com/hashicorp/terraform-plugin-framework/providerserver" -) - -// TestRunPassesDebugToServe verifies run forwards parsed debug state to Serve. -func TestRunPassesDebugToServe(t *testing.T) { - originalParseFlags := parseFlags - originalServeProvider := serveProvider - t.Cleanup(func() { - parseFlags = originalParseFlags - serveProvider = originalServeProvider - }) - - parseFlags = func(debug *bool) { - *debug = true - } - - called := false - serveProvider = func(_ context.Context, providerFunc func() provider.Provider, opts providerserver.ServeOpts) error { - called = true - if !opts.Debug { - t.Fatalf("expected debug=true in serve options") - } - if opts.Address == "" { - t.Fatalf("expected non-empty provider address") - } - if providerFunc() == nil { - t.Fatalf("expected provider constructor to return non-nil provider") - } - return nil - } - - if err := run(); err != nil { - t.Fatalf("run() returned unexpected error: %v", err) - } - if !called { - t.Fatalf("expected serveProvider to be called") - } -} - -// TestMainLogsFatalOnRunError verifies main logs fatally when startup fails. -func TestMainLogsFatalOnRunError(t *testing.T) { - originalParseFlags := parseFlags - originalServeProvider := serveProvider - originalFatalLogger := fatalLogger - t.Cleanup(func() { - parseFlags = originalParseFlags - serveProvider = originalServeProvider - fatalLogger = originalFatalLogger - }) - - parseFlags = func(_ *bool) {} - serveErr := errors.New("serve failed") - serveProvider = func(_ context.Context, _ func() provider.Provider, _ providerserver.ServeOpts) error { - return serveErr - } - - called := false - fatalLogger = func(v ...interface{}) { - called = true - if len(v) != 1 || v[0] != serveErr { - t.Fatalf("fatalLogger called with unexpected args: %#v", v) - } - } - - main() - - if !called { - t.Fatalf("expected fatalLogger to be called") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/netconf/LICENSE b/tf/providers/terraform-provider-junos-qfx/netconf/LICENSE deleted file mode 100644 index d69525fa..00000000 --- a/tf/providers/terraform-provider-junos-qfx/netconf/LICENSE +++ /dev/null @@ -1,11 +0,0 @@ -Copyright © 2013-2018 Juniper Networks, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -(1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -(2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Juniper Networks. diff --git a/tf/providers/terraform-provider-junos-qfx/netconf/client.go b/tf/providers/terraform-provider-junos-qfx/netconf/client.go deleted file mode 100644 index 8036df9e..00000000 --- a/tf/providers/terraform-provider-junos-qfx/netconf/client.go +++ /dev/null @@ -1,538 +0,0 @@ -package netconf - -import ( - "bytes" - "context" - "encoding/xml" - "fmt" - "io" - "os" - "sort" - "strings" - "sync" - - "golang.org/x/crypto/ssh" - netconf "nemith.io/netconf" - netconfssh "nemith.io/netconf/transport/ssh" -) - -const groupStrXML = ` -%s - -` - -const deleteStr = ` - - - - none - - - - %s - - %s - - -` - -const commitStr = `` - -const getGroupXMLStr = ` - - %s - - -` - -const getConfigXMLStr = ` - - - -` - -const applyGroupXML = ` - %s - -` - -const discardChanges = `` - -const patchEditConfigStr = ` - - merge - -%s - -` - -// defaultPort is the NETCONF-over-SSH default. -const defaultPort = 830 - -type configuration struct { - ApplyGroup []string `xml:"apply-groups"` -} - -func debugRPC(label string, payload string) { - if os.Getenv("JUNOS_TF_DEBUG_RPC") == "" { - return - } - fmt.Printf("\n=== %s ===\n%s\n", label, payload) -} - -func marshalRPCRequest(operation string, messageID string) (string, error) { - rpc := netconf.NewRPC([]byte(operation)) - if messageID != "" { - rpc.MessageID = messageID - } - - rpcXML, err := xml.Marshal(rpc) - if err != nil { - return "", err - } - - return string(rpcXML), nil -} - -func isMissingDeleteError(err error) bool { - if err == nil { - return false - } - - errText := strings.ToLower(err.Error()) - return strings.Contains(errText, "data-missing") && strings.Contains(errText, "statement not found") -} - -type operationExecutor func(ctx context.Context, operation string) (string, error) - -// GoNCClient implements the provider-facing NETCONF client API on top of nemith/netconf. -type GoNCClient struct { - host string - port int - sshConfig *ssh.ClientConfig - - Lock sync.RWMutex - exec operationExecutor -} - -// Close keeps existing behavior contract for provider lifecycle hooks. -func (g *GoNCClient) Close() error { - return nil -} - -// execute sends a single NETCONF RPC and returns its inner XML payload. -func (g *GoNCClient) execute(ctx context.Context, operation string) (string, error) { - if g.exec != nil { - return g.exec(ctx, operation) - } - - target := fmt.Sprintf("%s:%d", g.host, g.port) - transport, err := netconfssh.Dial(ctx, "tcp", target, g.sshConfig) - if err != nil { - return "", err - } - - session, err := netconf.NewSession(transport) - if err != nil { - _ = transport.Close() - return "", err - } - defer func() { - _ = session.Close(context.Background()) - }() - - rpc := session.Prepare(netconf.NewRPC([]byte(operation))) - rpcXML, err := xml.Marshal(rpc) - if err != nil { - return "", fmt.Errorf("failed to marshal rpc request: %w", err) - } - debugRPC("rpc request", string(rpcXML)) - - msg, err := session.Do(ctx, rpc) - if err != nil { - return "", err - } - defer func() { - _ = msg.Close() - }() - - rawReply, err := io.ReadAll(msg) - if err != nil { - return "", fmt.Errorf("failed to read rpc-reply: %w", err) - } - debugRPC("rpc reply", string(rawReply)) - - if rerr := netconfReplyError(rawReply); rerr != nil { - return "", rerr - } - - reply := struct { - XMLName xml.Name `xml:"rpc-reply"` - Data string `xml:",innerxml"` - }{} - - if err := xml.Unmarshal(rawReply, &reply); err != nil { - return "", fmt.Errorf("failed to decode rpc-reply: %w", err) - } - - return reply.Data, nil -} - -// updateRawConfig replaces an existing apply-group payload and optionally commits. -func (g *GoNCClient) updateRawConfig(applyGroup string, netconfCall string, commit bool) (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - ctx := context.Background() - deleteString := fmt.Sprintf(deleteStr, applyGroup, applyGroup) - if _, err := g.execute(ctx, deleteString); err != nil { - if !isMissingDeleteError(err) { - return "", err - } - } - - nameStart := strings.Index(netconfCall, "") - nameEnd := strings.Index(netconfCall, "") - if nameStart == -1 || nameEnd == -1 { - return "", fmt.Errorf("failed to extract the group name from the netconfcall") - } - groupName := netconfCall[nameStart+6 : nameEnd] - addToApplyGroupsList(groupName) - - groupString := fmt.Sprintf(groupStrXML, netconfCall) - reply, err := g.execute(ctx, groupString) - if err != nil { - return "", err - } - - if commit { - if _, err := g.execute(ctx, commitStr); err != nil { - return "", err - } - } - - return reply, nil -} - -// DeleteConfig deletes the target apply-group and optionally commits. -func (g *GoNCClient) DeleteConfig(applyGroup string, commit bool) (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - ctx := context.Background() - deleteString := fmt.Sprintf(deleteStr, applyGroup, applyGroup) - reply, err := g.execute(ctx, deleteString) - if err != nil { - if !isMissingDeleteError(err) { - return "", err - } - reply = "" - } - - if commit { - if _, err := g.execute(ctx, commitStr); err != nil { - return "", err - } - } - - return strings.ReplaceAll(reply, "\n", ""), nil -} - -// SendCommit emits apply-groups in deterministic order and commits candidate config. -func (g *GoNCClient) SendCommit() error { - g.Lock.Lock() - defer g.Lock.Unlock() - - hasApplyGroups := false - applyGroupsMutex.Lock() - for _, group := range applyGroupsList { - if group != "" { - hasApplyGroups = true - break - } - } - applyGroupsMutex.Unlock() - - if hasApplyGroups { - sortApplyGroupsList() - if err := g.sendApplyGroupsLocked(context.Background()); err != nil { - return err - } - } - - if _, err := g.execute(context.Background(), commitStr); err != nil { - _, _ = g.execute(context.Background(), discardChanges) - return err - } - - return nil -} - -// sendApplyGroupsLocked emits the current apply-groups list as load-configuration XML. -func (g *GoNCClient) sendApplyGroupsLocked(ctx context.Context) error { - applyGroupsMutex.Lock() - applyGroupsCopy := make([]string, len(applyGroupsList)) - copy(applyGroupsCopy, applyGroupsList) - applyGroupsMutex.Unlock() - - var applyG configuration - applyG.ApplyGroup = applyGroupsCopy - - cfg, err := xml.Marshal(applyG) - if err != nil { - return err - } - - _, err = g.execute(ctx, fmt.Sprintf(applyGroupXML, string(cfg))) - return err -} - -// MarshalGroup fetches a group and unmarshals XML into obj. -func (g *GoNCClient) MarshalGroup(id string, obj interface{}) error { - reply, err := g.readRawGroup(id) - if err != nil { - return err - } - - if err = xml.Unmarshal([]byte(reply), &obj); err != nil { - return err - } - return nil -} - -// MarshalConfig fetches the full configuration and unmarshals XML into obj. -func (g *GoNCClient) MarshalConfig(obj interface{}) error { - reply, err := g.readRawConfig() - if err != nil { - return err - } - - if err = xml.Unmarshal([]byte(reply), &obj); err != nil { - return err - } - return nil -} - -var applyGroupsList []string -var applyGroupsMutex sync.Mutex - -// SendTransaction updates or creates a config payload and optionally commits it. -func (g *GoNCClient) SendTransaction(id string, obj interface{}, commit bool) error { - cfg, err := xml.Marshal(obj) - if err != nil { - return err - } - - if id != "" { - if _, err = g.updateRawConfig(id, string(cfg), commit); err != nil { - return err - } - return nil - } - - if _, err = g.sendRawConfig(string(cfg), commit); err != nil { - return err - } - return nil -} - -// SendDirectTransaction loads raw XML config directly without apply-groups wrapping. -func (g *GoNCClient) SendDirectTransaction(obj interface{}, commit bool) error { - cfg, err := xml.Marshal(obj) - if err != nil { - return err - } - - if _, err = g.sendDirectRawConfig(string(cfg), commit); err != nil { - return err - } - return nil -} - -// addToApplyGroupsList records a group ID for deferred apply-groups emission. -func addToApplyGroupsList(id string) { - applyGroupsMutex.Lock() - defer applyGroupsMutex.Unlock() - applyGroupsList = append(applyGroupsList, id) -} - -// sortApplyGroupsList removes empty values and keeps group ordering deterministic. -func sortApplyGroupsList() { - applyGroupsMutex.Lock() - defer applyGroupsMutex.Unlock() - - filteredGroups := make([]string, 0, len(applyGroupsList)) - for _, group := range applyGroupsList { - if group != "" { - filteredGroups = append(filteredGroups, group) - } - } - sort.Strings(filteredGroups) - applyGroupsList = filteredGroups -} - -// SendUpdate applies a prepared XML diff payload and optionally commits it. -func (g *GoNCClient) SendUpdate(id string, diff string, commit bool) error { - g.Lock.Lock() - defer g.Lock.Unlock() - _ = id - - patchPayload := fmt.Sprintf(patchEditConfigStr, diff) - if _, err := g.execute(context.Background(), patchPayload); err != nil { - return err - } - - if commit { - if _, err := g.execute(context.Background(), commitStr); err != nil { - return err - } - } - - return nil -} - -// sendRawConfig loads raw XML config and optionally commits it. -func (g *GoNCClient) sendRawConfig(netconfCall string, commit bool) (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - nameStart := strings.Index(netconfCall, "") - nameEnd := strings.Index(netconfCall, "") - if nameStart == -1 || nameEnd == -1 { - return "", fmt.Errorf("failed to extract the group name from the netconfCall") - } - groupName := netconfCall[nameStart+6 : nameEnd] - addToApplyGroupsList(groupName) - - reply, err := g.execute(context.Background(), fmt.Sprintf(groupStrXML, netconfCall)) - if err != nil { - return "", err - } - - if commit { - if _, err = g.execute(context.Background(), commitStr); err != nil { - return "", err - } - } - - return reply, nil -} - -// sendDirectRawConfig loads raw XML configuration without group bookkeeping. -func (g *GoNCClient) sendDirectRawConfig(netconfCall string, commit bool) (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - reply, err := g.execute(context.Background(), fmt.Sprintf(groupStrXML, netconfCall)) - if err != nil { - return "", err - } - - if commit { - if _, err = g.execute(context.Background(), commitStr); err != nil { - return "", err - } - } - - return reply, nil -} - -// readRawGroup fetches a single apply-group configuration payload. -func (g *GoNCClient) readRawGroup(applyGroup string) (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - return g.execute(context.Background(), fmt.Sprintf(getGroupXMLStr, applyGroup)) -} - -// readRawConfig fetches the full configuration payload. -func (g *GoNCClient) readRawConfig() (string, error) { - g.Lock.Lock() - defer g.Lock.Unlock() - - return g.execute(context.Background(), getConfigXMLStr) -} - -// publicKeyFile parses an SSH private key file into an auth method. -func publicKeyFile(file string) (ssh.AuthMethod, error) { - buffer, err := os.ReadFile(file) - if err != nil { - return nil, fmt.Errorf("reading SSH key %q: %w", file, err) - } - - key, err := ssh.ParsePrivateKey(buffer) - if err != nil { - return nil, fmt.Errorf("parsing SSH key %q (is it a valid private key?): %w", file, err) - } - return ssh.PublicKeys(key), nil -} - -// NewClient returns a NETCONF client backed by nemith/netconf. -func NewClient(username, password, sshKey, address string, port int) (Client, error) { - if port == 0 { - port = defaultPort - } - - cfg := &ssh.ClientConfig{ - User: username, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } - - if sshKey != "" { - authMethod, err := publicKeyFile(sshKey) - if err != nil { - return nil, err - } - cfg.Auth = []ssh.AuthMethod{authMethod} - } else { - cfg.Auth = []ssh.AuthMethod{ssh.Password(password)} - } - - return &GoNCClient{ - host: address, - port: port, - sshConfig: cfg, - }, nil -} - - -// netconfReplyError returns a non-nil error if the rpc-reply contains any -// error-severity at ANY depth. Junos nests them under -// (optionally per ) and -// , not just directly under . -func netconfReplyError(raw []byte) error { - dec := xml.NewDecoder(bytes.NewReader(raw)) - var msgs []string - for { - tok, err := dec.Token() - if err != nil { - break - } - se, ok := tok.(xml.StartElement) - if !ok || se.Name.Local != "rpc-error" { - continue - } - var e struct { - Severity string `xml:"error-severity"` - Message string `xml:"error-message"` - Path string `xml:"error-path"` - } - if dec.DecodeElement(&e, &se) != nil { - continue - } - if strings.EqualFold(strings.TrimSpace(e.Severity), "warning") { - continue - } - m := strings.TrimSpace(e.Message) - if p := strings.TrimSpace(e.Path); p != "" { - m = strings.TrimSpace(p) + ": " + m - } - if m != "" { - msgs = append(msgs, m) - } - } - if len(msgs) > 0 { - return fmt.Errorf("device rejected the change: %s", strings.Join(msgs, "; ")) - } - return nil -} diff --git a/tf/providers/terraform-provider-junos-qfx/netconf/client_test.go b/tf/providers/terraform-provider-junos-qfx/netconf/client_test.go deleted file mode 100644 index eb5ee280..00000000 --- a/tf/providers/terraform-provider-junos-qfx/netconf/client_test.go +++ /dev/null @@ -1,560 +0,0 @@ -package netconf - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "encoding/xml" - "errors" - "os" - "path/filepath" - "strings" - "sync" - "testing" - "time" - - "golang.org/x/crypto/ssh" -) - -// newMockClient creates a client with an injected RPC executor for deterministic tests. -func newMockClient(calls *[]string, ret string, err error) *GoNCClient { - return &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - *calls = append(*calls, op) - return ret, err - }, - } -} - -// TestDeleteConfigCallsExpectedOperations verifies delete then commit RPC sequencing. -func TestDeleteConfigCallsExpectedOperations(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - - _, err := client.DeleteConfig("base-config", true) - if err != nil { - t.Fatalf("DeleteConfig returned error: %v", err) - } - - if len(calls) != 2 { - t.Fatalf("expected 2 operations, got %d", len(calls)) - } - if !strings.Contains(calls[0], "") { - t.Fatalf("first call should be edit-config, got %q", calls[0]) - } - if strings.TrimSpace(calls[1]) != commitStr { - t.Fatalf("second call should be commit, got %q", calls[1]) - } -} - -// TestSendUpdateBaseConfigPayload verifies patch updates do not require group name tags. -func TestSendUpdateBaseConfigPayload(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - - diff := `leaf1` - if err := client.SendUpdate("base-config", diff, false); err != nil { - t.Fatalf("SendUpdate returned error: %v", err) - } - - if len(calls) != 1 { - t.Fatalf("expected single edit-config operation, got %d", len(calls)) - } - if !strings.Contains(calls[0], "") || !strings.Contains(calls[0], "merge") { - t.Fatalf("expected patch edit-config envelope, got %q", calls[0]) - } -} - -// TestMarshalRPCRequestUsesInnerXML verifies patch requests are wrapped in -// while the operation body remains raw XML, not wrapper fields. -func TestMarshalRPCRequestUsesInnerXML(t *testing.T) { - rpcXML, err := marshalRPCRequest("", "1") - if err != nil { - t.Fatalf("marshalRPCRequest() returned error: %v", err) - } - - if !strings.Contains(rpcXML, ``) { - t.Fatalf("expected rpc envelope, got %q", rpcXML) - } - if !strings.Contains(rpcXML, "") { - t.Fatalf("expected edit-config payload in rpc, got %q", rpcXML) - } - if strings.Contains(rpcXML, "") || strings.Contains(rpcXML, "") { - t.Fatalf("expected raw payload without wrapper element, got %q", rpcXML) - } -} - -// TestSendTransactionWithIDReplacesGroup verifies ID-based transactions use update flow. -func TestSendTransactionWithIDReplacesGroup(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - - obj := struct { - XMLName struct{} `xml:"configuration"` - Groups struct { - Name string `xml:"name"` - } `xml:"groups"` - }{} - obj.Groups.Name = "base-config" - - if err := client.SendTransaction("base-config", obj, false); err != nil { - t.Fatalf("SendTransaction returned error: %v", err) - } - - if len(calls) != 2 { - t.Fatalf("expected 2 operations for update flow, got %d", len(calls)) - } - if !strings.Contains(calls[0], "operation=\"delete\"") { - t.Fatalf("expected delete operation first, got %q", calls[0]) - } - if !strings.Contains(calls[1], "", nil - }, - } - - applyGroupsList = []string{"b", "a"} - err := client.SendCommit() - if err == nil { - t.Fatal("expected commit error") - } - - if len(calls) < 3 { - t.Fatalf("expected apply-groups, commit, discard operations, got %d", len(calls)) - } - if strings.TrimSpace(calls[len(calls)-1]) != discardChanges { - t.Fatalf("expected discard-changes after commit failure, got %q", calls[len(calls)-1]) - } -} - -// TestNewClientAllowsMissingSSHKeyPath verifies client creation tolerates unreadable key paths. -func TestNewClientAllowsMissingSSHKeyPath(t *testing.T) { - client, err := NewClient("user", "", "/does/not/exist", "127.0.0.1", 830) - if err != nil { - t.Fatalf("expected no error for invalid ssh key path, got: %v", err) - } - if client == nil { - t.Fatal("expected non-nil client") - } -} - -// TestGoNCClientCloseNoop verifies Close preserves no-op behavior. -func TestGoNCClientCloseNoop(t *testing.T) { - client := &GoNCClient{} - if err := client.Close(); err != nil { - t.Fatalf("expected nil close error, got: %v", err) - } -} - -// TestUpdateRawConfigMissingName verifies group name extraction failures are surfaced. -func TestUpdateRawConfigMissingName(t *testing.T) { - client := newMockClient(&[]string{}, "", nil) - _, err := client.updateRawConfig("group", "", false) - if err == nil || !strings.Contains(err.Error(), "failed to extract") { - t.Fatalf("expected extract error, got: %v", err) - } -} - -// TestUpdateRawConfigIgnoresMissingDelete verifies initial group creation tolerates missing-group deletes. -func TestUpdateRawConfigIgnoresMissingDelete(t *testing.T) { - calls := []string{} - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - calls = append(calls, op) - if strings.Contains(op, "operation=\"delete\"") { - return "", errors.New("multiple netconf errors: netconf error: application data-missing: statement not found") - } - return "", nil - }, - } - - _, err := client.updateRawConfig("group", "group", false) - if err != nil { - t.Fatalf("expected missing delete to be ignored, got: %v", err) - } - if len(calls) != 2 { - t.Fatalf("expected delete then load calls, got %d", len(calls)) - } -} - -// TestSendRawConfigCommitFlow verifies raw config load followed by commit. -func TestSendRawConfigCommitFlow(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - applyGroupsList = nil - - reply, err := client.sendRawConfig("z-group", true) - if err != nil { - t.Fatalf("sendRawConfig() returned error: %v", err) - } - if reply != "" { - t.Fatalf("unexpected reply: %q", reply) - } - if len(calls) != 2 { - t.Fatalf("expected load and commit calls, got: %d", len(calls)) - } - if strings.TrimSpace(calls[1]) != commitStr { - t.Fatalf("expected commit as second call, got %q", calls[1]) - } -} - -// TestSendRawConfigMissingName verifies missing group names return an error. -func TestSendRawConfigMissingName(t *testing.T) { - client := newMockClient(&[]string{}, "", nil) - _, err := client.sendRawConfig("", false) - if err == nil || !strings.Contains(err.Error(), "failed to extract") { - t.Fatalf("expected extract error, got: %v", err) - } -} - -// TestReadRawGroupUsesGetConfigRPC verifies group reads use get-configuration RPC. -func TestReadRawGroupUsesGetConfigRPC(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - - reply, err := client.readRawGroup("base-config") - if err != nil { - t.Fatalf("readRawGroup() returned error: %v", err) - } - if reply != "" { - t.Fatalf("unexpected reply: %q", reply) - } - if len(calls) != 1 || !strings.Contains(calls[0], "") { - t.Fatalf("expected get-configuration call, got %#v", calls) - } -} - -// TestMarshalGroupSuccessAndError verifies XML unmarshalling success and failure paths. -func TestMarshalGroupSuccessAndError(t *testing.T) { - t.Run("success", func(t *testing.T) { - calls := []string{} - reply := `g1` - client := newMockClient(&calls, reply, nil) - - var out struct { - XMLName xml.Name `xml:"configuration"` - Groups struct { - Name string `xml:"name"` - } `xml:"groups"` - } - - if err := client.MarshalGroup("g1", &out); err != nil { - t.Fatalf("MarshalGroup() returned error: %v", err) - } - if out.Groups.Name != "g1" { - t.Fatalf("unexpected group name: %q", out.Groups.Name) - } - }) - - t.Run("invalid xml", func(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, `", nil) - applyGroupsList = nil - - obj := struct { - XMLName xml.Name `xml:"configuration"` - Groups struct { - Name string `xml:"name"` - } `xml:"groups"` - }{} - obj.Groups.Name = "group-a" - - if err := client.SendTransaction("", obj, false); err != nil { - t.Fatalf("SendTransaction() error: %v", err) - } - if len(calls) != 1 || !strings.Contains(calls[0], "") - if err == nil { - t.Fatalf("expected network execute to fail") - } -} - -// TestUpdateRawConfigCommitAndErrorBranches verifies update commit and error branches. -func TestUpdateRawConfigCommitAndErrorBranches(t *testing.T) { - t.Run("commit true success", func(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - applyGroupsList = nil - - reply, err := client.updateRawConfig("grp", "grp", true) - if err != nil { - t.Fatalf("updateRawConfig() error: %v", err) - } - if reply != "" { - t.Fatalf("unexpected reply: %q", reply) - } - if len(calls) != 3 { - t.Fatalf("expected 3 calls (delete, load, commit), got %d", len(calls)) - } - }) - - t.Run("delete error", func(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - if strings.Contains(op, "operation=\"delete\"") { - return "", errors.New("delete failed") - } - return "", nil - }, - } - _, err := client.updateRawConfig("grp", "grp", false) - if err == nil { - t.Fatalf("expected delete error") - } - }) - - t.Run("commit error", func(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - if strings.TrimSpace(op) == commitStr { - return "", errors.New("commit failed") - } - return "", nil - }, - } - _, err := client.updateRawConfig("grp", "grp", true) - if err == nil { - t.Fatalf("expected commit error") - } - }) -} - -// TestDeleteConfigBranches verifies non-commit and commit-error delete behavior. -func TestDeleteConfigBranches(t *testing.T) { - t.Run("without commit", func(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "\n", nil) - reply, err := client.DeleteConfig("grp", false) - if err != nil { - t.Fatalf("DeleteConfig() error: %v", err) - } - if reply != "" { - t.Fatalf("expected newline-stripped reply, got %q", reply) - } - if len(calls) != 1 { - t.Fatalf("expected single delete call, got %d", len(calls)) - } - }) - - t.Run("commit error", func(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - if strings.TrimSpace(op) == commitStr { - return "", errors.New("commit failed") - } - return "", nil - }, - } - _, err := client.DeleteConfig("grp", true) - if err == nil { - t.Fatalf("expected commit error") - } - }) - - t.Run("missing group delete", func(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, _ string) (string, error) { - return "", errors.New("netconf error: application data-missing: statement not found") - }, - } - - reply, err := client.DeleteConfig("grp", false) - if err != nil { - t.Fatalf("expected missing-group delete to be ignored, got: %v", err) - } - if reply != "" { - t.Fatalf("expected synthetic ok reply, got %q", reply) - } - }) -} - -// TestSendCommitSuccessAndApplyGroupError verifies commit success and apply-group RPC failure behavior. -func TestSendCommitSuccessAndApplyGroupError(t *testing.T) { - t.Run("success", func(t *testing.T) { - calls := []string{} - client := newMockClient(&calls, "", nil) - applyGroupsList = []string{"z", "", "a"} - - if err := client.SendCommit(); err != nil { - t.Fatalf("SendCommit() error: %v", err) - } - if len(calls) < 2 { - t.Fatalf("expected apply-group load then commit calls, got %d", len(calls)) - } - if !strings.Contains(calls[0], "a") || !strings.Contains(calls[0], "z") { - t.Fatalf("expected sorted apply groups in RPC, got %q", calls[0]) - } - }) - - t.Run("apply-group load error", func(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, op string) (string, error) { - if strings.Contains(op, "", nil - }, - } - applyGroupsList = []string{"x"} - if err := client.SendCommit(); err == nil { - t.Fatalf("expected sendApplyGroupsLocked error") - } - }) -} - -// TestMarshalGroupReadError verifies read errors are propagated by MarshalGroup. -func TestMarshalGroupReadError(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, _ string) (string, error) { - return "", errors.New("read failed") - }, - } - var out struct{} - if err := client.MarshalGroup("x", &out); err == nil { - t.Fatalf("expected read error") - } -} - -// TestSendRawConfigExecuteError verifies RPC execution errors are returned. -func TestSendRawConfigExecuteError(t *testing.T) { - client := &GoNCClient{ - Lock: sync.RWMutex{}, - exec: func(_ context.Context, _ string) (string, error) { - return "", errors.New("rpc failed") - }, - } - _, err := client.sendRawConfig("g", false) - if err == nil { - t.Fatalf("expected rpc error") - } -} - -// TestNewClientWithValidSSHKey verifies SSH key auth path setup with a valid key. -func TestNewClientWithValidSSHKey(t *testing.T) { - key, err := rsa.GenerateKey(rand.Reader, 1024) - if err != nil { - t.Fatalf("failed generating rsa key: %v", err) - } - keyDER := x509.MarshalPKCS1PrivateKey(key) - keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyDER}) - - keyPath := filepath.Join(t.TempDir(), "id_rsa") - if err := os.WriteFile(keyPath, keyPEM, 0600); err != nil { - t.Fatalf("failed writing key: %v", err) - } - - client, err := NewClient("user", "", keyPath, "127.0.0.1", 830) - if err != nil { - t.Fatalf("NewClient() error: %v", err) - } - gonc := client.(*GoNCClient) - if len(gonc.sshConfig.Auth) != 1 { - t.Fatalf("expected one auth method") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/netconf/common.go b/tf/providers/terraform-provider-junos-qfx/netconf/common.go deleted file mode 100644 index 53eda35c..00000000 --- a/tf/providers/terraform-provider-junos-qfx/netconf/common.go +++ /dev/null @@ -1,12 +0,0 @@ -package netconf - -type Client interface { - Close() error - DeleteConfig(applyGroup string, commit bool) (string, error) - SendCommit() error - MarshalGroup(id string, obj interface{}) error - MarshalConfig(obj interface{}) error - SendTransaction(id string, obj interface{}, commit bool) error - SendDirectTransaction(obj interface{}, commit bool) error - SendUpdate(id string, diff string, commit bool) error -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/corner_case_test.go b/tf/providers/terraform-provider-junos-qfx/patch/corner_case_test.go deleted file mode 100644 index f8ba4a0c..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/corner_case_test.go +++ /dev/null @@ -1,810 +0,0 @@ -package patch - -import ( - "strings" - "testing" -) - -// --------------------------------------------------------------------------- -// CC-10: Special characters in key values -// --------------------------------------------------------------------------- - -func TestCC10_ParseSegment_KeyWithSlashes(t *testing.T) { - // Interface names like ge-0/0/0 are the common case — already inside brackets - tag, keyName, keyValue := parseSegment("interface[name=ge-0/0/0]") - if tag != "interface" || keyName != "name" || keyValue != "ge-0/0/0" { - t.Errorf("got tag=%q key=%q val=%q", tag, keyName, keyValue) - } -} - -func TestCC10_ParseSegment_KeyWithNestedBrackets(t *testing.T) { - // Policy name containing brackets: "ALLOW[ALL]" - tag, keyName, keyValue := parseSegment("policy[name=ALLOW[ALL]]") - if tag != "policy" { - t.Errorf("expected tag=policy, got %q", tag) - } - if keyName != "name" { - t.Errorf("expected keyName=name, got %q", keyName) - } - if keyValue != "ALLOW[ALL]" { - t.Errorf("expected keyValue=ALLOW[ALL], got %q", keyValue) - } -} - -func TestCC10_ParseSegment_KeyWithEquals(t *testing.T) { - // Route key containing equals: "prefix=10.0.0.0/8" - tag, keyName, keyValue := parseSegment("route[prefix=10.0.0.0/8]") - if tag != "route" || keyName != "prefix" || keyValue != "10.0.0.0/8" { - t.Errorf("got tag=%q key=%q val=%q", tag, keyName, keyValue) - } -} - -func TestCC10_SplitPath_KeyWithSlashes(t *testing.T) { - // Verify path splitting handles keys with slashes correctly - path := "interfaces/interface[name=ge-0/0/0]/unit[name=0]/description" - segments := splitPathRespectingQuotes(path) - expected := []string{"interfaces", "interface[name=ge-0/0/0]", "unit[name=0]", "description"} - if len(segments) != len(expected) { - t.Fatalf("expected %d segments, got %d: %v", len(expected), len(segments), segments) - } - for i, seg := range segments { - if seg != expected[i] { - t.Errorf("segment[%d]: expected %q got %q", i, expected[i], seg) - } - } -} - -// --------------------------------------------------------------------------- -// CC-5: Container delete coalescing -// --------------------------------------------------------------------------- - -// CC-5 schema covers system/ntp with two list-entries and a leaf-list -const cc5Schema = `{ - "path": "", - "root": { - "children": [ - { - "name": "configuration", - "type": "container", - "path": "", - "children": [ - { - "name": "system", - "type": "container", - "path": "", - "children": [ - { - "name": "host-name", - "type": "leaf", - "path": "system", - "leaf-type": "string" - }, - { - "name": "ntp", - "type": "container", - "path": "system", - "children": [ - { - "name": "server", - "type": "list", - "path": "system/ntp", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "system/ntp/server", - "leaf-type": "string" - }, - { - "name": "routing-instance", - "type": "leaf", - "path": "system/ntp/server", - "leaf-type": "string" - } - ] - }, - { - "name": "trusted-key", - "type": "leaf-list", - "path": "system/ntp", - "leaf-type": "string" - } - ] - } - ] - } - ] - } - ] - } -}` - -func TestCC5_ContainerDeleteCoalescing(t *testing.T) { - // When ALL children of a container are deleted, the patch should ideally - // emit a single container-level delete. Currently, the engine emits - // individual per-leaf deletes. - - stateXML := ` - - router1 - - 10.0.0.1mgmt - 10.0.0.2mgmt - 1 - 2 - - -` - - // Plan: ntp entirely removed, host-name stays - planXML := ` - - router1 - -` - - idx := mustIdxFromSchema(t, cc5Schema) - - stateTree, err := BuildTree([]byte(stateXML)) - if err != nil { - t.Fatal(err) - } - planTree, err := BuildTree([]byte(planXML)) - if err != nil { - t.Fatal(err) - } - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - diffMap := ComputeDiff(stateMap, planMap) - - if len(diffMap) == 0 { - t.Fatal("expected non-empty diff") - } - - // Use schema-aware patch (with container coalescing) - patchBytes, err := CreateDiffPatchWithSchema(diffMap, "", idx) - if err != nil { - t.Fatal(err) - } - - output := string(patchBytes) - t.Logf("CC-5 patch output:\n%s", output) - - // Verify: the patch should contain a single container-level delete for ntp - if !strings.Contains(output, "delete") { - t.Error("expected delete operations in patch output") - } - - // Check that host-name is NOT deleted (it's in both state and plan) - if strings.Contains(output, "host-name") { - t.Error("host-name should not appear in patch (unchanged)") - } - - // Track whether coalescing is happening - if strings.Contains(output, ` - - - ge-0/0/0 - uplink - - -` - - planXML := ` - - - ge-0/0/0 - uplink - - - -` - - idx := mustIdxFromSchema(t, cc2Schema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - - // Verify the leaf map correctly represents the empty leaf - disableKey := "" - for k, v := range planMap { - if strings.HasSuffix(k, "/disable") { - disableKey = k - t.Logf("planMap disable: %q = %q", k, v) - } - } - if disableKey == "" { - t.Fatal("disable leaf not found in plan map") - } - - // State should NOT have disable - for k := range stateMap { - if strings.HasSuffix(k, "/disable") { - t.Fatal("disable should not be in state map") - } - } - - diffMap := ComputeDiff(stateMap, planMap) - patchBytes, err := CreateDiffPatch(diffMap, "") - if err != nil { - t.Fatal(err) - } - - output := string(patchBytes) - t.Logf("CC-2 create output:\n%s", output) - - if !strings.Contains(output, "disable") { - t.Error("expected disable in patch output") - } - if !strings.Contains(output, `nc:operation="merge"`) { - t.Error("expected create operation for disable") - } -} - -func TestCC2_EmptyLeafDelete(t *testing.T) { - // Remove disable from an interface - stateXML := ` - - - ge-0/0/0 - uplink - - - -` - - planXML := ` - - - ge-0/0/0 - uplink - - -` - - idx := mustIdxFromSchema(t, cc2Schema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - - // State SHOULD have disable - found := false - for k := range stateMap { - if strings.HasSuffix(k, "/disable") { - found = true - break - } - } - if !found { - t.Fatal("disable should be in state map") - } - - diffMap := ComputeDiff(stateMap, planMap) - patchBytes, err := CreateDiffPatch(diffMap, "") - if err != nil { - t.Fatal(err) - } - - output := string(patchBytes) - t.Logf("CC-2 delete output:\n%s", output) - - if !strings.Contains(output, "disable") { - t.Error("expected disable in patch output") - } - if !strings.Contains(output, `nc:operation="delete"`) { - t.Error("expected delete operation for disable") - } - // Empty leaf delete should NOT have text content - if strings.Contains(output, ``) { - // Check if there's text between tags - if strings.Contains(output, ``) { - t.Log("CC-2 NOTE: empty tags (OK)") - } - } -} - -// --------------------------------------------------------------------------- -// CC-3: Leaf-list full replacement vs incremental -// --------------------------------------------------------------------------- - -func TestCC3_LeafListBulkChange(t *testing.T) { - // Community members change from [A, B, C] to [A, D, E] - stateXML := ` - - - OC-STD - 65000:100 - 65000:200 - 65000:300 - - -` - - planXML := ` - - - OC-STD - 65000:100 - 65000:400 - 65000:500 - - -` - - idx := mustIdxFromSchema(t, matrixSchema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - diffMap := ComputeDiff(stateMap, planMap) - - patchBytes, err := CreateDiffPatch(diffMap, "") - if err != nil { - t.Fatal(err) - } - - output := string(patchBytes) - t.Logf("CC-3 leaf-list bulk change output:\n%s", output) - - // Verify: 65000:200 and 65000:300 should be deleted, 65000:400 and 65000:500 created - if !strings.Contains(output, "65000:200") { - t.Error("expected 65000:200 delete") - } - if !strings.Contains(output, "65000:300") { - t.Error("expected 65000:300 delete") - } - if !strings.Contains(output, "65000:400") { - t.Error("expected 65000:400 create") - } - if !strings.Contains(output, "65000:500") { - t.Error("expected 65000:500 create") - } - // 65000:100 should NOT appear (unchanged) - if strings.Contains(output, "65000:100") { - t.Error("65000:100 should not be in patch (unchanged)") - } -} - -// --------------------------------------------------------------------------- -// CC-1: Ordered leaf-list reorder detection -// --------------------------------------------------------------------------- - -const cc1Schema = `{ - "path": "", - "root": { - "children": [ - { - "name": "configuration", - "type": "container", - "path": "", - "children": [ - { - "name": "interfaces", - "type": "container", - "path": "", - "children": [ - { - "name": "interface", - "type": "list", - "path": "interfaces", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface", - "leaf-type": "string" - }, - { - "name": "unit", - "type": "list", - "path": "interfaces/interface", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface/unit", - "leaf-type": "string" - }, - { - "name": "family", - "type": "container", - "path": "interfaces/interface/unit", - "children": [ - { - "name": "inet", - "type": "container", - "path": "interfaces/interface/unit/family", - "children": [ - { - "name": "address", - "type": "list", - "path": "interfaces/interface/unit/family/inet", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface/unit/family/inet/address", - "leaf-type": "string" - }, - { - "name": "vrrp-group", - "type": "list", - "path": "interfaces/interface/unit/family/inet/address", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface/unit/family/inet/address/vrrp-group", - "leaf-type": "string" - }, - { - "name": "virtual-address", - "type": "leaf-list", - "path": "interfaces/interface/unit/family/inet/address/vrrp-group", - "leaf-type": "string", - "ordered-by": "user" - }, - { - "name": "priority", - "type": "leaf", - "path": "interfaces/interface/unit/family/inet/address/vrrp-group", - "leaf-type": "string" - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } -}` - -func TestCC1_OrderedLeafListReorder(t *testing.T) { - // VRRP virtual-address is ordered-by user — reorder should be detected - stateXML := ` - - - ge-0/0/0 - - 0 - - -
- 10.0.0.1/24 - - 1 - 10.0.0.10 - 10.0.0.20 - 200 - -
-
-
-
-
-
-
` - - // Reorder: swap virtual-address order - planXML := ` - - - ge-0/0/0 - - 0 - - -
- 10.0.0.1/24 - - 1 - 10.0.0.20 - 10.0.0.10 - 200 - -
-
-
-
-
-
-
` - - idx := mustIdxFromSchema(t, cc1Schema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - - t.Logf("State map entries:") - for k, v := range stateMap { - if strings.Contains(k, "virtual") { - t.Logf(" %s = %q", k, v) - } - } - t.Logf("Plan map entries:") - for k, v := range planMap { - if strings.Contains(k, "virtual") { - t.Logf(" %s = %q", k, v) - } - } - - diffMap := ComputeDiff(stateMap, planMap) - - if len(diffMap) == 0 { - t.Log("CC-1 CONFIRMED: Reorder produces EMPTY diff (order not tracked)") - t.Log("CC-1 STATUS: NOT COVERED — ordered leaf-lists need position-aware diff") - } else { - t.Logf("CC-1 diff has %d entries — reorder IS detected", len(diffMap)) - for k, v := range diffMap { - t.Logf(" %s: op=%d old=%q new=%q", k, v.Op, v.OldVal, v.NewVal) - } - } -} - -// --------------------------------------------------------------------------- -// CC-4: Nested list entry addition with mandatory leaves -// --------------------------------------------------------------------------- - -func TestCC4_NestedListEntryCreation(t *testing.T) { - // Add a completely new interface with nested unit/family/address - stateXML := ` - - - ge-0/0/0 - existing - - -` - - planXML := ` - - - ge-0/0/0 - existing - - - ge-0/0/1 - new-link - - -` - - idx := mustIdxFromSchema(t, matrixSchema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - diffMap := ComputeDiff(stateMap, planMap) - - patchBytes, err := CreateDiffPatch(diffMap, "") - if err != nil { - t.Fatal(err) - } - - output := string(patchBytes) - t.Logf("CC-4 nested list entry output:\n%s", output) - - // New interface should have create operation on parent - if !strings.Contains(output, "ge-0/0/1") { - t.Error("expected ge-0/0/1 in output") - } - if !strings.Contains(output, "new-link") { - t.Error("expected description new-link in output") - } - // Verify the new entry has the create operation - if !strings.Contains(output, "merge") { - t.Error("expected create operation for new list entry") - } - // Existing interface should NOT appear (unchanged) - if strings.Contains(output, "ge-0/0/0") { - t.Error("ge-0/0/0 should not be in patch (unchanged)") - } -} - -// --------------------------------------------------------------------------- -// CC-6: UTF-8 normalization -// --------------------------------------------------------------------------- - -func TestCC6_UTF8DiffNoFalsePositive(t *testing.T) { - // State and plan both have em-dash — should produce no diff - stateXML := ` - - - ge-0/0/0 - Uplink — Core Router - - -` - - planXML := ` - - - ge-0/0/0 - Uplink — Core Router - - -` - - idx := mustIdxFromSchema(t, matrixSchema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - diffMap := ComputeDiff(stateMap, planMap) - - if len(diffMap) != 0 { - t.Errorf("CC-6: Expected empty diff for identical UTF-8 content, got %d entries", len(diffMap)) - for k, v := range diffMap { - t.Logf(" %s: op=%d old=%q new=%q", k, v.Op, v.OldVal, v.NewVal) - } - } else { - t.Log("CC-6 PASSED: Identical UTF-8 strings produce no diff") - } -} - -func TestCC6_UTF8DiffWithEncodingVariation(t *testing.T) { - // Simulate encoding variation: em-dash as — vs UTF-8 literal - stateXML := ` - - - ge-0/0/0 - Uplink — Core - - -` - - planXML := ` - - - ge-0/0/0 - Uplink — Core - - -` - - idx := mustIdxFromSchema(t, matrixSchema) - - stateTree, _ := BuildTree([]byte(stateXML)) - planTree, _ := BuildTree([]byte(planXML)) - - stateMap := LeafMapWithSchema(stateTree, idx) - planMap := LeafMapWithSchema(planTree, idx) - diffMap := ComputeDiff(stateMap, planMap) - - if len(diffMap) == 0 { - t.Log("CC-6 PASSED: XML entity reference (—) and literal em-dash produce same string after XML decode — no false diff") - } else { - t.Log("CC-6 NOTE: XML entity vs literal character produces diff (may need normalization)") - for k, v := range diffMap { - t.Logf(" %s: op=%d old=%q new=%q", k, v.Op, v.OldVal, v.NewVal) - } - } -} - -func TestCC6_DoubleEncodedUTF8Repair(t *testing.T) { - // Simulate the actual double-encoding seen on vpaa: em-dash bytes - // misinterpreted as Latin-1 and re-encoded to UTF-8 - // Original: "Uplink — Core" (em-dash U+2014 = E2 80 94 in UTF-8) - // Double-encoded: each byte treated as Latin-1 code point: - // E2 → U+00E2 (â) → C3 A2 - // 80 → U+0080 → C2 80 - // 94 → U+0094 → C2 94 - doubleEncoded := "Uplink \u00e2\u0080\u0094 Core" // This is what double-encoding produces - original := "Uplink \xe2\x80\x94 Core" // Em-dash as UTF-8 bytes (but stored as string — same as —) - - // Using NormalizeLeafMapUTF8 to repair - m := map[string]string{"test": doubleEncoded} - normalized := NormalizeLeafMapUTF8(m) - - if normalized["test"] == original { - t.Log("CC-6 PASSED: Double-encoded UTF-8 repaired to original") - } else { - t.Logf("CC-6 FAILED: normalized=%q expected=%q", normalized["test"], original) - t.Logf(" double-encoded bytes: %x", []byte(doubleEncoded)) - t.Logf(" original bytes: %x", []byte(original)) - t.Logf(" normalized bytes: %x", []byte(normalized["test"])) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/diff.go b/tf/providers/terraform-provider-junos-qfx/patch/diff.go deleted file mode 100644 index 0cfc5cdf..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/diff.go +++ /dev/null @@ -1,113 +0,0 @@ -package patch - -import ( - "unicode/utf8" -) - -// NormalizeLeafMapUTF8 creates a copy of the leaf map with all string values -// sanitized: double-encoded UTF-8 sequences (where UTF-8 bytes were -// misinterpreted as Latin-1 and re-encoded) are repaired back to their -// original form. This prevents false diffs caused by encoding round-trip -// issues between Junos NETCONF responses and Go's xml.Marshal/Unmarshal. -func NormalizeLeafMapUTF8(m map[string]string) map[string]string { - result := make(map[string]string, len(m)) - for k, v := range m { - result[k] = repairDoubleEncodedUTF8(v) - } - return result -} - -// repairDoubleEncodedUTF8 detects and repairs strings where UTF-8 bytes were -// misinterpreted as Latin-1 (ISO-8859-1) and then re-encoded to UTF-8. -// For example, em-dash U+2014 (UTF-8: E2 80 94) becomes "â\x80\x94" -// when double-encoded. This function reverses that transformation. -func repairDoubleEncodedUTF8(s string) string { - // Quick check: if the string contains any rune in the C2-F4 range - // (UTF-8 lead bytes when misread as Latin-1 code points), it might - // be double-encoded. Also check for control chars (0x80-0x9F) which - // appear as raw runes when UTF-8 continuation bytes are misread. - hasDoubleEncodeSignal := false - for _, r := range s { - if (r >= 0x80 && r <= 0x9F) || (r >= 0xC0 && r <= 0xF4) { - hasDoubleEncodeSignal = true - break - } - } - if !hasDoubleEncodeSignal { - return s - } - - // Try to decode: treat each rune as a byte value (Latin-1 → byte) - // and see if the resulting byte sequence is valid UTF-8 - bytes := make([]byte, 0, len(s)) - for _, r := range s { - if r > 0xFF { - // Rune above Latin-1 range — not double-encoded - return s - } - bytes = append(bytes, byte(r)) - } - - if utf8.Valid(bytes) { - repaired := string(bytes) - // Sanity check: repaired string should be shorter (fewer bytes) - if len(repaired) < len(s) { - return repaired - } - } - - return s -} - -// ComputeDiff compares stateMap (what is currently on the device) with -// planMap (what Terraform wants it to be) and returns a map of leaf paths -// to their required CRUD operation. -// -// Rules: -// - Path in state only → Delete (remove it from the device) -// - Path in both, values differ → Replace (update the existing value) -// - Path in plan only → Create (add new leaf to the device) -// - Path in both, values identical → omitted (no change needed) -func ComputeDiff(stateMap, planMap map[string]string) map[string]Change { - diff := make(map[string]Change) - - // First pass: iterate state — find deletions and replacements - for path, stateVal := range stateMap { - if planVal, exists := planMap[path]; !exists { - diff[path] = Change{Op: Delete, OldVal: stateVal, NewVal: ""} - } else if planVal != stateVal { - diff[path] = Change{Op: Replace, OldVal: stateVal, NewVal: planVal} - } - // Values match — no change, do not add to diff - } - - // Second pass: iterate plan — find creations - for path, planVal := range planMap { - if _, exists := stateMap[path]; !exists { - diff[path] = Change{Op: Create, OldVal: "", NewVal: planVal} - } - } - - return diff -} - -type DebugChange struct { - Path string - Op ChangeType - OldVal string - NewVal string -} - -func DebugSortedChanges(diffMap map[string]Change) []DebugChange { - ordered := orderedChanges(diffMap) - result := make([]DebugChange, 0, len(ordered)) - for _, entry := range ordered { - result = append(result, DebugChange{ - Path: entry.path, - Op: entry.change.Op, - OldVal: entry.change.OldVal, - NewVal: entry.change.NewVal, - }) - } - return result -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/leafmap.go b/tf/providers/terraform-provider-junos-qfx/patch/leafmap.go deleted file mode 100644 index 8c795ed4..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/leafmap.go +++ /dev/null @@ -1,280 +0,0 @@ -package patch - -import ( - "fmt" - "strings" -) - -// junosListKeys contains the YANG list key element names common in Junos. -// "name" covers ~95% of cases (interfaces, units, BGP groups, policies, etc.). -// "id" and "type" handle a small number of edge-case list definitions. -var junosListKeys = map[string]bool{ - "name": true, - "id": true, - "type": true, -} - -// LeafMapWithSchema flattens an XML tree using schema-derived list keys and -// node kinds from trimmed_schema metadata. -// -// Behavior: -// - list identity is derived from schema list key (not hardcoded key names) -// - leaf-list entries are represented as distinct set elements by appending -// [value=] to the path segment, enabling add/remove diff semantics -// - key leaf children are excluded from emitted leaves -func LeafMapWithSchema(root *Node, idx map[string]*NodeInfo) map[string]string { - result := make(map[string]string) - leafMapRecurseWithSchema(root, "", result, idx) - return result -} - -func leafMapRecurseWithSchema(node *Node, parentPath string, result map[string]string, idx map[string]*NodeInfo) { - schemaPath := outputPathToSchemaPath(parentPath) - segment := buildSegmentWithSchema(node, schemaPath, idx) - - currentPath := segment - if parentPath != "" { - currentPath = parentPath + "/" + segment - } - - if len(node.Children) == 0 { - // Skip empty containers/lists — they have no leaf content to diff. - // Only emit actual leaves (YANG "empty" type like , - // or regular text leaves). - leafSchemaPath := outputPathToSchemaPath(currentPath) - if info, ok := idx[leafSchemaPath]; ok { - if info.Kind == KindContainer || info.Kind == KindList { - return - } - if info.Kind == KindLeafList { - currentPath = currentPath + fmt.Sprintf("[value=%s]", node.Text) - } - } else if node.Text == "" { - // Element not in schema and has no text — likely an empty - // container or unrecognised element; skip it. - return - } - - result[currentPath] = node.Text - return - } - - if keyPath, keyValue, ok := structuralKeyedListLeaf(node, currentPath, idx); ok { - result[keyPath] = keyValue - return - } - - // Process ALL children including key children. Key leaves must appear - // in the leaf map so ComputeDiff can detect new/removed list entries - // (where the key leaf is the diff signal for entry-level operations). - // - // For ordered-by-user leaf-lists, track position per leaf-list tag so that - // reordering produces Replace diffs rather than being invisible. - orderedCounters := make(map[string]int) // tag -> next position - for _, child := range node.Children { - childSchemaPath := outputPathToSchemaPath(currentPath + "/" + child.Tag) - if info, ok := idx[childSchemaPath]; ok && info.Kind == KindLeafList && info.OrderedByUser { - pos := orderedCounters[child.Tag] - orderedCounters[child.Tag] = pos + 1 - // Emit positional key: path[pos=N] = value - posPath := currentPath + "/" + child.Tag + fmt.Sprintf("[pos=%d]", pos) - result[posPath] = child.Text - continue - } - leafMapRecurseWithSchema(child, currentPath, result, idx) - } -} - -func structuralKeyedListLeaf(node *Node, currentPath string, idx map[string]*NodeInfo) (string, string, bool) { - schemaPath := outputPathToSchemaPath(currentPath) - info, ok := idx[schemaPath] - if !ok || info.Kind != KindList || info.ListKey == "" { - return "", "", false - } - - // Compound keys (e.g. "choice-ident choice-value community-name") have - // non-key structural children that must be preserved in the leaf map; - // the structural shortcut cannot be used. - if strings.Contains(info.ListKey, " ") { - return "", "", false - } - - keyValue := keyedListValue(node, info.ListKey) - if keyValue == "" || subtreeHasMaterialLeaves(node, currentPath, idx) { - return "", "", false - } - - return currentPath + "/" + info.ListKey, keyValue, true -} - -func keyedListValue(node *Node, keyName string) string { - for _, keyPart := range strings.Fields(keyName) { - for _, child := range node.Children { - if child.Tag == keyPart && child.Text != "" { - return child.Text - } - } - } - - return "" -} - -func subtreeHasMaterialLeaves(node *Node, currentPath string, idx map[string]*NodeInfo) bool { - for _, child := range node.Children { - if isKeyChildWithSchema(child, node, currentPath, idx) { - continue - } - - schemaPath := outputPathToSchemaPath(currentPath) - segment := buildSegmentWithSchema(child, schemaPath, idx) - childPath := segment - if currentPath != "" { - childPath = currentPath + "/" + segment - } - - // A non-key child that is a list entry is material even if its only - // descendant is its own key — nested list entries are real content. - childSchemaPath := outputPathToSchemaPath(childPath) - if info, ok := idx[childSchemaPath]; ok && info.Kind == KindList { - return true - } - - if len(child.Children) == 0 { - // Even empty elements (YANG type "empty") represent material - // config knobs; their presence prevents key-only early return. - return true - } - - if subtreeHasMaterialLeaves(child, childPath, idx) { - return true - } - } - - return false -} - -func buildSegmentWithSchema(node *Node, parentSchemaPath string, idx map[string]*NodeInfo) string { - currentSchemaPath := joinPath(parentSchemaPath, node.Tag) - if info, ok := idx[currentSchemaPath]; ok && info.Kind == KindList && info.ListKey != "" { - // Handle compound keys (space-separated) — try each part. - for _, keyPart := range strings.Fields(info.ListKey) { - for _, child := range node.Children { - if child.Tag == keyPart && child.Text != "" { - return fmt.Sprintf("%s[%s=%s]", node.Tag, keyPart, child.Text) - } - } - } - } - - return buildSegment(node) -} - -func isKeyChildWithSchema(child, parent *Node, parentOutputPath string, idx map[string]*NodeInfo) bool { - parentSchemaPath := outputPathToSchemaPath(parentOutputPath) - if info, ok := idx[parentSchemaPath]; ok && info.Kind == KindList && info.ListKey != "" { - for _, keyPart := range strings.Fields(info.ListKey) { - if child.Tag == keyPart { - return true - } - } - } - - return false -} - -func outputPathToSchemaPath(p string) string { - if p == "" { - return "" - } - - segs := splitPathRespectingQuotes(p) - out := make([]string, 0, len(segs)) - for i, seg := range segs { - tag, _, _ := parseSegment(seg) - - if i == 0 && tag == "configuration" { - continue - } - if len(out) == 0 && tag == "groups" { - continue - } - if len(out) == 0 && tag == "name" { - continue - } - - if tag != "" { - out = append(out, tag) - } - } - - return strings.Join(out, "/") -} - -// LeafMap flattens a *Node tree into a map of XPath-style paths to leaf text -// values. Only nodes with no children (true leaves) and non-empty text are -// included. Keyed list entries encode the key in the path segment so siblings -// with different keys are kept distinct: -// -// interfaces/interface[name=ge-0/0/0]/unit[name=0]/description → "uplink" -// -// Key elements themselves (e.g. ge-0/0/0) are NOT emitted as -// separate entries — they are encoded in the parent segment and cannot be -// independently patched (changing a key requires delete + create). -func LeafMap(root *Node) map[string]string { - result := make(map[string]string) - leafMapRecurse(root, "", result) - return result -} - -func leafMapRecurse(node *Node, parentPath string, result map[string]string) { - segment := buildSegment(node) - - var currentPath string - if parentPath == "" { - currentPath = segment - } else { - currentPath = parentPath + "/" + segment - } - - // Leaf node — record it and stop recursing - if len(node.Children) == 0 { - if node.Text != "" { - result[currentPath] = node.Text - } - return - } - - for _, child := range node.Children { - // Skip the key child — it is already encoded in the current segment. - // Emitting it separately would create spurious "delete key" operations - // whenever an ancestor list entry is modified. - if isKeyChild(child, node) { - continue - } - leafMapRecurse(child, currentPath, result) - } -} - -// buildSegment returns "tag" for plain elements and "tag[keyName=keyValue]" -// for Junos YANG list entries whose first child is a recognised key element. -func buildSegment(node *Node) string { - if len(node.Children) > 0 { - first := node.Children[0] - if junosListKeys[first.Tag] && first.Text != "" { - return fmt.Sprintf("%s[%s=%s]", node.Tag, first.Tag, first.Text) - } - } - return node.Tag -} - -// isKeyChild returns true when child is the key element of a YANG list entry. -// The convention in Junos-generated XML is that the key is always the first -// child of the list element, so we check both position and tag name. -func isKeyChild(child, parent *Node) bool { - if len(parent.Children) == 0 { - return false - } - return junosListKeys[child.Tag] && - parent.Children[0] == child && - child.Text != "" -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/matrix_test.go b/tf/providers/terraform-provider-junos-qfx/patch/matrix_test.go deleted file mode 100644 index eda9b150..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/matrix_test.go +++ /dev/null @@ -1,914 +0,0 @@ -package patch - -import ( - "strings" - "testing" -) - -// matrixSchema covers all four YANG node types needed by the test matrix: -// - container: system, interfaces, policy-options, chassis, services -// - list: interface (key=name), unit (key=name), host (key=name), contents (key=name) -// - leaf: host-name, description, device-count, etc. -// - leaf-list: members -const matrixSchema = `{ - "path": "", - "root": { - "children": [ - { - "name": "configuration", - "type": "container", - "path": "", - "children": [ - { - "name": "system", - "type": "container", - "path": "", - "children": [ - { - "name": "host-name", - "type": "leaf", - "path": "system", - "leaf-type": "string" - }, - { - "name": "syslog", - "type": "container", - "path": "system", - "children": [ - { - "name": "host", - "type": "list", - "path": "system/syslog", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "system/syslog/host", - "leaf-type": "string" - }, - { - "name": "contents", - "type": "list", - "path": "system/syslog/host", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "system/syslog/host/contents", - "leaf-type": "string" - }, - { - "name": "any", - "type": "leaf", - "path": "system/syslog/host/contents", - "leaf-type": "empty" - }, - { - "name": "notice", - "type": "leaf", - "path": "system/syslog/host/contents", - "leaf-type": "empty" - } - ] - } - ] - } - ] - }, - { - "name": "services", - "type": "container", - "path": "system", - "children": [ - { - "name": "ssh", - "type": "container", - "path": "system/services", - "children": [] - } - ] - } - ] - }, - { - "name": "interfaces", - "type": "container", - "path": "", - "children": [ - { - "name": "interface", - "type": "list", - "path": "interfaces", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface", - "leaf-type": "string" - }, - { - "name": "description", - "type": "leaf", - "path": "interfaces/interface", - "leaf-type": "string" - }, - { - "name": "unit", - "type": "list", - "path": "interfaces/interface", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "interfaces/interface/unit", - "leaf-type": "string" - }, - { - "name": "description", - "type": "leaf", - "path": "interfaces/interface/unit", - "leaf-type": "string" - } - ] - } - ] - } - ] - }, - { - "name": "policy-options", - "type": "container", - "path": "", - "children": [ - { - "name": "community", - "type": "list", - "path": "policy-options", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "policy-options/community", - "leaf-type": "string" - }, - { - "name": "members", - "type": "leaf-list", - "path": "policy-options/community", - "leaf-type": "string" - } - ] - } - ] - }, - { - "name": "chassis", - "type": "container", - "path": "", - "children": [ - { - "name": "aggregated-devices", - "type": "container", - "path": "chassis", - "children": [ - { - "name": "ethernet", - "type": "container", - "path": "chassis/aggregated-devices", - "children": [ - { - "name": "device-count", - "type": "leaf", - "path": "chassis/aggregated-devices/ethernet", - "leaf-type": "string" - } - ] - } - ] - } - ] - } - ] - } - ] - } -}` - -func matrixIdx(t *testing.T) map[string]*NodeInfo { - t.Helper() - return mustIdxFromSchema(t, matrixSchema) -} - -// --------------------------------------------------------------------------- -// 2.1 Leaf Operations -// --------------------------------------------------------------------------- - -// L1 — Create leaf: add host-name to empty system container -func TestL1_CreateLeaf(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `` - planXML := `router1` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff entry, got %d: %v", len(diff), diff) - } - - key := "configuration/system/host-name" - ch, ok := diff[key] - if !ok { - t.Fatalf("expected diff key %s, got %v", key, diff) - } - if ch.Op != Create { - t.Fatalf("expected Create, got %v", ch.Op) - } - if ch.NewVal != "router1" { - t.Fatalf("expected NewVal=router1, got %q", ch.NewVal) - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - if !strings.Contains(patchStr, `nc:operation="merge"`) { - t.Fatalf("patch missing create operation:\n%s", patchStr) - } - if !strings.Contains(patchStr, ">router1<") { - t.Fatalf("patch missing value router1:\n%s", patchStr) - } -} - -// L2 — Replace leaf: change host-name value -func TestL2_ReplaceLeaf(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `router1` - planXML := `router2` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff entry, got %d", len(diff)) - } - - key := "configuration/system/host-name" - ch := diff[key] - if ch.Op != Replace { - t.Fatalf("expected Replace, got %v", ch.Op) - } - if ch.OldVal != "router1" || ch.NewVal != "router2" { - t.Fatalf("expected router1->router2, got %q->%q", ch.OldVal, ch.NewVal) - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(patch), `nc:operation="replace"`) { - t.Fatalf("patch missing replace operation:\n%s", string(patch)) - } -} - -// L3 — Delete leaf: remove description from interface -func TestL3_DeleteLeaf(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/0uplink` - planXML := `ge-0/0/0` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff entry, got %d: %v", len(diff), diff) - } - - key := "configuration/interfaces/interface[name=ge-0/0/0]/description" - ch, ok := diff[key] - if !ok { - // Dump all keys for debugging - for k := range diff { - t.Logf("diff key: %s", k) - } - t.Fatalf("expected diff key %s", key) - } - if ch.Op != Delete { - t.Fatalf("expected Delete, got %v", ch.Op) - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(patch), `nc:operation="delete"`) { - t.Fatalf("patch missing delete operation:\n%s", string(patch)) - } -} - -// L4 — Replace leaf with XML special characters -func TestL4_ReplaceLeafSpecialChars(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/0old` - planXML := `ge-0/0/0x&y<z>w` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff entry, got %d", len(diff)) - } - - ch := diff["configuration/interfaces/interface[name=ge-0/0/0]/description"] - if ch.Op != Replace { - t.Fatalf("expected Replace, got %v", ch.Op) - } - // After XML parsing, the value should be the un-escaped form - if ch.NewVal != "x&yw" { - t.Fatalf("expected un-escaped value x&yw, got %q", ch.NewVal) - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - // The output XML must re-escape the special chars - if !strings.Contains(patchStr, "&") { - t.Fatalf("patch missing & escape:\n%s", patchStr) - } - if !strings.Contains(patchStr, "<") { - t.Fatalf("patch missing < escape:\n%s", patchStr) - } - if !strings.Contains(patchStr, ">") { - t.Fatalf("patch missing > escape:\n%s", patchStr) - } -} - -// L6 — No-op: same value produces empty diff -func TestL6_ReplaceLeafNoOp(t *testing.T) { - idx := matrixIdx(t) - - xml := `r1` - stateMap := LeafMapWithSchema(mustTree(t, xml), idx) - planMap := LeafMapWithSchema(mustTree(t, xml), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 0 { - t.Fatalf("expected empty diff for identical config, got %d entries: %v", len(diff), diff) - } -} - -// --------------------------------------------------------------------------- -// 2.2 Leaf-List Operations -// --------------------------------------------------------------------------- - -// LL1 — Add entry to leaf-list -func TestLL1_AddLeafListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-commtarget:65000:100` - planXML := `my-commtarget:65000:100target:65000:200` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff entry, got %d: %v", len(diff), diff) - } - - for path, ch := range diff { - if ch.Op != Create { - t.Errorf("expected Create, got %v for %s", ch.Op, path) - } - if !strings.Contains(path, "members[value=target:65000:200]") { - t.Errorf("unexpected path: %s", path) - } - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - if !strings.Contains(patchStr, `nc:operation="merge"`) { - t.Errorf("patch missing create operation:\n%s", patchStr) - } - if !strings.Contains(patchStr, "target:65000:200") { - t.Errorf("patch missing new member value:\n%s", patchStr) - } -} - -// LL2 — Remove entry from leaf-list -func TestLL2_RemoveLeafListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-commtarget:65000:100target:65000:200` - planXML := `my-commtarget:65000:100` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff, got %d: %v", len(diff), diff) - } - - for path, ch := range diff { - if ch.Op != Delete { - t.Errorf("expected Delete, got %v for %s", ch.Op, path) - } - if !strings.Contains(path, "members[value=target:65000:200]") { - t.Errorf("unexpected path: %s", path) - } - } -} - -// LL3 — Replace entry in leaf-list (delete old + create new) -func TestLL3_ReplaceLeafListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-commab` - planXML := `my-commac` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 2 { - t.Fatalf("expected 2 diff entries (delete b + create c), got %d: %v", len(diff), diff) - } - - deletePath := "configuration/policy-options/community[name=my-comm]/members[value=b]" - createPath := "configuration/policy-options/community[name=my-comm]/members[value=c]" - if ch, ok := diff[deletePath]; !ok || ch.Op != Delete { - t.Fatalf("expected Delete for %s, got %v", deletePath, diff) - } - if ch, ok := diff[createPath]; !ok || ch.Op != Create { - t.Fatalf("expected Create for %s, got %v", createPath, diff) - } -} - -// LL4 — Reorder leaf-list produces no diff (set semantics) -func TestLL4_ReorderLeafListNoOp(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-commba` - planXML := `my-commab` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 0 { - t.Fatalf("expected empty diff for reordered leaf-list, got %d: %v", len(diff), diff) - } -} - -// LL5 — Delete all leaf-list entries -func TestLL5_DeleteAllLeafListEntries(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-commab` - planXML := `my-comm` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 2 { - t.Fatalf("expected 2 deletes, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Delete { - t.Errorf("expected Delete, got %v", ch.Op) - } - } -} - -// LL6 — Create leaf-list from scratch -func TestLL6_CreateLeafListFromScratch(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `my-comm` - planXML := `my-commxy` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 2 { - t.Fatalf("expected 2 creates, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Create { - t.Errorf("expected Create, got %v", ch.Op) - } - } -} - -// --------------------------------------------------------------------------- -// 2.3 List Operations -// --------------------------------------------------------------------------- - -// K1 — Add new list entry -func TestK1_AddListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/0uplink` - planXML := `ge-0/0/0uplinkge-0/0/1downlink` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // New entry creates: name (promoted to entry operation) + description - if len(diff) != 2 { - t.Fatalf("expected 2 diff entries for new list entry, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Create { - t.Errorf("expected Create, got %v", ch.Op) - } - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - // The key leaf create should promote nc:operation to the parent - if !strings.Contains(patchStr, `interface nc:operation="merge"`) { - t.Fatalf("expected nc:operation on interface entry, got:\n%s", patchStr) - } -} - -// K2 — Delete list entry -func TestK2_DeleteListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/0uplinkge-0/0/1downlink` - planXML := `ge-0/0/0uplink` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - for _, ch := range diff { - if ch.Op != Delete { - t.Errorf("expected Delete, got %v", ch.Op) - } - } - - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - if !strings.Contains(patchStr, `interface nc:operation="delete"`) { - t.Fatalf("expected nc:operation on interface entry, got:\n%s", patchStr) - } -} - -// K3 — Rename list key (delete old + create new) -func TestK3_RenameListKey(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/0link` - planXML := `ge-0/0/1link` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // Should produce deletes for old entry + creates for new entry - hasDelete := false - hasCreate := false - for _, ch := range diff { - if ch.Op == Delete { - hasDelete = true - } - if ch.Op == Create { - hasCreate = true - } - } - if !hasDelete || !hasCreate { - t.Fatalf("expected both Delete and Create for key rename, got: %v", diff) - } -} - -// K4 — Modify leaf inside list entry -func TestK4_ModifyLeafInListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/00old` - planXML := `ge-0/0/00new` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff, got %d: %v", len(diff), diff) - } - - key := "configuration/interfaces/interface[name=ge-0/0/0]/unit[name=0]/description" - ch, ok := diff[key] - if !ok { - for k := range diff { - t.Logf("diff key: %s", k) - } - t.Fatalf("expected diff key %s", key) - } - if ch.Op != Replace { - t.Fatalf("expected Replace, got %v", ch.Op) - } -} - -// K5 — Add leaf inside existing list entry -func TestK5_AddLeafInListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/00` - planXML := `ge-0/0/00new` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Create { - t.Errorf("expected Create, got %v", ch.Op) - } - } -} - -// K6 — Delete leaf inside list entry -func TestK6_DeleteLeafInListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `ge-0/0/00old` - planXML := `ge-0/0/00` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Delete { - t.Errorf("expected Delete, got %v", ch.Op) - } - } -} - -// K8 — Add entry in nested list (host/contents) -func TestK8_AddNestedListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `logany` - planXML := `loganykernel` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // New contents entry: name (key, promoted) + any (empty leaf) - hasCreate := false - for _, ch := range diff { - if ch.Op == Create { - hasCreate = true - } - } - if !hasCreate { - t.Fatalf("expected Create operations for new nested list entry, got: %v", diff) - } -} - -// K10 — Key-only list entry (structural) -func TestK10_KeyOnlyListEntry(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `` - planXML := `log` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 diff for key-only entry, got %d: %v", len(diff), diff) - } - - key := "configuration/system/syslog/host[name=log]/name" - ch, ok := diff[key] - if !ok { - for k := range diff { - t.Logf("diff key: %s", k) - } - t.Fatalf("expected diff key %s", key) - } - if ch.Op != Create { - t.Fatalf("expected Create, got %v", ch.Op) - } -} - -// --------------------------------------------------------------------------- -// 2.4 Container Operations -// --------------------------------------------------------------------------- - -// C1 — Create container with children -func TestC1_CreateContainer(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `` - planXML := `24` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 Create for device-count leaf, got %d: %v", len(diff), diff) - } - - for _, ch := range diff { - if ch.Op != Create { - t.Errorf("expected Create, got %v", ch.Op) - } - } -} - -// C3 — Empty presence container (e.g., ) -func TestC3_EmptyContainer(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `` - planXML := `` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // An empty container like has no text and no children, so it - // produces no leaf map entries. The diff should be empty because - // LeafMapWithSchema only tracks leaf values. - // This is a known limitation: presence containers need special handling. - // For now, verify the leaf maps are consistent. - t.Logf("state map: %v", stateMap) - t.Logf("plan map: %v", planMap) - t.Logf("diff: %v", diff) - - // Both maps should be empty (no leaves under services or ssh) - // This documents the current behavior — empty containers don't produce diffs. - // The provider handles this via the full-config SendDirectTransaction path. -} - -// C5 — Modify children within container -func TestC5_ModifyChildrenInContainer(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `old` - planXML := `new` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 1 { - t.Fatalf("expected 1 Replace, got %d: %v", len(diff), diff) - } - - ch := diff["configuration/system/host-name"] - if ch.Op != Replace { - t.Fatalf("expected Replace, got %v", ch.Op) - } -} - -// --------------------------------------------------------------------------- -// 2.5 Compound / Cross-Type Operations -// --------------------------------------------------------------------------- - -// M1 — Mixed operations: replace + create + delete in one diff -func TestM1_MixedOperations(t *testing.T) { - idx := matrixIdx(t) - - stateXML := ` - r1 - - ge-0/0/0old-desc - - - comm1target:65000:100target:65000:300 - -` - - planXML := ` - r2 - - ge-0/0/0old-desc - ge-0/0/2new-link - - - comm1target:65000:100 - -` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // Expected changes: - // 1. Replace system/host-name r1 -> r2 - // 2. Delete members[value=target:65000:300] - // 3. Create interface[name=ge-0/0/2]/name (promoted) - // 4. Create interface[name=ge-0/0/2]/description - hasReplace := false - hasDelete := false - hasCreate := false - for _, ch := range diff { - switch ch.Op { - case Replace: - hasReplace = true - case Delete: - hasDelete = true - case Create: - hasCreate = true - } - } - if !hasReplace || !hasDelete || !hasCreate { - t.Fatalf("expected Replace+Delete+Create, got: %v", diff) - } - - // Verify ordering: deletes first, then replacements, then creates - patch, err := CreateDiffPatch(diff, "") - if err != nil { - t.Fatal(err) - } - patchStr := string(patch) - - deleteIdx := strings.Index(patchStr, `"delete"`) - replaceIdx := strings.Index(patchStr, `"replace"`) - createIdx := strings.Index(patchStr, `"merge"`) - - if deleteIdx < 0 || replaceIdx < 0 || createIdx < 0 { - t.Fatalf("patch missing expected operations:\n%s", patchStr) - } - - if deleteIdx > replaceIdx { - t.Errorf("delete should come before replace in patch output") - } - if replaceIdx > createIdx { - t.Errorf("replace should come before create in patch output") - } -} - -// M2 — Deep nesting: 4+ levels (system/syslog/host/contents/notice) -func TestM2_DeepNesting(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `logany` - planXML := `logany` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - // The is an empty leaf, so in LeafMapWithSchema it may or may - // not produce a leaf map entry (depends on how empty leaves are handled). - t.Logf("state map: %v", stateMap) - t.Logf("plan map: %v", planMap) - t.Logf("diff: %v", diff) - - // At minimum, the key-only entries should be consistent - stateKey := "configuration/system/syslog/host[name=log]/contents[name=any]/name" - if _, ok := stateMap[stateKey]; ok { - // If contents has children beyond key, key should not be in map - t.Logf("contents key is in state map (structural key behavior)") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/order.go b/tf/providers/terraform-provider-junos-qfx/patch/order.go deleted file mode 100644 index f8386802..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/order.go +++ /dev/null @@ -1,192 +0,0 @@ -package patch - -import "sort" - -// AlignXMLOrderToReference reorders current XML siblings to follow the order in -// the reference XML where possible, while keeping a deterministic fallback -// ordering for entries absent from the reference. -func AlignXMLOrderToReference(currentXML []byte, referenceXML []byte, idx map[string]*NodeInfo) ([]byte, error) { - currentTree, err := BuildTree(currentXML) - if err != nil { - return nil, err - } - - var referenceTree *Node - if len(referenceXML) > 0 { - referenceTree, err = BuildTree(referenceXML) - if err != nil { - return nil, err - } - } - - alignNodeOrder(currentTree, referenceSiblingOrders(referenceTree, idx), idx, currentTree.Tag) - return marshalNodeTree(currentTree) -} - -func alignNodeOrder(node *Node, ref map[string]map[string]int, idx map[string]*NodeInfo, instancePath string) { - if len(node.Children) == 0 { - return - } - - referenceOrder := ref[instancePath] - - sort.SliceStable(node.Children, func(i, j int) bool { - left := childSortKey(node.Children[i], instancePath, referenceOrder, idx) - right := childSortKey(node.Children[j], instancePath, referenceOrder, idx) - - if left.hasReference != right.hasReference { - return left.hasReference - } - if left.referenceRank != right.referenceRank { - return left.referenceRank < right.referenceRank - } - if left.tag != right.tag { - return left.tag < right.tag - } - if left.identity != right.identity { - return left.identity < right.identity - } - return left.text < right.text - }) - - for _, child := range node.Children { - childInstance := instanceIdentity(child, instancePath, idx) - alignNodeOrder(child, ref, idx, childInstance) - } -} - -type sortKey struct { - hasReference bool - referenceRank int - tag string - identity string - text string -} - -func childSortKey(child *Node, parentInstancePath string, referenceOrder map[string]int, idx map[string]*NodeInfo) sortKey { - identity := nodeIdentity(child, parentInstancePath, idx) - rank, ok := referenceOrder[identity] - if !ok { - rank = 1 << 30 - } - - return sortKey{ - hasReference: ok, - referenceRank: rank, - tag: child.Tag, - identity: identity, - text: child.Text, - } -} - -func referenceSiblingOrders(root *Node, idx map[string]*NodeInfo) map[string]map[string]int { - orders := make(map[string]map[string]int) - if root == nil { - return orders - } - - var walk func(node *Node, instancePath string) - walk = func(node *Node, instancePath string) { - if _, ok := orders[instancePath]; !ok { - orders[instancePath] = make(map[string]int) - } - for i, child := range node.Children { - identity := nodeIdentity(child, instancePath, idx) - if _, seen := orders[instancePath][identity]; !seen { - orders[instancePath][identity] = i - } - childInstance := instanceIdentity(child, instancePath, idx) - walk(child, childInstance) - } - } - - walk(root, root.Tag) - return orders -} - -// nodeIdentity returns a short identity string for sorting siblings under the -// same parent. It does NOT include the parent path. -func nodeIdentity(node *Node, parentInstancePath string, idx map[string]*NodeInfo) string { - schPath := schemaPathFromInstance(parentInstancePath, node.Tag) - info := idx[schPath] - if info == nil { - if node.Text != "" { - return node.Tag + "=" + node.Text - } - return node.Tag - } - - switch info.Kind { - case KindList: - keyVal := findKeyChild(node, info.ListKey) - if keyVal != "" { - return node.Tag + "[" + info.ListKey + "=" + keyVal + "]" - } - case KindLeafList: - return node.Tag + "[value=" + node.Text + "]" - } - - if node.Text != "" { - return node.Tag + "=" + node.Text - } - return node.Tag -} - -// instanceIdentity returns a full instance-aware path for a child node, -// including keyed-list identity so that different list entries get distinct -// ordering buckets. -func instanceIdentity(child *Node, parentInstancePath string, idx map[string]*NodeInfo) string { - base := parentInstancePath + "/" + child.Tag - schPath := schemaPathFromInstance(parentInstancePath, child.Tag) - info := idx[schPath] - if info != nil && info.Kind == KindList && info.ListKey != "" { - keyVal := findKeyChild(child, info.ListKey) - if keyVal != "" { - return base + "[" + info.ListKey + "=" + keyVal + "]" - } - } - return base -} - -// schemaPathFromInstance extracts the schema path for a child tag given an -// instance-aware parent path. It strips keyed-list predicates like -// "host[name=log]" back to "host" so the result matches schema index keys. -func schemaPathFromInstance(parentInstancePath string, childTag string) string { - return schemaPath(joinXMLPath(stripInstancePredicates(parentInstancePath), childTag)) -} - -// stripInstancePredicates removes [key=value] predicates from every segment -// of an instance path, returning the plain XML element path. -func stripInstancePredicates(path string) string { - var out []byte - inBracket := false - for i := 0; i < len(path); i++ { - if path[i] == '[' { - inBracket = true - continue - } - if path[i] == ']' { - inBracket = false - continue - } - if !inBracket { - out = append(out, path[i]) - } - } - return string(out) -} - -func joinXMLPath(parentPath string, tag string) string { - if parentPath == "" { - return tag - } - return parentPath + "/" + tag -} - -func schemaPath(path string) string { - path = normalizePath(path) - if path == "configuration" { - return "" - } - return normalizePath(path) -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/order_test.go b/tf/providers/terraform-provider-junos-qfx/patch/order_test.go deleted file mode 100644 index 3a0f8cd0..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/order_test.go +++ /dev/null @@ -1,265 +0,0 @@ -package patch - -import ( - "strings" - "testing" -) - -func TestAlignXMLOrderToReference_ReordersKeyedListsAndLeafLists(t *testing.T) { - idx := mustIdxFromSchema(t, testTrimmedSchema) - current := []byte(` - - c - a -
10.0.0.2
beta
-
10.0.0.1
alpha
-
-
`) - reference := []byte(` - -
10.0.0.1
alpha
-
10.0.0.2
beta
- a - c -
-
`) - - aligned, err := AlignXMLOrderToReference(current, reference, idx) - if err != nil { - t.Fatalf("AlignXMLOrderToReference() error: %v", err) - } - - got := string(aligned) - if strings.Index(got, "10.0.0.1") > strings.Index(got, "10.0.0.2") { - t.Fatalf("expected keyed list entries to follow reference order, got %s", got) - } - if strings.Index(got, ">a") > strings.Index(got, ">c") { - t.Fatalf("expected leaf-list values to follow reference order, got %s", got) - } -} - -func TestAlignXMLOrderToReference_UsesDeterministicFallbackWhenReferenceEmpty(t *testing.T) { - idx := mustIdxFromSchema(t, testTrimmedSchema) - current := []byte(` - -
10.0.0.2
beta
-
10.0.0.1
alpha
-
-
`) - - aligned, err := AlignXMLOrderToReference(current, nil, idx) - if err != nil { - t.Fatalf("AlignXMLOrderToReference() error: %v", err) - } - - got := string(aligned) - if strings.Index(got, "10.0.0.1") > strings.Index(got, "10.0.0.2") { - t.Fatalf("expected deterministic fallback ordering by keyed identity, got %s", got) - } -} - -// --------------------------------------------------------------------------- -// Matrix Order Tests — O1-O6 -// --------------------------------------------------------------------------- - -// O1 — Top-level list reorder: [B,A,C] → reference [A,B,C] -func TestAlignOrder_TopLevelListReorder(t *testing.T) { - idx := matrixIdx(t) - - current := []byte(` - ge-0/0/2c - ge-0/0/0a - ge-0/0/1b -`) - - reference := []byte(` - ge-0/0/0a - ge-0/0/1b - ge-0/0/2c -`) - - aligned, err := AlignXMLOrderToReference(current, reference, idx) - if err != nil { - t.Fatal(err) - } - got := string(aligned) - - idx0 := strings.Index(got, "ge-0/0/0") - idx1 := strings.Index(got, "ge-0/0/1") - idx2 := strings.Index(got, "ge-0/0/2") - if idx0 > idx1 || idx1 > idx2 { - t.Fatalf("expected order ge-0/0/0, ge-0/0/1, ge-0/0/2, got:\n%s", got) - } -} - -// O2 — Nested list reorder: each parent entry has independent child ordering -func TestAlignOrder_NestedListPerInstanceReorder(t *testing.T) { - idx := mustIdxFromSchema(t, testStructuralKeyTrimmedSchema) - - current := []byte(` - security - kernel - interactive-commands - - messages - interactive-commands - kernel - -`) - - reference := []byte(` - security - interactive-commands - kernel - - messages - kernel - interactive-commands - -`) - - aligned, err := AlignXMLOrderToReference(current, reference, idx) - if err != nil { - t.Fatal(err) - } - got := string(aligned) - - // Split output at "messages" to get the two file sections - msgIdx := strings.Index(got, "messages") - if msgIdx < 0 { - t.Fatalf("expected messages file in output:\n%s", got) - } - secSection := got[:msgIdx] - msgSection := got[msgIdx:] - - // In file[security], interactive-commands should come before kernel - icIdx := strings.Index(secSection, "interactive-commands") - kerIdx := strings.Index(secSection, "kernel") - if icIdx < 0 || kerIdx < 0 { - t.Fatalf("file[security]: missing expected contents entries in:\n%s", secSection) - } - if icIdx > kerIdx { - t.Errorf("file[security]: expected interactive-commands before kernel, got:\n%s", secSection) - } - - // In file[messages], kernel should come before interactive-commands - icIdx2 := strings.Index(msgSection, "interactive-commands") - kerIdx2 := strings.Index(msgSection, "kernel") - if icIdx2 < 0 || kerIdx2 < 0 { - t.Fatalf("file[messages]: missing expected contents entries in:\n%s", msgSection) - } - if kerIdx2 > icIdx2 { - t.Errorf("file[messages]: expected kernel before interactive-commands, got:\n%s", msgSection) - } -} - -// O3 — Extra entries in current (not in reference) sort after reference entries -func TestAlignOrder_ExtraEntriesInCurrent(t *testing.T) { - idx := matrixIdx(t) - - current := []byte(` - ge-0/0/3extra-d - ge-0/0/1b - ge-0/0/0a - ge-0/0/2extra-c -`) - - reference := []byte(` - ge-0/0/1b - ge-0/0/0a -`) - - aligned, err := AlignXMLOrderToReference(current, reference, idx) - if err != nil { - t.Fatal(err) - } - got := string(aligned) - - // Reference entries first in ref order: ge-0/0/1, ge-0/0/0 - // Then extras sorted deterministically: ge-0/0/2, ge-0/0/3 - idx1 := strings.Index(got, "ge-0/0/1") - idx0 := strings.Index(got, "ge-0/0/0") - idx2 := strings.Index(got, "ge-0/0/2") - idx3 := strings.Index(got, "ge-0/0/3") - - if idx1 > idx0 { - t.Errorf("expected ge-0/0/1 before ge-0/0/0 (reference order), got:\n%s", got) - } - if idx0 > idx2 || idx0 > idx3 { - t.Errorf("expected reference entries before extras, got:\n%s", got) - } -} - -// O4 — Missing entry in current (reference has entry current lacks) -func TestAlignOrder_MissingEntryInCurrent(t *testing.T) { - idx := matrixIdx(t) - - current := []byte(` - ge-0/0/2c - ge-0/0/0a -`) - - reference := []byte(` - ge-0/0/0a - ge-0/0/1b - ge-0/0/2c -`) - - aligned, err := AlignXMLOrderToReference(current, reference, idx) - if err != nil { - t.Fatal(err) - } - got := string(aligned) - - // ge-0/0/0 should come before ge-0/0/2 (respecting reference order) - idx0 := strings.Index(got, "ge-0/0/0") - idx2 := strings.Index(got, "ge-0/0/2") - if idx0 > idx2 { - t.Errorf("expected ge-0/0/0 before ge-0/0/2, got:\n%s", got) - } - // Missing ge-0/0/1 should not crash or appear - if strings.Contains(got, "ge-0/0/1") { - t.Errorf("missing reference entry ge-0/0/1 should not appear in output") - } -} - -// O5 — Leaf-list reorder produces no diff (set semantics via LeafMapWithSchema) -func TestAlignOrder_LeafListSetSemantics(t *testing.T) { - idx := matrixIdx(t) - - stateXML := `commba` - planXML := `commab` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - if len(diff) != 0 { - t.Fatalf("leaf-list reorder should produce empty diff (set semantics), got %d: %v", len(diff), diff) - } -} - -// O6 — Empty reference: deterministic fallback by identity -func TestAlignOrder_EmptyReference(t *testing.T) { - idx := matrixIdx(t) - - current := []byte(` - ge-0/0/2c - ge-0/0/0a - ge-0/0/1b -`) - - aligned, err := AlignXMLOrderToReference(current, nil, idx) - if err != nil { - t.Fatal(err) - } - got := string(aligned) - - // With empty reference, should sort by key identity: ge-0/0/0, ge-0/0/1, ge-0/0/2 - idx0 := strings.Index(got, "ge-0/0/0") - idx1 := strings.Index(got, "ge-0/0/1") - idx2 := strings.Index(got, "ge-0/0/2") - if idx0 > idx1 || idx1 > idx2 { - t.Fatalf("expected deterministic fallback order ge-0/0/0, ge-0/0/1, ge-0/0/2, got:\n%s", got) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/patch.go b/tf/providers/terraform-provider-junos-qfx/patch/patch.go deleted file mode 100644 index 4ca42d83..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/patch.go +++ /dev/null @@ -1,414 +0,0 @@ -package patch - -import ( - "bytes" - "fmt" - "sort" - "strings" -) - -// CreateDiffPatch builds the Junos NETCONF XML body from the -// diff map, targeting base configuration paths directly. -// -// The nc:operation attributes written here reference the xmlns:nc declaration -// that sendNetconfPatch places on the enclosing element — no -// additional namespace declaration is required in this output. -// -// Example output for a single Replace: -// -// -// -// -// ge-0/0/0 -// -// 0 -// new-desc -// -// -// -// -func CreateDiffPatch(diffMap map[string]Change, groupName string) ([]byte, error) { - return CreateDiffPatchWithSchema(diffMap, groupName, nil) -} - -// CreateDiffPatchWithSchema is like CreateDiffPatch but accepts a schema index -// for container delete coalescing. When idx is non-nil and ALL leaves under a -// schema container are being deleted (with no creates or replaces), they are -// coalesced into a single container-level nc:operation="delete". -func CreateDiffPatchWithSchema(diffMap map[string]Change, groupName string, idx map[string]*NodeInfo) ([]byte, error) { - _ = groupName - - // Pre-pass: coalesce container deletes when schema is available. - if idx != nil { - diffMap = coalesceContainerDeletes(diffMap, idx) - } - - // Root of the output tree - root := &Node{Tag: "configuration"} - - type pendingLeaf struct { - parent *Node - tag string - keyName string - change Change - } - - ordered := orderedChanges(diffMap) - - // Two-pass strategy: - // Pass 1 — process key-entry operations and collect pending leaf ops. - // This ensures parent nodes get nc:operation="delete" BEFORE we decide - // whether a child leaf needs its own operation attribute. - var pending []pendingLeaf - for _, entry := range ordered { - path := entry.path - change := entry.change - segments := splitPathRespectingQuotes(path) - if len(segments) == 0 { - continue - } - - if segments[0] == "configuration" { - segments = segments[1:] - } - if len(segments) > 0 { - firstTag, _, _ := parseSegment(segments[0]) - if firstTag == "groups" { - segments = segments[1:] - } - } - if len(segments) == 0 { - continue - } - - parentSegments := segments[:len(segments)-1] - leafSegment := segments[len(segments)-1] - - parent := ensurePath(root, parentSegments) - - if applyKeyedListEntryOperation(parent, parentSegments, leafSegment, change) { - continue - } - - leafTag, keyName, _ := parseSegment(leafSegment) - pending = append(pending, pendingLeaf{ - parent: parent, tag: leafTag, keyName: keyName, change: change, - }) - } - - // Pass 2 — create leaf nodes, inheriting context from pass-1 parent ops. - for _, p := range pending { - // Positional leaf-list entries (path ends with [pos=N]) represent - // ordered-by-user leaf-lists. A Replace means the value at that - // position changed — emit delete of old + create of new. - if p.keyName == "pos" { - switch p.change.Op { - case Create: - leaf := &Node{Tag: p.tag, Parent: p.parent, Operation: "merge", Text: p.change.NewVal} - p.parent.Children = append(p.parent.Children, leaf) - case Delete: - leaf := &Node{Tag: p.tag, Parent: p.parent, Operation: "delete", Text: p.change.OldVal} - p.parent.Children = append(p.parent.Children, leaf) - case Replace: - // Reorder: delete old value, create new value - del := &Node{Tag: p.tag, Parent: p.parent, Operation: "delete", Text: p.change.OldVal} - p.parent.Children = append(p.parent.Children, del) - cre := &Node{Tag: p.tag, Parent: p.parent, Operation: "merge", Text: p.change.NewVal} - p.parent.Children = append(p.parent.Children, cre) - } - continue - } - - leaf := &Node{ - Tag: p.tag, - Parent: p.parent, - } - - switch p.change.Op { - case Create: - // merge, not create: idempotent over pre-existing device config - // (brownfield/empty-state applies), consistent with default-operation=merge. - leaf.Operation = "merge" - leaf.Text = p.change.NewVal - case Replace: - leaf.Operation = "replace" - leaf.Text = p.change.NewVal - case Delete: - // Leaf-list entries (paths with [value=xxx]) need the old value - // so Junos knows which instance to remove. - // Scalar leaves must NOT include text — Junos rejects - // value for scalar leaves. - if p.keyName == "value" { - leaf.Text = p.change.OldVal - } - // If the parent already has nc:operation="delete" (set by - // applyKeyedListEntryOperation in pass 1), this leaf is just - // a structural sibling — do NOT add an operation. This is - // critical for Junos compound-key lists where choice-ident - // elements (e.g. ) must appear WITHOUT an operation. - if p.parent.Operation == "delete" { - // structural child — no operation - } else { - leaf.Operation = "delete" - } - } - - p.parent.Children = append(p.parent.Children, leaf) - } - - return marshalNodeTree(root) -} - -func applyKeyedListEntryOperation(parent *Node, parentSegments []string, leafSegment string, change Change) bool { - if len(parentSegments) == 0 { - return false - } - - _, parentKeyName, parentKeyValue := parseSegment(parentSegments[len(parentSegments)-1]) - leafTag, _, _ := parseSegment(leafSegment) - if parentKeyName == "" || leafTag != parentKeyName { - return false - } - - keyValue := change.NewVal - if change.Op == Delete { - keyValue = change.OldVal - } - if keyValue == "" || keyValue != parentKeyValue { - return false - } - - switch change.Op { - case Create: - // merge (see Create case above): idempotent over existing config. - parent.Operation = "merge" - case Replace: - parent.Operation = "replace" - case Delete: - parent.Operation = "delete" - default: - return false - } - - return true -} - -type orderedChange struct { - path string - change Change -} - -func orderedChanges(diffMap map[string]Change) []orderedChange { - result := make([]orderedChange, 0, len(diffMap)) - for path, change := range diffMap { - result = append(result, orderedChange{path: path, change: change}) - } - - sort.SliceStable(result, func(i, j int) bool { - a := result[i] - b := result[j] - - pa := opPriority(a.change.Op) - pb := opPriority(b.change.Op) - if pa != pb { - return pa < pb - } - - da := pathDepth(a.path) - db := pathDepth(b.path) - if a.change.Op == Delete { - if da != db { - return da > db - } - } else { - if da != db { - return da < db - } - } - - return a.path < b.path - }) - - return result -} - -func opPriority(op ChangeType) int { - switch op { - case Delete: - return 0 - case Replace: - return 1 - case Create: - return 2 - default: - return 3 - } -} - -func pathDepth(path string) int { - if path == "" { - return 0 - } - return len(splitPathRespectingQuotes(path)) -} - -// marshalNodeTree serializes a *Node tree to indented XML bytes. -func marshalNodeTree(root *Node) ([]byte, error) { - var buf bytes.Buffer - if err := encodeNode(&buf, root, 0); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// encodeNode recursively writes a node and all its descendants to buf. -func encodeNode(buf *bytes.Buffer, n *Node, depth int) error { - indent := strings.Repeat(" ", depth) - buf.WriteString(indent + "<" + n.Tag) - - // Standard XML attributes - for k, v := range n.Attrs { - if _, err := fmt.Fprintf(buf, ` %s="%s"`, k, xmlEscape(v)); err != nil { - return err - } - } - - // nc:operation attribute — references xmlns:nc on the ancestor - if n.Operation != "" { - if _, err := fmt.Fprintf(buf, ` nc:operation="%s"`, n.Operation); err != nil { - return err - } - } - - // Self-closing for delete and empty nodes - if len(n.Children) == 0 && n.Text == "" { - buf.WriteString("/>\n") - return nil - } - - buf.WriteString(">") - - if len(n.Children) > 0 { - buf.WriteString("\n") - for _, child := range n.Children { - if err := encodeNode(buf, child, depth+1); err != nil { - return err - } - } - buf.WriteString(indent + "\n") - } else { - // Inline text with XML escaping - buf.WriteString(xmlEscape(n.Text) + "\n") - } - - return nil -} - -// xmlEscape escapes the five XML special characters in text content and -// attribute values. -func xmlEscape(s string) string { - s = strings.ReplaceAll(s, "&", "&") // must be first - s = strings.ReplaceAll(s, "<", "<") - s = strings.ReplaceAll(s, ">", ">") - s = strings.ReplaceAll(s, "\"", """) - s = strings.ReplaceAll(s, "'", "'") - return s -} - -// coalesceContainerDeletes detects when ALL leaves under a schema container are -// Delete operations (no Creates or Replaces share that prefix), and replaces -// them with a single synthetic Delete entry for the container path itself. -// This produces a compact instead of N -// individual leaf deletes, which is both more efficient and avoids ordering -// issues on Junos. -func coalesceContainerDeletes(diffMap map[string]Change, idx map[string]*NodeInfo) map[string]Change { - // Build a set of all leaf paths grouped by their deepest container ancestor. - // A "container" here means a schema node of KindContainer (not KindList). - type containerStats struct { - allDelete bool - count int - paths []string - } - - containers := make(map[string]*containerStats) - - for path, change := range diffMap { - segments := splitPathRespectingQuotes(path) - // Strip configuration prefix - if len(segments) > 0 && segments[0] == "configuration" { - segments = segments[1:] - } - - // Find the deepest container ancestor in the schema - for depth := len(segments) - 1; depth >= 1; depth-- { - ancestorSegments := segments[:depth] - // Build schema path from segments (strip key predicates) - schemaPath := "" - for _, seg := range ancestorSegments { - tag, _, _ := parseSegment(seg) - if schemaPath == "" { - schemaPath = tag - } else { - schemaPath = schemaPath + "/" + tag - } - } - - info, ok := idx[schemaPath] - if !ok || info.Kind != KindContainer { - continue - } - - // Found a container ancestor — record this path - if _, exists := containers[schemaPath]; !exists { - containers[schemaPath] = &containerStats{allDelete: true} - } - stat := containers[schemaPath] - stat.count++ - stat.paths = append(stat.paths, path) - if change.Op != Delete { - stat.allDelete = false - } - break // only use the deepest container - } - } - - // Identify containers where ALL children are Delete - coalesced := make(map[string]bool) - for _, stat := range containers { - if !stat.allDelete || stat.count < 2 { - continue - } - // Mark all child paths for removal - for _, p := range stat.paths { - coalesced[p] = true - } - } - - if len(coalesced) == 0 { - return diffMap - } - - // Build new diffMap: remove coalesced leaves, add container-level deletes - result := make(map[string]Change, len(diffMap)) - for path, change := range diffMap { - if !coalesced[path] { - result[path] = change - } - } - - // Add synthetic container deletes - added := make(map[string]bool) - for containerPath, stat := range containers { - if !stat.allDelete || stat.count < 2 { - continue - } - if added[containerPath] { - continue - } - added[containerPath] = true - // The path needs "configuration/" prefix for CreateDiffPatch processing - result["configuration/"+containerPath] = Change{Op: Delete, OldVal: "", NewVal: ""} - } - - return result -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/patch_test.go b/tf/providers/terraform-provider-junos-qfx/patch/patch_test.go deleted file mode 100644 index 8895eea6..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/patch_test.go +++ /dev/null @@ -1,2493 +0,0 @@ -package patch - -import ( - "os" - "strings" - "testing" -) - -var TrimmedSchemaJSON = `{ - "path": "", - "root": { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "device-count", - "path": "chassis/aggregated-devices/ethernet", - "type": "leaf", - "types": [ - { - "path": "chassis/aggregated-devices/ethernet/device-count", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "chassis/aggregated-devices/ethernet/device-count", - "type": "uint32" - } - ] - } - ], - "name": "ethernet", - "path": "chassis/aggregated-devices", - "type": "container" - } - ], - "name": "aggregated-devices", - "path": "chassis", - "type": "container" - } - ], - "name": "chassis", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 127, - "min": 1, - "path": "forwarding-options/storm-control-profiles/name" - } - ], - "name": "name", - "path": "forwarding-options/storm-control-profiles", - "type": "leaf" - }, - { - "name": "all", - "path": "forwarding-options/storm-control-profiles", - "type": "container" - } - ], - "key": "name", - "name": "storm-control-profiles", - "path": "forwarding-options", - "type": "list" - } - ], - "name": "forwarding-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "interfaces/interface", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "description", - "path": "interfaces/interface", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "vlan-tagging", - "path": "interfaces/interface", - "type": "leaf" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:esi", - "name": "identifier", - "path": "interfaces/interface/esi", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "all-active", - "path": "interfaces/interface/esi", - "type": "leaf" - } - ], - "name": "esi", - "path": "interfaces/interface", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "bundle", - "path": "interfaces/interface/ether-options/ieee-802.3ad", - "type": "leaf", - "types": [ - { - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle", - "type": "string" - }, - { - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - } - ], - "name": "ieee-802.3ad", - "path": "interfaces/interface/ether-options", - "type": "container" - } - ], - "name": "ether-options", - "path": "interfaces/interface", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "empty", - "name": "active", - "path": "interfaces/interface/aggregated-ether-options/lacp", - "type": "leaf" - }, - { - "enums": [ - { - "id": "fast", - "path": "interfaces/interface/aggregated-ether-options/lacp/periodic", - "value": 0 - }, - { - "id": "slow", - "path": "interfaces/interface/aggregated-ether-options/lacp/periodic", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "periodic", - "path": "interfaces/interface/aggregated-ether-options/lacp", - "type": "leaf" - }, - { - "base-type": "string", - "leaf-type": "jt:mac-addr", - "name": "system-id", - "path": "interfaces/interface/aggregated-ether-options/lacp", - "type": "leaf" - } - ], - "name": "lacp", - "path": "interfaces/interface/aggregated-ether-options", - "type": "container" - } - ], - "name": "aggregated-ether-options", - "path": "interfaces/interface", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "interfaces/interface/unit", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "description", - "path": "interfaces/interface/unit", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "vlan-id", - "path": "interfaces/interface/unit", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipv4prefix", - "name": "name", - "path": "interfaces/interface/unit/family/inet/address", - "type": "leaf" - } - ], - "key": "name", - "name": "address", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/inet", - "type": "list" - } - ], - "name": "inet", - "path": "interfaces/interface/unit/family", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "members", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/ethernet-switching/vlan", - "type": "leaf-list" - } - ], - "name": "vlan", - "path": "interfaces/interface/unit/family/ethernet-switching", - "type": "container" - } - ], - "name": "ethernet-switching", - "path": "interfaces/interface/unit/family", - "type": "container" - } - ], - "name": "family", - "path": "interfaces/interface/unit", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:mac-unicast", - "name": "mac", - "path": "interfaces/interface/unit", - "type": "leaf" - } - ], - "key": "name", - "name": "unit", - "path": "interfaces/interface", - "type": "list" - } - ], - "key": "name", - "name": "interface", - "path": "interfaces", - "type": "list" - } - ], - "name": "interfaces", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "policy-options/policy-statement", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "policy-options/policy-statement/term", - "type": "leaf" - }, - { - "children": [ - { - "enums": [ - { - "id": "aggregate", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 0 - }, - { - "id": "bgp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 1 - }, - { - "id": "direct", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 2 - }, - { - "id": "dvmrp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 3 - }, - { - "id": "isis", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 4 - }, - { - "id": "esis", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 5 - }, - { - "id": "l2circuit", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 6 - }, - { - "id": "l2vpn", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 7 - }, - { - "id": "local", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 8 - }, - { - "id": "ospf", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 9 - }, - { - "id": "ospf2", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 10 - }, - { - "id": "ospf3", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 11 - }, - { - "id": "pim", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 12 - }, - { - "id": "rip", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 13 - }, - { - "id": "ripng", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 14 - }, - { - "id": "static", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 15 - }, - { - "id": "arp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 16 - }, - { - "id": "frr", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 17 - }, - { - "id": "mpls", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 18 - }, - { - "id": "ldp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 19 - }, - { - "id": "rsvp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 20 - }, - { - "id": "msdp", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 21 - }, - { - "id": "route-target", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 22 - }, - { - "id": "access", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 23 - }, - { - "id": "access-internal", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 24 - }, - { - "id": "anchor", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 25 - }, - { - "id": "bgp-static", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 26 - }, - { - "id": "vpls", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 27 - }, - { - "id": "evpn", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 28 - }, - { - "id": "spring-te", - "path": "policy-options/policy-statement/term/from/protocol", - "value": 29 - } - ], - "leaf-type": "enumeration", - "name": "protocol", - "ordered-by": "user", - "path": "policy-options/policy-statement/term/from", - "type": "leaf-list" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipprefix", - "name": "address", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "exact", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "orlonger", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "prefix-length-range", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - } - ], - "key": "address choice-ident choice-value", - "name": "route-filter", - "ordered-by": "user", - "path": "policy-options/policy-statement/term/from", - "type": "list" - } - ], - "name": "from", - "path": "policy-options/policy-statement/term", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "add", - "path": "policy-options/policy-statement/term/then/community", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "community-name", - "path": "policy-options/policy-statement/term/then/community", - "type": "leaf" - } - ], - "key": "choice-ident choice-value community-name", - "name": "community", - "ordered-by": "user", - "path": "policy-options/policy-statement/term/then", - "type": "list" - }, - { - "leaf-type": "empty", - "name": "accept", - "path": "policy-options/policy-statement/term/then", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "reject", - "path": "policy-options/policy-statement/term/then", - "type": "leaf" - } - ], - "name": "then", - "path": "policy-options/policy-statement/term", - "type": "container" - } - ], - "key": "name", - "name": "term", - "ordered-by": "user", - "path": "policy-options/policy-statement", - "type": "list" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "empty", - "name": "per-packet", - "path": "policy-options/policy-statement/then/load-balance", - "type": "leaf" - } - ], - "name": "load-balance", - "path": "policy-options/policy-statement/then", - "type": "container" - } - ], - "name": "then", - "path": "policy-options/policy-statement", - "type": "container" - } - ], - "key": "name", - "name": "policy-statement", - "path": "policy-options", - "type": "list" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "policy-options/community", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "members", - "ordered-by": "user", - "path": "policy-options/community", - "type": "leaf-list" - } - ], - "key": "name", - "name": "community", - "path": "policy-options", - "type": "list" - } - ], - "name": "policy-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "enums": [ - { - "id": "internal", - "path": "protocols/bgp/group/type", - "value": 0 - }, - { - "id": "external", - "path": "protocols/bgp/group/type", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "type", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "no-nexthop-change", - "path": "protocols/bgp/group/multihop", - "type": "leaf" - } - ], - "name": "multihop", - "path": "protocols/bgp/group", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:ipaddr", - "name": "local-address", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "mtu-discovery", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "import", - "ordered-by": "user", - "path": "protocols/bgp/group", - "type": "leaf-list" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "routing-uptime", - "path": "protocols/bgp/group/family/evpn/signaling/delay-route-advertisements/minimum-delay", - "type": "leaf", - "types": [ - { - "path": "protocols/bgp/group/family/evpn/signaling/delay-route-advertisements/minimum-delay/routing-uptime", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "protocols/bgp/group/family/evpn/signaling/delay-route-advertisements/minimum-delay/routing-uptime", - "ranges": [ - { - "max": 36000, - "min": 1, - "path": "protocols/bgp/group/family/evpn/signaling/delay-route-advertisements/minimum-delay/routing-uptime" - } - ], - "type": "uint32" - } - ] - } - ], - "name": "minimum-delay", - "path": "protocols/bgp/group/family/evpn/signaling/delay-route-advertisements", - "type": "container" - } - ], - "name": "delay-route-advertisements", - "path": "protocols/bgp/group/family/evpn/signaling", - "type": "container" - } - ], - "name": "signaling", - "path": "protocols/bgp/group/family/evpn", - "type": "container" - } - ], - "name": "evpn", - "path": "protocols/bgp/group/family", - "type": "container" - } - ], - "name": "family", - "path": "protocols/bgp/group", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "export", - "ordered-by": "user", - "path": "protocols/bgp/group", - "type": "leaf-list" - }, - { - "leaf-type": "empty", - "name": "vpn-apply-export", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "base-type": "string", - "leaf-type": "jt:areaid", - "name": "cluster", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "as-number", - "path": "protocols/bgp/group/local-as", - "type": "leaf" - } - ], - "name": "local-as", - "path": "protocols/bgp/group", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "multiple-as", - "path": "protocols/bgp/group/multipath", - "type": "leaf" - } - ], - "name": "multipath", - "path": "protocols/bgp/group", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "union", - "name": "minimum-interval", - "path": "protocols/bgp/group/bfd-liveness-detection", - "type": "leaf", - "types": [ - { - "path": "protocols/bgp/group/bfd-liveness-detection/minimum-interval", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "protocols/bgp/group/bfd-liveness-detection/minimum-interval", - "ranges": [ - { - "max": 255000, - "min": 1, - "path": "protocols/bgp/group/bfd-liveness-detection/minimum-interval" - } - ], - "type": "uint32" - } - ], - "units": "milliseconds" - }, - { - "default": "3", - "leaf-type": "union", - "name": "multiplier", - "path": "protocols/bgp/group/bfd-liveness-detection", - "type": "leaf", - "types": [ - { - "path": "protocols/bgp/group/bfd-liveness-detection/multiplier", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "protocols/bgp/group/bfd-liveness-detection/multiplier", - "ranges": [ - { - "max": 255, - "min": 1, - "path": "protocols/bgp/group/bfd-liveness-detection/multiplier" - } - ], - "type": "uint32" - } - ] - } - ], - "name": "bfd-liveness-detection", - "path": "protocols/bgp/group", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:ipprefix", - "name": "allow", - "ordered-by": "user", - "path": "protocols/bgp/group", - "type": "leaf-list" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipaddr", - "name": "name", - "path": "protocols/bgp/group/neighbor", - "type": "leaf" - }, - { - "leaf-type": "string", - "lengths": [ - { - "max": 255, - "min": 1, - "path": "protocols/bgp/group/neighbor/description" - } - ], - "name": "description", - "path": "protocols/bgp/group/neighbor", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "peer-as", - "path": "protocols/bgp/group/neighbor", - "type": "leaf" - } - ], - "key": "name", - "name": "neighbor", - "ordered-by": "user", - "path": "protocols/bgp/group", - "type": "list" - } - ], - "key": "name", - "name": "group", - "ordered-by": "user", - "path": "protocols/bgp", - "type": "list" - } - ], - "name": "bgp", - "path": "protocols", - "type": "container" - }, - { - "children": [ - { - "default": "mpls", - "enums": [ - { - "id": "mpls", - "path": "protocols/evpn/encapsulation", - "value": 0 - }, - { - "id": "vxlan", - "path": "protocols/evpn/encapsulation", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "encapsulation", - "path": "protocols/evpn", - "type": "leaf" - }, - { - "default": "ingress-replication", - "enums": [ - { - "id": "ingress-replication", - "path": "protocols/evpn/multicast-mode", - "value": 0 - } - ], - "leaf-type": "enumeration", - "name": "multicast-mode", - "path": "protocols/evpn", - "type": "leaf" - }, - { - "enums": [ - { - "id": "advertise", - "path": "protocols/evpn/default-gateway", - "value": 0 - }, - { - "id": "no-gateway-community", - "path": "protocols/evpn/default-gateway", - "value": 1 - }, - { - "id": "do-not-advertise", - "path": "protocols/evpn/default-gateway", - "value": 2 - } - ], - "leaf-type": "enumeration", - "name": "default-gateway", - "path": "protocols/evpn", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "extended-vni-list", - "path": "protocols/evpn", - "type": "leaf-list" - }, - { - "leaf-type": "empty", - "name": "no-core-isolation", - "path": "protocols/evpn", - "type": "leaf" - } - ], - "name": "evpn", - "path": "protocols", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "protocols/lldp/interface", - "type": "leaf" - } - ], - "key": "name", - "name": "interface", - "ordered-by": "user", - "path": "protocols/lldp", - "type": "list" - } - ], - "name": "lldp", - "path": "protocols", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "protocols/igmp-snooping/vlan", - "type": "leaf" - } - ], - "key": "name", - "name": "vlan", - "ordered-by": "user", - "path": "protocols/igmp-snooping", - "type": "list" - } - ], - "name": "igmp-snooping", - "path": "protocols", - "type": "container" - } - ], - "name": "protocols", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "routing-instances/instance", - "type": "leaf" - }, - { - "enums": [ - { - "id": "forwarding", - "path": "routing-instances/instance/instance-type", - "value": 0 - }, - { - "id": "vrf", - "path": "routing-instances/instance/instance-type", - "value": 1 - }, - { - "id": "no-forwarding", - "path": "routing-instances/instance/instance-type", - "value": 2 - }, - { - "id": "l2vpn", - "path": "routing-instances/instance/instance-type", - "value": 3 - }, - { - "id": "vpls", - "path": "routing-instances/instance/instance-type", - "value": 4 - }, - { - "id": "virtual-switch", - "path": "routing-instances/instance/instance-type", - "value": 5 - }, - { - "id": "l2backhaul-vpn", - "path": "routing-instances/instance/instance-type", - "value": 6 - }, - { - "id": "virtual-router", - "path": "routing-instances/instance/instance-type", - "value": 7 - }, - { - "id": "layer2-control", - "path": "routing-instances/instance/instance-type", - "value": 8 - }, - { - "id": "mpls-internet-multicast", - "path": "routing-instances/instance/instance-type", - "value": 9 - }, - { - "id": "evpn", - "path": "routing-instances/instance/instance-type", - "value": 10 - }, - { - "id": "mpls-forwarding", - "path": "routing-instances/instance/instance-type", - "value": 11 - }, - { - "id": "evpn-vpws", - "path": "routing-instances/instance/instance-type", - "value": 12 - } - ], - "leaf-type": "enumeration", - "name": "instance-type", - "path": "routing-instances/instance", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "routing-instances/instance/interface", - "type": "leaf" - } - ], - "key": "name", - "name": "interface", - "path": "routing-instances/instance", - "type": "list" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "rd-type", - "path": "routing-instances/instance/route-distinguisher", - "type": "leaf" - } - ], - "name": "route-distinguisher", - "path": "routing-instances/instance", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "community", - "path": "routing-instances/instance/vrf-target", - "type": "leaf" - } - ], - "name": "vrf-target", - "path": "routing-instances/instance", - "type": "container" - }, - { - "name": "vrf-table-label", - "path": "routing-instances/instance", - "type": "container" - }, - { - "children": [ - { - "name": "auto-export", - "path": "routing-instances/instance/routing-options", - "type": "container" - } - ], - "name": "routing-options", - "path": "routing-instances/instance", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "export", - "ordered-by": "user", - "path": "routing-instances/instance/protocols/ospf", - "type": "leaf-list" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:areaid", - "name": "name", - "path": "routing-instances/instance/protocols/ospf/area", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "union", - "name": "name", - "path": "routing-instances/instance/protocols/ospf/area/interface", - "type": "leaf", - "types": [ - { - "path": "routing-instances/instance/protocols/ospf/area/interface/name", - "type": "string" - }, - { - "path": "routing-instances/instance/protocols/ospf/area/interface/name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - }, - { - "leaf-type": "union", - "name": "metric", - "path": "routing-instances/instance/protocols/ospf/area/interface", - "type": "leaf", - "types": [ - { - "path": "routing-instances/instance/protocols/ospf/area/interface/metric", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "routing-instances/instance/protocols/ospf/area/interface/metric", - "ranges": [ - { - "max": 65535, - "min": 1, - "path": "routing-instances/instance/protocols/ospf/area/interface/metric" - } - ], - "type": "uint16" - } - ] - } - ], - "key": "name", - "name": "interface", - "ordered-by": "user", - "path": "routing-instances/instance/protocols/ospf/area", - "type": "list" - } - ], - "key": "name", - "name": "area", - "ordered-by": "user", - "path": "routing-instances/instance/protocols/ospf", - "type": "list" - } - ], - "name": "ospf", - "path": "routing-instances/instance/protocols", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "enums": [ - { - "id": "gateway-address", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/advertise", - "value": 0 - }, - { - "id": "direct-nexthop", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/advertise", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "advertise", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes", - "type": "leaf" - }, - { - "enums": [ - { - "id": "mpls", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/encapsulation", - "value": 0 - }, - { - "id": "vxlan", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/encapsulation", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "encapsulation", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "vni", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes", - "type": "leaf", - "types": [ - { - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/vni", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/vni", - "ranges": [ - { - "max": 16777214, - "min": 1, - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes/vni" - } - ], - "type": "uint32" - } - ] - }, - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "export", - "ordered-by": "user", - "path": "routing-instances/instance/protocols/evpn/ip-prefix-routes", - "type": "leaf-list" - } - ], - "name": "ip-prefix-routes", - "path": "routing-instances/instance/protocols/evpn", - "type": "container" - } - ], - "name": "evpn", - "path": "routing-instances/instance/protocols", - "type": "container" - } - ], - "name": "protocols", - "path": "routing-instances/instance", - "type": "container" - } - ], - "key": "name", - "name": "instance", - "path": "routing-instances", - "type": "list" - } - ], - "name": "routing-instances", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipprefix", - "name": "name", - "path": "routing-options/static/route", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "next-hop", - "ordered-by": "user", - "path": "routing-options/static/route", - "type": "leaf-list", - "types": [ - { - "path": "routing-options/static/route/next-hop", - "type": "string" - }, - { - "path": "routing-options/static/route/next-hop", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - } - ], - "key": "name", - "name": "route", - "ordered-by": "user", - "path": "routing-options/static", - "type": "list" - } - ], - "name": "static", - "path": "routing-options", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:ipv4addr", - "name": "router-id", - "path": "routing-options", - "type": "leaf" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "export", - "ordered-by": "user", - "path": "routing-options/forwarding-table", - "type": "leaf-list" - }, - { - "leaf-type": "empty", - "name": "ecmp-fast-reroute", - "path": "routing-options/forwarding-table", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "empty", - "name": "evpn", - "path": "routing-options/forwarding-table/chained-composite-next-hop/ingress", - "type": "leaf" - } - ], - "name": "ingress", - "path": "routing-options/forwarding-table/chained-composite-next-hop", - "type": "container" - } - ], - "name": "chained-composite-next-hop", - "path": "routing-options/forwarding-table", - "type": "container" - } - ], - "name": "forwarding-table", - "path": "routing-options", - "type": "container" - } - ], - "name": "routing-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "location", - "path": "snmp", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "contact", - "path": "snmp", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "snmp/community", - "type": "leaf" - }, - { - "enums": [ - { - "id": "read-only", - "path": "snmp/community/authorization", - "value": 0 - }, - { - "id": "read-write", - "path": "snmp/community/authorization", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "authorization", - "path": "snmp/community", - "type": "leaf" - } - ], - "key": "name", - "name": "community", - "ordered-by": "user", - "path": "snmp", - "type": "list" - } - ], - "name": "snmp", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "interface-name", - "path": "switch-options/vtep-source-interface", - "type": "leaf", - "types": [ - { - "path": "switch-options/vtep-source-interface/interface-name", - "type": "string" - }, - { - "path": "switch-options/vtep-source-interface/interface-name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - } - ], - "name": "vtep-source-interface", - "path": "switch-options", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "rd-type", - "path": "switch-options/route-distinguisher", - "type": "leaf" - } - ], - "name": "route-distinguisher", - "path": "switch-options", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "community", - "path": "switch-options/vrf-target", - "type": "leaf" - }, - { - "name": "auto", - "path": "switch-options/vrf-target", - "type": "container" - } - ], - "name": "vrf-target", - "path": "switch-options", - "type": "container" - } - ], - "name": "switch-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/login/user", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "uid", - "path": "system/login/user", - "type": "leaf", - "types": [ - { - "path": "system/login/user/uid", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "system/login/user/uid", - "ranges": [ - { - "max": 64000, - "min": 100, - "path": "system/login/user/uid" - } - ], - "type": "uint32" - } - ] - }, - { - "leaf-type": "string", - "name": "class", - "path": "system/login/user", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 128, - "min": 1, - "path": "system/login/user/authentication/encrypted-password" - } - ], - "name": "encrypted-password", - "path": "system/login/user/authentication", - "type": "leaf" - } - ], - "name": "authentication", - "path": "system/login/user", - "type": "container" - } - ], - "key": "name", - "name": "user", - "path": "system/login", - "type": "list" - }, - { - "leaf-type": "string", - "lengths": [ - { - "max": 2048, - "min": 1, - "path": "system/login/message" - } - ], - "name": "message", - "path": "system/login", - "type": "leaf" - } - ], - "name": "login", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 128, - "min": 1, - "path": "system/root-authentication/encrypted-password" - } - ], - "name": "encrypted-password", - "path": "system/root-authentication", - "type": "leaf" - } - ], - "name": "root-authentication", - "path": "system", - "type": "container" - }, - { - "leaf-type": "string", - "lengths": [ - { - "max": 255, - "min": 1, - "path": "system/host-name" - } - ], - "name": "host-name", - "path": "system", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "enums": [ - { - "id": "allow", - "path": "system/services/ssh/root-login", - "value": 0 - }, - { - "id": "deny", - "path": "system/services/ssh/root-login", - "value": 1 - }, - { - "id": "deny-password", - "path": "system/services/ssh/root-login", - "value": 2 - } - ], - "leaf-type": "enumeration", - "name": "root-login", - "path": "system/services/ssh", - "type": "leaf" - } - ], - "name": "ssh", - "path": "system/services", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "default": "5", - "leaf-type": "union", - "name": "max-connections", - "path": "system/services/extension-service/request-response/grpc", - "type": "leaf", - "types": [ - { - "path": "system/services/extension-service/request-response/grpc/max-connections", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "system/services/extension-service/request-response/grpc/max-connections", - "ranges": [ - { - "max": 30, - "min": 1, - "path": "system/services/extension-service/request-response/grpc/max-connections" - } - ], - "type": "uint32" - } - ] - } - ], - "name": "grpc", - "path": "system/services/extension-service/request-response", - "type": "container" - } - ], - "name": "request-response", - "path": "system/services/extension-service", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipprefix-optional", - "name": "address", - "ordered-by": "user", - "path": "system/services/extension-service/notification/allow-clients", - "type": "leaf-list" - } - ], - "name": "allow-clients", - "path": "system/services/extension-service/notification", - "type": "container" - } - ], - "name": "notification", - "path": "system/services/extension-service", - "type": "container" - } - ], - "name": "extension-service", - "path": "system/services", - "type": "container" - }, - { - "children": [ - { - "name": "ssh", - "path": "system/services/netconf", - "type": "container" - } - ], - "name": "netconf", - "path": "system/services", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "default": "3000", - "leaf-type": "union", - "name": "port", - "path": "system/services/rest/http", - "type": "leaf", - "types": [ - { - "path": "system/services/rest/http/port", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "system/services/rest/http/port", - "ranges": [ - { - "max": 65535, - "min": 1024, - "path": "system/services/rest/http/port" - } - ], - "type": "uint32" - } - ] - } - ], - "name": "http", - "path": "system/services/rest", - "type": "container" - }, - { - "leaf-type": "empty", - "name": "enable-explorer", - "path": "system/services/rest", - "type": "leaf" - } - ], - "name": "rest", - "path": "system/services", - "type": "container" - } - ], - "name": "services", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/syslog/user", - "type": "leaf" - }, - { - "children": [ - { - "enums": [ - { - "id": "any", - "path": "system/syslog/user/contents/name", - "value": 0 - }, - { - "id": "authorization", - "path": "system/syslog/user/contents/name", - "value": 1 - }, - { - "id": "daemon", - "path": "system/syslog/user/contents/name", - "value": 2 - }, - { - "id": "ftp", - "path": "system/syslog/user/contents/name", - "value": 3 - }, - { - "id": "ntp", - "path": "system/syslog/user/contents/name", - "value": 4 - }, - { - "id": "security", - "path": "system/syslog/user/contents/name", - "value": 5 - }, - { - "id": "kernel", - "path": "system/syslog/user/contents/name", - "value": 6 - }, - { - "id": "user", - "path": "system/syslog/user/contents/name", - "value": 7 - }, - { - "id": "dfc", - "path": "system/syslog/user/contents/name", - "value": 8 - }, - { - "id": "external", - "path": "system/syslog/user/contents/name", - "value": 9 - }, - { - "id": "firewall", - "path": "system/syslog/user/contents/name", - "value": 10 - }, - { - "id": "pfe", - "path": "system/syslog/user/contents/name", - "value": 11 - }, - { - "id": "conflict-log", - "path": "system/syslog/user/contents/name", - "value": 12 - }, - { - "id": "change-log", - "path": "system/syslog/user/contents/name", - "value": 13 - }, - { - "id": "interactive-commands", - "path": "system/syslog/user/contents/name", - "value": 14 - } - ], - "leaf-type": "enumeration", - "name": "name", - "path": "system/syslog/user/contents", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "emergency", - "path": "system/syslog/user/contents", - "type": "leaf" - } - ], - "key": "name", - "name": "contents", - "path": "system/syslog/user", - "type": "list" - } - ], - "key": "name", - "name": "user", - "ordered-by": "user", - "path": "system/syslog", - "type": "list" - }, - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 1024, - "min": 1, - "path": "system/syslog/file/name" - } - ], - "name": "name", - "path": "system/syslog/file", - "type": "leaf" - }, - { - "children": [ - { - "enums": [ - { - "id": "any", - "path": "system/syslog/file/contents/name", - "value": 0 - }, - { - "id": "authorization", - "path": "system/syslog/file/contents/name", - "value": 1 - }, - { - "id": "daemon", - "path": "system/syslog/file/contents/name", - "value": 2 - }, - { - "id": "ftp", - "path": "system/syslog/file/contents/name", - "value": 3 - }, - { - "id": "ntp", - "path": "system/syslog/file/contents/name", - "value": 4 - }, - { - "id": "security", - "path": "system/syslog/file/contents/name", - "value": 5 - }, - { - "id": "kernel", - "path": "system/syslog/file/contents/name", - "value": 6 - }, - { - "id": "user", - "path": "system/syslog/file/contents/name", - "value": 7 - }, - { - "id": "dfc", - "path": "system/syslog/file/contents/name", - "value": 8 - }, - { - "id": "external", - "path": "system/syslog/file/contents/name", - "value": 9 - }, - { - "id": "firewall", - "path": "system/syslog/file/contents/name", - "value": 10 - }, - { - "id": "pfe", - "path": "system/syslog/file/contents/name", - "value": 11 - }, - { - "id": "conflict-log", - "path": "system/syslog/file/contents/name", - "value": 12 - }, - { - "id": "change-log", - "path": "system/syslog/file/contents/name", - "value": 13 - }, - { - "id": "interactive-commands", - "path": "system/syslog/file/contents/name", - "value": 14 - } - ], - "leaf-type": "enumeration", - "name": "name", - "path": "system/syslog/file/contents", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "any", - "path": "system/syslog/file/contents", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "notice", - "path": "system/syslog/file/contents", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "info", - "path": "system/syslog/file/contents", - "type": "leaf" - } - ], - "key": "name", - "name": "contents", - "path": "system/syslog/file", - "type": "list" - } - ], - "key": "name", - "name": "file", - "ordered-by": "user", - "path": "system/syslog", - "type": "list" - } - ], - "name": "syslog", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/extensions/providers", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/extensions/providers/license-type", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "deployment-scope", - "ordered-by": "user", - "path": "system/extensions/providers/license-type", - "type": "leaf-list" - } - ], - "key": "name", - "name": "license-type", - "ordered-by": "user", - "path": "system/extensions/providers", - "type": "list" - } - ], - "key": "name", - "name": "providers", - "ordered-by": "user", - "path": "system/extensions", - "type": "list" - } - ], - "name": "extensions", - "path": "system", - "type": "container" - } - ], - "name": "system", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 64, - "min": 2, - "path": "vlans/vlan/name" - } - ], - "name": "name", - "path": "vlans/vlan", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "vlan-id", - "path": "vlans/vlan", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "l3-interface", - "path": "vlans/vlan", - "type": "leaf", - "types": [ - { - "path": "vlans/vlan/l3-interface", - "type": "string" - }, - { - "path": "vlans/vlan/l3-interface", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - }, - { - "children": [ - { - "leaf-type": "union", - "name": "vni", - "path": "vlans/vlan/vxlan", - "type": "leaf", - "types": [ - { - "path": "vlans/vlan/vxlan/vni", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "vlans/vlan/vxlan/vni", - "ranges": [ - { - "max": 16777214, - "min": 0, - "path": "vlans/vlan/vxlan/vni" - } - ], - "type": "uint32" - } - ] - } - ], - "name": "vxlan", - "path": "vlans/vlan", - "type": "container" - } - ], - "key": "name", - "name": "vlan", - "path": "vlans", - "type": "list" - } - ], - "name": "vlans", - "path": "", - "type": "container" - } - ], - "config": "true", - "name": "configuration", - "path": "", - "type": "container" - } - ], - "name": "root", - "path": "", - "type": "container" - } -}` - -var idx map[string]*NodeInfo - -func TestMain(m *testing.M) { - // setup - var err error - idx, err = UnmarshalTrimmedSchemaIndex(TrimmedSchemaJSON) - if err != nil { - panic(err) - } - os.Exit(m.Run()) -} - -func TestCreateDiffPatch_ReplaceHostName_DeleteThenCreate(t *testing.T) { - name := "base-config" - - editLeaf := map[string]Change{ - `configuration/groups[name="base-config"]/system/host-name`: { - Op: Replace, - OldVal: "dc1-leaf1", - NewVal: "dc1-leaf1-test", - }, - } - - correctDiff := ` - - dc1-leaf1-test - - -` - - diff, err := CreateDiffPatch(editLeaf, name) - if err != nil { - t.Fatalf("CreateDiffPatch returned error: %v", err) - } - - normalizeXML := func(s string) string { - return strings.Join(strings.Fields(s), "") - } - - if normalizeXML(string(diff)) != normalizeXML(correctDiff) { - t.Fatalf("diff mismatch\n--- got ---\n%s\n--- want ---\n%s\n", string(diff), correctDiff) - } -} - -func TestCreateDiffPatch_KeyedListRenameWithDescendantsUsesEntryOperations(t *testing.T) { - name := "base-config" - - editLeaf := map[string]Change{ - `configuration/groups[name="base-config"]/system/login/user[name="regress"]/name`: { - Op: Delete, - OldVal: "regress", - }, - `configuration/groups[name="base-config"]/system/login/user[name="regress"]/uid`: { - Op: Delete, - OldVal: "928", - }, - `configuration/groups[name="base-config"]/system/login/user[name="regress"]/class`: { - Op: Delete, - OldVal: "superuser", - }, - `configuration/groups[name="base-config"]/system/login/user[name="regress"]/authentication/encrypted-password`: { - Op: Delete, - OldVal: "$1$kPU..$w.4FGRAGanJ8U4Yq6sbj7.", - }, - `configuration/groups[name="base-config"]/system/login/user[name="vinay"]/name`: { - Op: Create, - NewVal: "vinay", - }, - `configuration/groups[name="base-config"]/system/login/user[name="vinay"]/uid`: { - Op: Create, - NewVal: "928", - }, - `configuration/groups[name="base-config"]/system/login/user[name="vinay"]/class`: { - Op: Create, - NewVal: "superuser", - }, - `configuration/groups[name="base-config"]/system/login/user[name="vinay"]/authentication/encrypted-password`: { - Op: Create, - NewVal: "$1$kPU..$w.4FGRAGanJ8U4Yq6sbj7.", - }, - } - - correctDiff := ` - - - - regress - - - - - - - - vinay - - $1$kPU..$w.4FGRAGanJ8U4Yq6sbj7. - - superuser - 928 - - - - -` - - diff, err := CreateDiffPatch(editLeaf, name) - if err != nil { - t.Fatalf("CreateDiffPatch returned error: %v", err) - } - - if string(diff) != correctDiff { - t.Fatalf("diff mismatch\n--- got ---\n%s\n--- want ---\n%s\n", string(diff), correctDiff) - } -} - -func TestCreateDiffPatch_StructuralKeyRenameUsesEntryOperations(t *testing.T) { - name := "base-config" - - editLeaf := map[string]Change{ - `configuration/groups[name="base-config"]/system/syslog/file[name="security"]/name`: { - Op: Delete, - OldVal: "security", - }, - `configuration/groups[name="base-config"]/system/syslog/file[name="vinay"]/name`: { - Op: Create, - NewVal: "vinay", - }, - } - - correctDiff := ` - - - - security - - - vinay - - - - -` - - diff, err := CreateDiffPatch(editLeaf, name) - if err != nil { - t.Fatalf("CreateDiffPatch returned error: %v", err) - } - - if string(diff) != correctDiff { - t.Fatalf("diff mismatch\n--- got ---\n%s\n--- want ---\n%s\n", string(diff), correctDiff) - } -} - -func TestCreateDiffPatch_CompoundKeyDeleteIncludesChoiceSibling(t *testing.T) { - // community under then/community is a list with compound key - // "choice-ident choice-value community-name". Junos requires the - // choice-ident element () to be present in deletes. - name := "base-config" - - editLeaf := map[string]Change{ - `configuration/groups[name="base-config"]/policy-options/policy-statement[name="PS1"]/term[name="T1"]/then/community[community-name=OC-STD]/add`: { - Op: Delete, - OldVal: "", - }, - `configuration/groups[name="base-config"]/policy-options/policy-statement[name="PS1"]/term[name="T1"]/then/community[community-name=OC-STD]/community-name`: { - Op: Delete, - OldVal: "OC-STD", - }, - } - - diff, err := CreateDiffPatch(editLeaf, name) - if err != nil { - t.Fatalf("CreateDiffPatch returned error: %v", err) - } - - got := string(diff) - - // The parent must have the delete operation (promoted from key). - if !strings.Contains(got, ``) { - t.Fatalf("expected parent-level delete on , got:\n%s", got) - } - // The sibling must be present WITHOUT an operation (stripped by - // stripRedundantChildOps since parent is delete). - if !strings.Contains(got, ``) { - t.Fatalf("expected choice-ident sibling, got:\n%s", got) - } - // The key must be present as a structural identifier. - if !strings.Contains(got, `OC-STD`) { - t.Fatalf("expected OC-STD key child, got:\n%s", got) - } - - t.Logf("compound-key delete output:\n%s", got) -} - -func TestLeafMapWithSchema_CompoundKeyEmitsAllChildren(t *testing.T) { - // Verify LeafMapWithSchema emits both the key leaf and the empty - // choice-ident leaf for compound-key list entries. - tree := &Node{Tag: "configuration", Children: []*Node{ - {Tag: "policy-options", Children: []*Node{ - {Tag: "policy-statement", Children: []*Node{ - {Tag: "name", Text: "PS1"}, - {Tag: "term", Children: []*Node{ - {Tag: "name", Text: "T1"}, - {Tag: "then", Children: []*Node{ - {Tag: "community", Children: []*Node{ - {Tag: "add"}, - {Tag: "community-name", Text: "OC-STD"}, - }}, - }}, - }}, - }}, - }}, - }} - - lm := LeafMapWithSchema(tree, idx) - - // community-name should appear with a keyed parent segment - var foundKey, foundAdd bool - for path, val := range lm { - t.Logf(" %s = %q", path, val) - if strings.Contains(path, "community[community-name=OC-STD]/community-name") { - foundKey = true - } - if strings.Contains(path, "community[community-name=OC-STD]/add") { - foundAdd = true - } - } - - if !foundKey { - t.Fatal("expected leaf map entry for community-name under keyed community") - } - if !foundAdd { - t.Fatal("expected leaf map entry for add (choice-ident) under keyed community") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/path.go b/tf/providers/terraform-provider-junos-qfx/patch/path.go deleted file mode 100644 index 160021fa..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/path.go +++ /dev/null @@ -1,116 +0,0 @@ -package patch - -import "strings" - -// splitPathRespectingQuotes splits a path string on '/' while treating -// bracket predicates as opaque — a '/' inside "[name=ge-0/0/0]" is not -// treated as a separator. -// -// Example: -// -// "interfaces/interface[name=ge-0/0/0]/unit[name=0]/description" -// → ["interfaces", "interface[name=ge-0/0/0]", "unit[name=0]", "description"] -func splitPathRespectingQuotes(path string) []string { - var segments []string - var current strings.Builder - inBracket := false - - for _, ch := range path { - switch { - case !inBracket && ch == '[': - inBracket = true - current.WriteRune(ch) - case inBracket && ch == ']': - inBracket = false - current.WriteRune(ch) - case !inBracket && ch == '/': - if current.Len() > 0 { - segments = append(segments, current.String()) - current.Reset() - } - default: - current.WriteRune(ch) - } - } - if current.Len() > 0 { - segments = append(segments, current.String()) - } - return segments -} - -// parseSegment splits a path segment into its tag and optional key predicate. -// -// "interface[name=ge-0/0/0]" → ("interface", "name", "ge-0/0/0") -// "description" → ("description", "", "") -func parseSegment(seg string) (tag, keyName, keyValue string) { - idx := strings.Index(seg, "[") - if idx == -1 { - return seg, "", "" - } - tag = seg[:idx] - predicate := seg[idx+1 : len(seg)-1] // strip outer [ and ] - eqIdx := strings.Index(predicate, "=") - if eqIdx == -1 { - return tag, "", "" - } - keyName = predicate[:eqIdx] - keyValue = strings.Trim(predicate[eqIdx+1:], "'\"") // strip optional quotes - return -} - -// ensurePath walks the node tree starting at current, creating intermediate -// nodes as needed for each segment, and returns the node at the end of the -// path. -// -// For keyed segments (e.g. "interface[name=ge-0/0/0]") it: -// 1. Looks for an existing child with matching tag AND key child value. -// 2. If not found, creates the element and injects a ge-0/0/0 -// child immediately so subsequent sibling leaf writes land in the right -// list entry. -func ensurePath(current *Node, segments []string) *Node { - for _, seg := range segments { - tag, keyName, keyValue := parseSegment(seg) - - // Search for an existing child that matches this segment - var found *Node - for _, child := range current.Children { - if child.Tag != tag { - continue - } - // Plain element — first match wins - if keyName == "" { - found = child - break - } - // Keyed element — must also match the key value - if findKeyChild(child, keyName) == keyValue { - found = child - break - } - } - - if found == nil { - found = &Node{Tag: tag, Parent: current} - if keyName != "" { - // Inject key child as the first child of this list entry - keyNode := &Node{Tag: keyName, Text: keyValue, Parent: found} - found.Children = append(found.Children, keyNode) - } - current.Children = append(current.Children, found) - } - - current = found - } - return current -} - -// findKeyChild returns the text of the first child whose tag matches keyName, -// used to disambiguate keyed list entries during ensurePath traversal. -func findKeyChild(node *Node, keyName string) string { - for _, child := range node.Children { - if child.Tag == keyName { - return child.Text - } - } - return "" -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/process_schema.go b/tf/providers/terraform-provider-junos-qfx/patch/process_schema.go deleted file mode 100644 index 9f07eec2..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/process_schema.go +++ /dev/null @@ -1,357 +0,0 @@ -package patch - -import ( - "encoding/json" - "strings" -) - -// ------------------------- Type Definitions [START] ------------------------- - -type NodeKind uint8 - -const ( - KindContainer NodeKind = iota - KindList - KindLeaf - KindLeafList -) - -type LeafBase uint8 - -const ( - LeafString LeafBase = iota - LeafBool - LeafInt - LeafUint - LeafEnum - LeafUnion - LeafOther -) - -// Raw JSON node -type SchemaNode struct { - Name string `json:"name"` - Type string `json:"type"` // container | list | leaf - Path string `json:"path"` // often parent path - Key string `json:"key"` // list key - LeafType string `json:"leaf-type"` // leaf-only: string, union, etc. - OrderedBy string `json:"ordered-by"` // "user" for ordered leaf-lists/lists - - Children []SchemaNode `json:"children"` - // Union branches (when leaf-type == "union") - Types []UnionType `json:"types"` - // Constraints (sometimes on leaves, sometimes inside union branches) - Lengths []LenRange `json:"lengths"` - Ranges []NumRange `json:"ranges"` - Patterns []string `json:"patterns"` - Enums []EnumValue `json:"enums"` // if your trimmed schema ever includes enums -} - -type UnionType struct { - Type string `json:"type"` - Path string `json:"path"` - Patterns []string `json:"patterns"` - Ranges []NumRange `json:"ranges"` - Lengths []LenRange `json:"lengths"` - Enums []EnumValue `json:"enums"` -} - -type NumRange struct { - Min *float64 `json:"min"` - Max *float64 `json:"max"` - Path string `json:"path"` -} - -type LenRange struct { - Min *int `json:"min"` - Max *int `json:"max"` - Path string `json:"path"` -} - -type EnumValue struct { - Name string `json:"name"` - Value any `json:"value"` -} - -type NodeInfo struct { - Path string - Name string - Kind NodeKind - Parent string - Children []string - // Lists - ListKey string - ListKeyPath string - // Ordered-by user (meaningful ordering) - OrderedByUser bool - // Leaves - Leaf LeafInfo -} - -type LeafInfo struct { - Base LeafBase - Union []UnionBranch - Patterns []string - Ranges []NumRange - Lengths []LenRange - Enums map[string]struct{} // canonical set of enum names (if present) -} - -type UnionBranch struct { - Base LeafBase - Patterns []string - Ranges []NumRange - Lengths []LenRange - Enums map[string]struct{} -} - -type TrimmedSchemaWrapper struct { - Path string `json:"path"` - Root SchemaRoot `json:"root"` -} - -type SchemaRoot struct { - Children []SchemaNode `json:"children"` -} - -// ------------------------- Type Definitions [END] ------------------------- - -// ------------------------- Process Trimmed Schema [START] ------------------------- - -// Go raw string literal -> compiled index -func UnmarshalTrimmedSchemaIndex(trimmedSchemaJSON string) (map[string]*NodeInfo, error) { - - var w TrimmedSchemaWrapper - if err := json.Unmarshal([]byte(trimmedSchemaJSON), &w); err != nil { - return nil, err - } - - roots := w.Root.Children - - idx := make(map[string]*NodeInfo) - - // Walk and compile - var walk func(n SchemaNode, parentFull string) - - walk = func(n SchemaNode, parentFull string) { - full := canonicalFullPath(n, parentFull) - if full == "" { - // still walk children (some schemas have virtual root nodes) - for _, c := range n.Children { - walk(c, parentFull) - } - return - } - - info := idx[full] - if info == nil { - info = &NodeInfo{ - Path: full, - Name: n.Name, - Parent: parentFull, - } - idx[full] = info - } else { - // if collisions happen, keep existing and merge below - if info.Name == "" { - info.Name = n.Name - } - if info.Parent == "" { - info.Parent = parentFull - } - } - - // Kind - switch n.Type { - case "container": - info.Kind = KindContainer - case "list": - info.Kind = KindList - case "leaf": - info.Kind = KindLeaf - case "leaf-list": - info.Kind = KindLeafList - default: - // unknown: treat like container-ish to keep traversal working - info.Kind = KindContainer - } - - // List metadata - if info.Kind == KindList { - info.ListKey = n.Key - if n.Key != "" { - info.ListKeyPath = joinPath(full, n.Key) - } - } - - // Ordered-by user - if n.OrderedBy == "user" { - info.OrderedByUser = true - } - - // Leaf metadata - if info.Kind == KindLeaf || info.Kind == KindLeafList { - compileLeafInfo(&info.Leaf, n) - } - - // Children bookkeeping - for _, c := range n.Children { - childFull := canonicalFullPath(c, full) - - // record child path - if childFull != "" { - info.Children = appendUnique(info.Children, childFull) - } - - walk(c, full) - } - } - - for _, r := range roots { - // Some trimmed schemas include a top-level node with path:"" and children; still safe. - walk(r, "") - } - - return idx, nil -} - -// ------------------------- Process Trimmed Schema [END] ------------------------- - -// ------------------------- Helpers [START] ------------------------- - -func normalizePath(p string) string { - p = strings.Trim(p, "/") - - // Strip "configuration" root in both forms - if p == "configuration" { - return "" - } - p = strings.TrimPrefix(p, "configuration/") - return p -} - -func canonicalFullPath(n SchemaNode, parentFull string) string { - if n.Name == "" { - return "" - } - - parentFull = normalizePath(parentFull) - - // Your JSON "path" is usually the parent path (often includes "configuration/") - p := normalizePath(n.Path) - - switch n.Type { - case "leaf", "leaf-list": - // leaf full path should be parent-path + leaf name - if p != "" { - return joinPath(p, n.Name) - } - return joinPath(parentFull, n.Name) - - default: // container, list - // container/list full path should be its parent + its name - // Prefer n.Path if present (because your JSON is anchored under configuration) - if p != "" { - return joinPath(p, n.Name) - } - return joinPath(parentFull, n.Name) - } -} - -func joinPath(a, b string) string { - a = normalizePath(a) - b = normalizePath(b) - if a == "" { - return b - } - if b == "" { - return a - } - return a + "/" + b -} - -func appendUnique(xs []string, s string) []string { - for _, x := range xs { - if x == s { - return xs - } - } - return append(xs, s) -} - -func compileLeafInfo(out *LeafInfo, n SchemaNode) { - base := leafBaseFromLeafType(n.LeafType) - out.Base = base - - // Direct constraints on the leaf node - out.Patterns = append(out.Patterns, n.Patterns...) - out.Ranges = append(out.Ranges, n.Ranges...) - out.Lengths = append(out.Lengths, n.Lengths...) - out.Enums = enumSetFromEnumValues(n.Enums, out.Enums) - - // Union branches - if base == LeafUnion { - for _, br := range n.Types { - ub := UnionBranch{ - Base: leafBaseFromLeafType(br.Type), - Patterns: append([]string(nil), br.Patterns...), - Ranges: append([]NumRange(nil), br.Ranges...), - Lengths: append([]LenRange(nil), br.Lengths...), - Enums: enumSetFromEnumValues(br.Enums, nil), - } - out.Union = append(out.Union, ub) - - // Often useful to have a flattened view too: - out.Patterns = append(out.Patterns, br.Patterns...) - out.Ranges = append(out.Ranges, br.Ranges...) - out.Lengths = append(out.Lengths, br.Lengths...) - out.Enums = mergeEnumSets(out.Enums, ub.Enums) - } - } -} - -func leafBaseFromLeafType(t string) LeafBase { - switch strings.ToLower(strings.TrimSpace(t)) { - case "string": - return LeafString - case "boolean", "bool": - return LeafBool - case "int8", "int16", "int32", "int64", "int": - return LeafInt - case "uint8", "uint16", "uint32", "uint64", "uint": - return LeafUint - case "enumeration", "enum": - return LeafEnum - case "union": - return LeafUnion - default: - return LeafOther - } -} - -func enumSetFromEnumValues(vals []EnumValue, existing map[string]struct{}) map[string]struct{} { - if len(vals) == 0 && existing != nil { - return existing - } - if existing == nil { - existing = make(map[string]struct{}) - } - for _, v := range vals { - if v.Name != "" { - existing[v.Name] = struct{}{} - } - } - return existing -} - -func mergeEnumSets(a, b map[string]struct{}) map[string]struct{} { - if a == nil { - return b - } - for k := range b { - a[k] = struct{}{} - } - return a -} - -// ------------------------- Helpers [END] ------------------------- diff --git a/tf/providers/terraform-provider-junos-qfx/patch/schema_phase1_test.go b/tf/providers/terraform-provider-junos-qfx/patch/schema_phase1_test.go deleted file mode 100644 index 63b0b687..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/schema_phase1_test.go +++ /dev/null @@ -1,388 +0,0 @@ -package patch - -import "testing" - -const testTrimmedSchema = `{ - "path": "", - "root": { - "children": [ - { - "name": "configuration", - "type": "container", - "path": "", - "children": [ - { - "name": "foo", - "type": "container", - "path": "", - "children": [ - { - "name": "item", - "type": "list", - "path": "foo", - "key": "address", - "children": [ - { - "name": "address", - "type": "leaf", - "path": "foo/item", - "leaf-type": "string" - }, - { - "name": "value", - "type": "leaf", - "path": "foo/item", - "leaf-type": "string" - } - ] - }, - { - "name": "members", - "type": "leaf-list", - "path": "foo", - "leaf-type": "string" - } - ] - } - ] - } - ] - } -}` - -const testStructuralKeyTrimmedSchema = `{ - "path": "", - "root": { - "children": [ - { - "name": "configuration", - "type": "container", - "path": "", - "children": [ - { - "name": "system", - "type": "container", - "path": "", - "children": [ - { - "name": "syslog", - "type": "container", - "path": "system", - "children": [ - { - "name": "file", - "type": "list", - "path": "system/syslog", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "system/syslog/file", - "leaf-type": "string" - }, - { - "name": "contents", - "type": "list", - "path": "system/syslog/file", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "path": "system/syslog/file/contents", - "leaf-type": "string" - }, - { - "name": "any", - "type": "leaf", - "path": "system/syslog/file/contents", - "leaf-type": "empty" - } - ] - }, - { - "name": "archive", - "type": "container", - "path": "system/syslog/file", - "children": [ - { - "name": "world-readable", - "type": "leaf", - "path": "system/syslog/file/archive", - "leaf-type": "empty" - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } -}` - -func mustTree(t *testing.T, xmlStr string) *Node { - t.Helper() - tree, err := BuildTree([]byte(xmlStr)) - if err != nil { - t.Fatalf("BuildTree error: %v", err) - } - return tree -} - -func mustIdx(t *testing.T) map[string]*NodeInfo { - t.Helper() - return mustIdxFromSchema(t, testTrimmedSchema) -} - -func mustIdxFromSchema(t *testing.T, schema string) map[string]*NodeInfo { - t.Helper() - idx, err := UnmarshalTrimmedSchemaIndex(schema) - if err != nil { - t.Fatalf("UnmarshalTrimmedSchemaIndex error: %v", err) - } - return idx -} - -func TestLeafMapWithSchema_UsesSchemaListKey(t *testing.T) { - idx := mustIdx(t) - xmlStr := ` - - g1 - - -
10.0.0.1
- alpha -
-
-
-
` - - m := LeafMapWithSchema(mustTree(t, xmlStr), idx) - path := `configuration/groups[name=g1]/foo/item[address=10.0.0.1]/value` - if got := m[path]; got != "alpha" { - t.Fatalf("expected %s => alpha, got %q", path, got) - } - - // Key leaf is now also emitted (needed for new/removed entry detection) - keyLeafPath := `configuration/groups[name=g1]/foo/item[address=10.0.0.1]/address` - if got := m[keyLeafPath]; got != "10.0.0.1" { - t.Fatalf("expected key leaf %s => 10.0.0.1, got %q", keyLeafPath, got) - } -} - -func TestLeafMapWithSchema_KeyOnlyListEmitsKeyLeaf(t *testing.T) { - idx := mustIdx(t) - xmlStr := ` - - g1 - - -
10.0.0.1
-
-
-
-
` - - m := LeafMapWithSchema(mustTree(t, xmlStr), idx) - path := `configuration/groups[name=g1]/foo/item[address=10.0.0.1]/address` - if got := m[path]; got != "10.0.0.1" { - t.Fatalf("expected %s => 10.0.0.1, got %q", path, got) - } -} - -func TestLeafMapWithSchema_LeafListSetDiff(t *testing.T) { - idx := mustIdx(t) - stateXML := ` - - g1 - - a - c - - -` - planXML := ` - - g1 - - a - b - - -` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - createPath := `configuration/groups[name=g1]/foo/members[value=b]` - deletePath := `configuration/groups[name=g1]/foo/members[value=c]` - commonPath := `configuration/groups[name=g1]/foo/members[value=a]` - - if ch, ok := diff[createPath]; !ok || ch.Op != Create { - t.Fatalf("expected create for %s", createPath) - } - if ch, ok := diff[deletePath]; !ok || ch.Op != Delete { - t.Fatalf("expected delete for %s", deletePath) - } - if _, ok := diff[commonPath]; ok { - t.Fatalf("did not expect diff for unchanged leaf-list value %s", commonPath) - } -} - -func TestBuildTree_PreservesTrailingNewlineInLeafText(t *testing.T) { - xmlStr := `g1banner line -` - - tree := mustTree(t, xmlStr) - idx, err := UnmarshalTrimmedSchemaIndex(TrimmedSchemaJSON) - if err != nil { - t.Fatalf("UnmarshalTrimmedSchemaIndex error: %v", err) - } - - leafMap := LeafMapWithSchema(tree, idx) - path := `configuration/groups[name=g1]/system/login/message` - if got := leafMap[path]; got != "banner line\n" { - t.Fatalf("expected %s => %q, got %q", path, "banner line\n", got) - } -} - -func TestOrderedChanges_DeleteBeforeCreate_AndDepthRules(t *testing.T) { - diffMap := map[string]Change{ - `configuration/groups[name=g1]/foo/bar/baz`: {Op: Delete, OldVal: "x"}, - `configuration/groups[name=g1]/foo/bar`: {Op: Delete, OldVal: "x"}, - `configuration/groups[name=g1]/foo/alpha`: {Op: Replace, OldVal: "a", NewVal: "b"}, - `configuration/groups[name=g1]/foo/new`: {Op: Create, NewVal: "n"}, - } - - ordered := orderedChanges(diffMap) - if len(ordered) != 4 { - t.Fatalf("expected 4 ordered changes, got %d", len(ordered)) - } - - if ordered[0].change.Op != Delete || ordered[1].change.Op != Delete { - t.Fatalf("expected first two operations to be deletes") - } - if pathDepth(ordered[0].path) < pathDepth(ordered[1].path) { - t.Fatalf("expected deeper delete path first: %s then %s", ordered[0].path, ordered[1].path) - } - if ordered[2].change.Op != Replace || ordered[3].change.Op != Create { - t.Fatalf("expected replace before create in trailing operations") - } -} - -func TestComputeDiff_ListKeyRename_ShowsDeleteAndCreate(t *testing.T) { - stateMap := map[string]string{ - `configuration/groups[name=g1]/foo/item[address=10.0.0.1]/value`: "alpha", - } - planMap := map[string]string{ - `configuration/groups[name=g1]/foo/item[address=10.0.0.2]/value`: "alpha", - } - - diff := ComputeDiff(stateMap, planMap) - if len(diff) != 2 { - t.Fatalf("expected 2 changes for key rename, got %d", len(diff)) - } - if diff[`configuration/groups[name=g1]/foo/item[address=10.0.0.1]/value`].Op != Delete { - t.Fatalf("expected delete for old key path") - } - if diff[`configuration/groups[name=g1]/foo/item[address=10.0.0.2]/value`].Op != Create { - t.Fatalf("expected create for new key path") - } -} - -func TestLeafMapWithSchema_StructuralKeyedListEmitsKeyLeaf(t *testing.T) { - idx := mustIdxFromSchema(t, testStructuralKeyTrimmedSchema) - xmlStr := ` - - g1 - - - - security - - interactive-commands - - - - - - - - - -` - - m := LeafMapWithSchema(mustTree(t, xmlStr), idx) - path := `configuration/groups[name=g1]/system/syslog/file[name=security]/name` - if got := m[path]; got != "security" { - t.Fatalf("expected %s => security, got %q", path, got) - } - - // Nested key is now also emitted (contents has material children) - nestedPath := `configuration/groups[name=g1]/system/syslog/file[name=security]/contents[name=interactive-commands]/name` - if got := m[nestedPath]; got != "interactive-commands" { - t.Fatalf("expected nested key %s => interactive-commands, got %q", nestedPath, got) - } -} - -func TestComputeDiff_StructuralListKeyRename_ShowsDeleteAndCreate(t *testing.T) { - idx := mustIdxFromSchema(t, testStructuralKeyTrimmedSchema) - stateXML := ` - - g1 - - - - security - - interactive-commands - - - - - - - - - -` - planXML := ` - - g1 - - - - vinay - - interactive-commands - - - - - - - - - -` - - stateMap := LeafMapWithSchema(mustTree(t, stateXML), idx) - planMap := LeafMapWithSchema(mustTree(t, planXML), idx) - diff := ComputeDiff(stateMap, planMap) - - deletePath := `configuration/groups[name=g1]/system/syslog/file[name=security]/name` - createPath := `configuration/groups[name=g1]/system/syslog/file[name=vinay]/name` - if ch, ok := diff[deletePath]; !ok || ch.Op != Delete { - t.Fatalf("expected delete for %s", deletePath) - } - if ch, ok := diff[createPath]; !ok || ch.Op != Create { - t.Fatalf("expected create for %s", createPath) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/tree.go b/tf/providers/terraform-provider-junos-qfx/patch/tree.go deleted file mode 100644 index cc57eecc..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/tree.go +++ /dev/null @@ -1,73 +0,0 @@ -package patch - -import ( - "bytes" - "encoding/xml" - "fmt" - "io" -) - -// BuildTree parses XML bytes into a *Node tree using a streaming token decoder. -// It handles the XML declaration header produced by xml.Header gracefully. -func BuildTree(xmlBytes []byte) (*Node, error) { - decoder := xml.NewDecoder(bytes.NewReader(xmlBytes)) - - var stack []*Node - var root *Node - - for { - tok, err := decoder.Token() - if err != nil { - if err == io.EOF { - break - } - return nil, fmt.Errorf("failed parsing XML token stream: %w", err) - } - - switch t := tok.(type) { - - case xml.StartElement: - node := &Node{ - Tag: t.Name.Local, - Attrs: make(map[string]string), - } - for _, attr := range t.Attr { - // Skip xmlns declarations — not needed in our tree - if attr.Name.Space == "xmlns" || attr.Name.Local == "xmlns" { - continue - } - node.Attrs[attr.Name.Local] = attr.Value - } - if len(stack) > 0 { - parent := stack[len(stack)-1] - node.Parent = parent - parent.Children = append(parent.Children, node) - } - stack = append(stack, node) - - case xml.EndElement: - if len(stack) == 0 { - return nil, fmt.Errorf("unexpected end element ", t.Name.Local) - } - popped := stack[len(stack)-1] - stack = stack[:len(stack)-1] - // When we pop the last element off the stack it is the root - if len(stack) == 0 { - root = popped - } - - case xml.CharData: - if len(stack) > 0 { - if len(bytes.TrimSpace([]byte(t))) > 0 { - top := stack[len(stack)-1] - top.Text += string(t) - } - } - } - } - - if root == nil { - return nil, fmt.Errorf("no root element found in XML") - } - return root, nil -} diff --git a/tf/providers/terraform-provider-junos-qfx/patch/types.go b/tf/providers/terraform-provider-junos-qfx/patch/types.go deleted file mode 100644 index 5dc707d3..00000000 --- a/tf/providers/terraform-provider-junos-qfx/patch/types.go +++ /dev/null @@ -1,27 +0,0 @@ -package patch - -// ChangeType represents the CRUD operation for a single leaf diff. -type ChangeType int - -const ( - Create ChangeType = iota - Replace // value exists in both state and plan but differs - Delete // value exists in state but not in plan -) - -// Node is a single element in the parsed XML tree. -type Node struct { - Tag string - Attrs map[string]string // standard XML attributes (not nc:operation) - Operation string // nc:operation value: "create", "replace", "delete", or "" - Text string // character data between open/close tags - Children []*Node - Parent *Node -} - -// Change holds a single leaf-level diff entry produced by ComputeDiff. -type Change struct { - Op ChangeType - OldVal string // empty for Create - NewVal string // empty for Delete -} diff --git a/tf/providers/terraform-provider-junos-qfx/provider.go b/tf/providers/terraform-provider-junos-qfx/provider.go deleted file mode 100644 index 8efde52a..00000000 --- a/tf/providers/terraform-provider-junos-qfx/provider.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "context" - - "terraform-provider-junos-qfx/netconf" - "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/provider" - "github.com/hashicorp/terraform-plugin-framework/provider/schema" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -var _ provider.Provider = new(Provider) - -var providerClientFactory = func(cfg *Config) (netconf.Client, error) { - return cfg.Client() -} - -func newProvider() provider.Provider { - return Provider{} -} - -type Provider struct { -} - -type providerModel struct { - Host types.String `tfsdk:"host"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - Port types.Int64 `tfsdk:"port"` - SshKey types.String `tfsdk:"sshkey"` -} - -// ProviderConfig is to hold client information -type ProviderConfig struct { - netconf.Client - Host string -} - -func buildProviderConfig(config providerModel) (ProviderConfig, error) { - clientConfig := Config{ - Host: config.Host.ValueString(), - Port: int(config.Port.ValueInt64()), - Username: config.Username.ValueString(), - Password: config.Password.ValueString(), - SSHKey: config.SshKey.ValueString(), - } - - client, err := providerClientFactory(&clientConfig) - if err != nil { - return ProviderConfig{}, err - } - - return ProviderConfig{ - Client: client, - Host: clientConfig.Host, - }, nil -} - -// Configure implements provider.Provider. -func (p Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var config providerModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - - providerConfig, err := buildProviderConfig(config) - if err != nil { - resp.Diagnostics.AddError("failed to create client", err.Error()) - return - } - - resp.ResourceData = providerConfig -} - -// DataSources implements provider.Provider. -func (p Provider) DataSources(_ context.Context) []func() datasource.DataSource { - return nil -} - -// Metadata implements provider.Provider. -func (p Provider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { - resp.TypeName = "junos-qfx" -} - -// Resources implements provider.Provider. -func (p Provider) Resources(_ context.Context) []func() resource.Resource { - return []func() resource.Resource{ - func() resource.Resource { return new(configResource) }, - } -} - -// Schema implements provider.Provider. -func (p Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "host": schema.StringAttribute{ - Required: true, - }, - "username": schema.StringAttribute{ - Required: true, - }, - "password": schema.StringAttribute{ - Optional: true, - Sensitive: true, - }, - "port": schema.Int64Attribute{ - Required: true, - //Optional: true, - //Computed: true, - //Default: int64default.StaticInt64(22), - }, - "sshkey": schema.StringAttribute{ - Optional: true, - Sensitive: true, - // Will need to add eventually - //Validators: []validator.String{stringvalidator.AtLeastOneOf(path.MatchRoot("d")...)}, - }, - }, - } -} \ No newline at end of file diff --git a/tf/providers/terraform-provider-junos-qfx/provider_build_test.go b/tf/providers/terraform-provider-junos-qfx/provider_build_test.go deleted file mode 100644 index 6a84606a..00000000 --- a/tf/providers/terraform-provider-junos-qfx/provider_build_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "errors" - "testing" - - "terraform-provider-junos-qfx/netconf" - - "github.com/hashicorp/terraform-plugin-framework/types" -) - -type fakeNetconfClient struct{} - -// Close implements netconf.Client for unit tests. -func (f *fakeNetconfClient) Close() error { return nil } - -// DeleteConfig implements netconf.Client for unit tests. -func (f *fakeNetconfClient) DeleteConfig(string, bool) (string, error) { return "", nil } - -// SendCommit implements netconf.Client for unit tests. -func (f *fakeNetconfClient) SendCommit() error { return nil } - -// MarshalGroup implements netconf.Client for unit tests. -func (f *fakeNetconfClient) MarshalGroup(string, interface{}) error { return nil } - -// MarshalConfig implements netconf.Client for unit tests. -func (f *fakeNetconfClient) MarshalConfig(interface{}) error { return nil } - -// SendTransaction implements netconf.Client for unit tests. -func (f *fakeNetconfClient) SendTransaction(string, interface{}, bool) error { return nil } - -// SendDirectTransaction implements netconf.Client for unit tests. -func (f *fakeNetconfClient) SendDirectTransaction(interface{}, bool) error { return nil } - -// SendUpdate implements netconf.Client for unit tests. -func (f *fakeNetconfClient) SendUpdate(string, string, bool) error { return nil } - -// TestBuildProviderConfigSuccess verifies successful provider config construction. -func TestBuildProviderConfigSuccess(t *testing.T) { - originalFactory := providerClientFactory - t.Cleanup(func() { providerClientFactory = originalFactory }) - - providerClientFactory = func(cfg *Config) (netconf.Client, error) { - if cfg.Host != "127.0.0.1" || cfg.Port != 830 || cfg.Username != "user" { - t.Fatalf("unexpected config passed to factory: %+v", cfg) - } - return &fakeNetconfClient{}, nil - } - - model := providerModel{ - Host: types.StringValue("127.0.0.1"), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - Port: types.Int64Value(830), - SshKey: types.StringValue(""), - } - - cfg, err := buildProviderConfig(model) - if err != nil { - t.Fatalf("buildProviderConfig() returned error: %v", err) - } - if cfg.Host != "127.0.0.1" { - t.Fatalf("unexpected host: %q", cfg.Host) - } - if cfg.Client == nil { - t.Fatalf("expected non-nil netconf client") - } -} - -// TestBuildProviderConfigFactoryError verifies client factory errors are returned. -func TestBuildProviderConfigFactoryError(t *testing.T) { - originalFactory := providerClientFactory - t.Cleanup(func() { providerClientFactory = originalFactory }) - - expectedErr := errors.New("boom") - providerClientFactory = func(_ *Config) (netconf.Client, error) { - return nil, expectedErr - } - - _, err := buildProviderConfig(providerModel{}) - if !errors.Is(err, expectedErr) { - t.Fatalf("expected %v, got %v", expectedErr, err) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/provider_runtime_test.go b/tf/providers/terraform-provider-junos-qfx/provider_runtime_test.go deleted file mode 100644 index 91b697e0..00000000 --- a/tf/providers/terraform-provider-junos-qfx/provider_runtime_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package main - -import ( - "context" - "errors" - "math/big" - "strings" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/provider" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tftypes" - "terraform-provider-junos-qfx/netconf" -) - -// TestProviderMetadata verifies provider type metadata is set correctly. -func TestProviderMetadata(t *testing.T) { - p := &Provider{} - resp := &provider.MetadataResponse{} - p.Metadata(context.Background(), provider.MetadataRequest{}, resp) - if resp.TypeName == "" { - t.Fatalf("expected non-empty provider type name") - } - if resp.TypeName != "terraform_provider" && !strings.HasPrefix(resp.TypeName, "junos-") { - t.Fatalf("unexpected provider type name: %q", resp.TypeName) - } -} - -// TestProviderConfigureMissingConfigPanics documents current framework panic behavior. -func TestProviderConfigureMissingConfigPanics(t *testing.T) { - p := &Provider{} - defer func() { - if recover() == nil { - t.Fatalf("expected panic when ConfigureRequest.Config is unset") - } - }() - - resp := &provider.ConfigureResponse{} - p.Configure(context.Background(), provider.ConfigureRequest{}, resp) - if resp.Diagnostics.HasError() { - t.Fatalf("unexpected diagnostics before panic") - } -} - -// providerConfigRaw creates a typed provider config payload for Configure tests. -func providerConfigRaw(t *testing.T, p *Provider) tfsdk.Config { - t.Helper() - - ctx := context.Background() - schemaResp := &provider.SchemaResponse{} - p.Schema(ctx, provider.SchemaRequest{}, schemaResp) - - tfType := schemaResp.Schema.Type().TerraformType(ctx) - raw := tftypes.NewValue(tfType, map[string]tftypes.Value{ - "host": tftypes.NewValue(tftypes.String, "127.0.0.1"), - "username": tftypes.NewValue(tftypes.String, "user"), - "password": tftypes.NewValue(tftypes.String, "pass"), - "port": tftypes.NewValue(tftypes.Number, big.NewFloat(830)), - "sshkey": tftypes.NewValue(tftypes.String, ""), - }) - - return tfsdk.Config{Schema: schemaResp.Schema, Raw: raw} -} - -// TestProviderConfigureSuccess verifies successful provider configuration. -func TestProviderConfigureSuccess(t *testing.T) { - originalFactory := providerClientFactory - t.Cleanup(func() { providerClientFactory = originalFactory }) - - providerClientFactory = func(_ *Config) (netconf.Client, error) { - return &fakeNetconfClient{}, nil - } - - p := &Provider{} - req := provider.ConfigureRequest{Config: providerConfigRaw(t, p)} - resp := &provider.ConfigureResponse{} - - p.Configure(context.Background(), req, resp) - if resp.Diagnostics.HasError() { - t.Fatalf("unexpected diagnostics: %v", resp.Diagnostics) - } - if resp.ResourceData == nil { - t.Fatalf("expected provider resource data") - } -} - -// TestProviderConfigureClientError verifies provider diagnostics on client creation failures. -func TestProviderConfigureClientError(t *testing.T) { - originalFactory := providerClientFactory - t.Cleanup(func() { providerClientFactory = originalFactory }) - - providerClientFactory = func(_ *Config) (netconf.Client, error) { - return nil, errors.New("client failed") - } - - p := &Provider{} - req := provider.ConfigureRequest{Config: providerConfigRaw(t, p)} - resp := &provider.ConfigureResponse{} - - p.Configure(context.Background(), req, resp) - if !resp.Diagnostics.HasError() { - t.Fatalf("expected diagnostics on client factory error") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/provider_test.go b/tf/providers/terraform-provider-junos-qfx/provider_test.go deleted file mode 100644 index cc9e435f..00000000 --- a/tf/providers/terraform-provider-junos-qfx/provider_test.go +++ /dev/null @@ -1,294 +0,0 @@ -package main - -import ( - "context" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/provider" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// TestNewProvider tests instantiation of a new provider -func TestNewProvider(t *testing.T) { - p := newProvider() - if p == nil { - t.Fatal("expected non-nil provider") - } - - // Verify it implements the Provider interface - // p already has the correct type - -} - -// TestProviderMetadata tests the Metadata method removed because it changes when used with generated module - -// TestProviderSchema tests the Schema method -func TestProviderSchema(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - req := provider.SchemaRequest{} - resp := &provider.SchemaResponse{} - - p.Schema(ctx, req, resp) - - // Verify schema contains attributes map - if len(resp.Schema.Attributes) == 0 { - t.Error("expected schema to have attributes") - } - - // Verify required attributes exist - requiredAttrs := []string{"host", "username", "port"} - for _, attr := range requiredAttrs { - if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("expected attribute %q in schema", attr) - } - } - - // Verify optional attributes exist - optionalAttrs := []string{"password", "sshkey"} - for _, attr := range optionalAttrs { - if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("expected attribute %q in schema", attr) - } - } -} - -// TestProviderSchemaHostAttribute tests the host attribute configuration -func TestProviderSchemaHostAttribute(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - req := provider.SchemaRequest{} - resp := &provider.SchemaResponse{} - - p.Schema(ctx, req, resp) - - hostAttr := resp.Schema.Attributes["host"] - if hostAttr == nil { - t.Fatal("host attribute not found") - } - - // Host should be required - if !hostAttr.IsRequired() { - t.Error("host attribute should be required") - } -} - -// TestProviderSchemaPasswordAttribute tests the password attribute configuration -func TestProviderSchemaPasswordAttribute(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - req := provider.SchemaRequest{} - resp := &provider.SchemaResponse{} - - p.Schema(ctx, req, resp) - - passwordAttr := resp.Schema.Attributes["password"] - if passwordAttr == nil { - t.Fatal("password attribute not found") - } - - // Password should be optional and sensitive - if passwordAttr.IsRequired() { - t.Error("password attribute should be optional") - } - - if !passwordAttr.IsSensitive() { - t.Error("password attribute should be sensitive") - } -} - -// TestProviderSchemaSshKeyAttribute tests the sshkey attribute configuration -func TestProviderSchemaSshKeyAttribute(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - req := provider.SchemaRequest{} - resp := &provider.SchemaResponse{} - - p.Schema(ctx, req, resp) - - sshKeyAttr := resp.Schema.Attributes["sshkey"] - if sshKeyAttr == nil { - t.Fatal("sshkey attribute not found") - } - - // SSH Key should be optional and sensitive - if sshKeyAttr.IsRequired() { - t.Error("sshkey attribute should be optional") - } - - if !sshKeyAttr.IsSensitive() { - t.Error("sshkey attribute should be sensitive") - } -} - -// TestProviderSchemaPortAttribute tests the port attribute configuration -func TestProviderSchemaPortAttribute(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - req := provider.SchemaRequest{} - resp := &provider.SchemaResponse{} - - p.Schema(ctx, req, resp) - - portAttr := resp.Schema.Attributes["port"] - if portAttr == nil { - t.Fatal("port attribute not found") - } - - // Port should be required - if !portAttr.IsRequired() { - t.Error("port attribute should be required") - } -} - -// TestProviderConfigure tests the Configure method -func TestProviderConfigure(t *testing.T) { - p := &Provider{} - _ = p - ctx := context.Background() - _ = ctx - - req := provider.ConfigureRequest{} - resp := &provider.ConfigureResponse{} - _ = req - _ = resp - - // Note: Configure method requires properly populated ConfigureRequest - // which involves the Terraform plugin framework infrastructure. - // In a unit test environment, calling p.Configure directly would panic - // because the Config field would be nil. Typically this is called by - // the Terraform plugin framework during provider initialization. - // provider value constructed directly; nil check unnecessary - -} - -// TestProviderConfigureWithConfig tests the Configure method with actual config -func TestProviderConfigureWithConfig(t *testing.T) { - p := &Provider{} - _ = p - ctx := context.Background() - _ = ctx - - // Create a fake config with providerModel struct - providerConfig := providerModel{ - Host: types.StringValue("localhost"), - Username: types.StringValue("admin"), - Password: types.StringValue("pass"), - Port: types.Int64Value(830), - SshKey: types.StringValue(""), - } - _ = providerConfig - - // Note: We can't easily test the actual config parsing without a full TF setup. - // The Configure method requires framework infrastructure that isn't available - // in unit tests. Testing is done via integration tests with the Terraform CLI. - req := provider.ConfigureRequest{} - resp := &provider.ConfigureResponse{} - _ = req - _ = resp - - // direct instantiation yields non-nil provider - - // Verify providerModel is a struct - _ = providerConfig -} - -// TestProviderDataSources tests the DataSources method -func TestProviderDataSources(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - dataSources := p.DataSources(ctx) - - // Currently returns nil - this test ensures it doesn't panic - if dataSources != nil { - t.Logf("data sources: %v", dataSources) - } -} - -// TestProviderResources tests the Resources method -func TestProviderResources(t *testing.T) { - p := &Provider{} - ctx := context.Background() - - resources := p.Resources(ctx) - - // Currently returns nil - this test ensures it doesn't panic - if resources != nil { - t.Logf("resources: %v", resources) - } -} - -// TestProviderModelStructure tests the providerModel structure -func TestProviderModelStructure(t *testing.T) { - model := providerModel{ - Host: types.StringValue("example.com"), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - Port: types.Int64Value(830), - SshKey: types.StringValue("/path/to/key"), - } - - if model.Host.ValueString() != "example.com" { - t.Error("host value mismatch") - } - - if model.Username.ValueString() != "user" { - t.Error("username value mismatch") - } - - if model.Password.ValueString() != "pass" { - t.Error("password value mismatch") - } - - if model.Port.ValueInt64() != 830 { - t.Error("port value mismatch") - } - - if model.SshKey.ValueString() != "/path/to/key" { - t.Error("sshkey value mismatch") - } -} - -// TestProviderInterfaceImplementation verifies that Provider implements provider.Provider -func TestProviderInterfaceImplementation(t *testing.T) { - var _ provider.Provider = (*Provider)(nil) - // If this compiles, the interface is properly implemented -} - -// TestProviderCreation tests various ways to create a provider -func TestProviderCreation(t *testing.T) { - testCases := []struct { - name string - createFn func() provider.Provider - expectErr bool - }{ - { - name: "newProvider function", - createFn: newProvider, - expectErr: false, - }, - { - name: "direct instantiation", - createFn: func() provider.Provider { - return &Provider{} - }, - expectErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - p := tc.createFn() - _ = p // avoid unused variable warning - // Verify it implements the interface - // p already has provider.Provider type - - }) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_config_provider.go b/tf/providers/terraform-provider-junos-qfx/resource_config_provider.go deleted file mode 100644 index 46446485..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_config_provider.go +++ /dev/null @@ -1,7845 +0,0 @@ -package main - -import ( - "bytes" - "context" - "encoding/xml" - "fmt" - "os" - "terraform-provider-junos-qfx/patch" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/attr" - "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/types" -) - - - -var TrimmedSchemaJSON = `{ - "device_type": "qfx", - "path": "", - "root": { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "device-count", - "path": "chassis/aggregated-devices/ethernet", - "type": "leaf", - "types": [ - { - "path": "chassis/aggregated-devices/ethernet/device-count", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "chassis/aggregated-devices/ethernet/device-count", - "type": "uint32" - } - ] - } - ], - "name": "ethernet", - "path": "chassis/aggregated-devices", - "type": "container" - } - ], - "name": "aggregated-devices", - "path": "chassis", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "union", - "name": "name", - "path": "chassis/fpc", - "type": "leaf", - "types": [ - { - "path": "chassis/fpc/name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "chassis/fpc/name", - "type": "uint32" - } - ] - }, - { - "children": [ - { - "leaf-type": "union", - "name": "name", - "path": "chassis/fpc/pic", - "type": "leaf", - "types": [ - { - "path": "chassis/fpc/pic/name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "chassis/fpc/pic/name", - "type": "uint32" - } - ] - }, - { - "children": [ - { - "leaf-type": "union", - "name": "name", - "path": "chassis/fpc/pic/port", - "type": "leaf", - "types": [ - { - "path": "chassis/fpc/pic/port/name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "chassis/fpc/pic/port/name", - "type": "uint32" - } - ] - }, - { - "enums": [ - { - "id": "oc3-stm1", - "path": "chassis/fpc/pic/port/speed", - "value": 0 - }, - { - "id": "oc12-stm4", - "path": "chassis/fpc/pic/port/speed", - "value": 1 - }, - { - "id": "oc48-stm16", - "path": "chassis/fpc/pic/port/speed", - "value": 2 - }, - { - "id": "1G", - "path": "chassis/fpc/pic/port/speed", - "value": 3 - }, - { - "id": "10g", - "path": "chassis/fpc/pic/port/speed", - "value": 4 - }, - { - "id": "25g", - "path": "chassis/fpc/pic/port/speed", - "value": 5 - }, - { - "id": "40g", - "path": "chassis/fpc/pic/port/speed", - "value": 6 - }, - { - "id": "100g", - "path": "chassis/fpc/pic/port/speed", - "value": 7 - }, - { - "id": "50g", - "path": "chassis/fpc/pic/port/speed", - "value": 8 - }, - { - "id": "200g", - "path": "chassis/fpc/pic/port/speed", - "value": 9 - }, - { - "id": "400g", - "path": "chassis/fpc/pic/port/speed", - "value": 10 - } - ], - "leaf-type": "enumeration", - "name": "speed", - "path": "chassis/fpc/pic/port", - "type": "leaf" - }, - { - "enums": [ - { - "id": "10g", - "path": "chassis/fpc/pic/port/channel-speed", - "value": 0 - }, - { - "id": "25g", - "path": "chassis/fpc/pic/port/channel-speed", - "value": 1 - }, - { - "id": "50g", - "path": "chassis/fpc/pic/port/channel-speed", - "value": 2 - }, - { - "id": "disable-auto-speed-detection", - "path": "chassis/fpc/pic/port/channel-speed", - "value": 3 - } - ], - "leaf-type": "enumeration", - "name": "channel-speed", - "path": "chassis/fpc/pic/port", - "type": "leaf" - } - ], - "key": "name", - "name": "port", - "path": "chassis/fpc/pic", - "type": "list" - } - ], - "key": "name", - "name": "pic", - "path": "chassis/fpc", - "type": "list" - } - ], - "key": "name", - "name": "fpc", - "path": "chassis", - "type": "list" - } - ], - "name": "chassis", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "firewall/family/inet/filter", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "firewall/family/inet/filter/term", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipv4prefix", - "name": "name", - "path": "firewall/family/inet/filter/term/from/source-address", - "type": "leaf" - } - ], - "key": "name", - "name": "source-address", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from", - "type": "list" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipv4prefix", - "name": "name", - "path": "firewall/family/inet/filter/term/from/destination-address", - "type": "leaf" - } - ], - "key": "name", - "name": "destination-address", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from", - "type": "list" - }, - { - "leaf-type": "string", - "name": "protocol", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from", - "type": "leaf-list" - }, - { - "leaf-type": "string", - "name": "destination-port", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from", - "type": "leaf-list" - } - ], - "name": "from", - "path": "firewall/family/inet/filter/term", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "accept", - "path": "firewall/family/inet/filter/term/then", - "type": "leaf" - }, - { - "name": "discard", - "path": "firewall/family/inet/filter/term/then", - "type": "container" - } - ], - "name": "then", - "path": "firewall/family/inet/filter/term", - "type": "container" - } - ], - "key": "name", - "name": "term", - "ordered-by": "user", - "path": "firewall/family/inet/filter", - "type": "list" - } - ], - "key": "name", - "name": "filter", - "path": "firewall/family/inet", - "type": "list" - } - ], - "name": "inet", - "path": "firewall/family", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "firewall/family/inet6/filter", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "firewall/family/inet6/filter/term", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "next-header", - "ordered-by": "user", - "path": "firewall/family/inet6/filter/term/from", - "type": "leaf-list" - }, - { - "leaf-type": "string", - "name": "destination-port", - "ordered-by": "user", - "path": "firewall/family/inet6/filter/term/from", - "type": "leaf-list" - } - ], - "name": "from", - "path": "firewall/family/inet6/filter/term", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "accept", - "path": "firewall/family/inet6/filter/term/then", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "discard", - "path": "firewall/family/inet6/filter/term/then", - "type": "leaf" - } - ], - "name": "then", - "path": "firewall/family/inet6/filter/term", - "type": "container" - } - ], - "key": "name", - "name": "term", - "ordered-by": "user", - "path": "firewall/family/inet6/filter", - "type": "list" - } - ], - "key": "name", - "name": "filter", - "path": "firewall/family/inet6", - "type": "list" - } - ], - "name": "inet6", - "path": "firewall/family", - "type": "container" - } - ], - "name": "family", - "path": "firewall", - "type": "container" - } - ], - "name": "firewall", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 127, - "min": 1, - "path": "forwarding-options/storm-control-profiles/name" - } - ], - "name": "name", - "path": "forwarding-options/storm-control-profiles", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "union", - "name": "bandwidth-level", - "path": "forwarding-options/storm-control-profiles/all", - "type": "leaf", - "types": [ - { - "path": "forwarding-options/storm-control-profiles/all/bandwidth-level", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "forwarding-options/storm-control-profiles/all/bandwidth-level", - "type": "uint32" - } - ], - "units": "kbps" - } - ], - "name": "all", - "path": "forwarding-options/storm-control-profiles", - "type": "container" - } - ], - "key": "name", - "name": "storm-control-profiles", - "path": "forwarding-options", - "type": "list" - } - ], - "name": "forwarding-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "interfaces/interface", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "mtu", - "path": "interfaces/interface", - "type": "leaf", - "types": [ - { - "path": "interfaces/interface/mtu", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "interfaces/interface/mtu", - "type": "uint32" - } - ] - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "union", - "name": "bundle", - "path": "interfaces/interface/ether-options/ieee-802.3ad", - "type": "leaf", - "types": [ - { - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle", - "type": "string" - }, - { - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - } - ], - "name": "ieee-802.3ad", - "path": "interfaces/interface/ether-options", - "type": "container" - } - ], - "name": "ether-options", - "path": "interfaces/interface", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "empty", - "name": "active", - "path": "interfaces/interface/aggregated-ether-options/lacp", - "type": "leaf" - } - ], - "name": "lacp", - "path": "interfaces/interface/aggregated-ether-options", - "type": "container" - } - ], - "name": "aggregated-ether-options", - "path": "interfaces/interface", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "interfaces/interface/unit", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "filter-name", - "path": "interfaces/interface/unit/family/inet/filter/input", - "type": "leaf" - } - ], - "name": "input", - "path": "interfaces/interface/unit/family/inet/filter", - "type": "container" - } - ], - "name": "filter", - "path": "interfaces/interface/unit/family/inet", - "type": "container" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipv4prefix", - "name": "name", - "path": "interfaces/interface/unit/family/inet/address", - "type": "leaf" - } - ], - "key": "name", - "name": "address", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/inet", - "type": "list" - } - ], - "name": "inet", - "path": "interfaces/interface/unit/family", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "filter-name", - "path": "interfaces/interface/unit/family/inet6/filter/input", - "type": "leaf" - } - ], - "name": "input", - "path": "interfaces/interface/unit/family/inet6/filter", - "type": "container" - } - ], - "name": "filter", - "path": "interfaces/interface/unit/family/inet6", - "type": "container" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipv6prefix", - "name": "name", - "path": "interfaces/interface/unit/family/inet6/address", - "type": "leaf" - } - ], - "key": "name", - "name": "address", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/inet6", - "type": "list" - } - ], - "name": "inet6", - "path": "interfaces/interface/unit/family", - "type": "container" - }, - { - "children": [ - { - "default": "access", - "enums": [ - { - "id": "access", - "path": "interfaces/interface/unit/family/ethernet-switching/interface-mode", - "value": 0 - }, - { - "id": "trunk", - "path": "interfaces/interface/unit/family/ethernet-switching/interface-mode", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "interface-mode", - "path": "interfaces/interface/unit/family/ethernet-switching", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "members", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/ethernet-switching/vlan", - "type": "leaf-list" - } - ], - "name": "vlan", - "path": "interfaces/interface/unit/family/ethernet-switching", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "profile-name", - "path": "interfaces/interface/unit/family/ethernet-switching/storm-control", - "type": "leaf" - } - ], - "name": "storm-control", - "path": "interfaces/interface/unit/family/ethernet-switching", - "type": "container" - } - ], - "name": "ethernet-switching", - "path": "interfaces/interface/unit/family", - "type": "container" - } - ], - "name": "family", - "path": "interfaces/interface/unit", - "type": "container" - } - ], - "key": "name", - "name": "unit", - "path": "interfaces/interface", - "type": "list" - } - ], - "key": "name", - "name": "interface", - "path": "interfaces", - "type": "list" - } - ], - "name": "interfaces", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "policy-options/policy-statement", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "policy-options/policy-statement/term", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipprefix", - "name": "address", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "exact", - "path": "policy-options/policy-statement/term/from/route-filter", - "type": "leaf" - } - ], - "key": "address choice-ident choice-value", - "name": "route-filter", - "ordered-by": "user", - "path": "policy-options/policy-statement/term/from", - "type": "list" - } - ], - "name": "from", - "path": "policy-options/policy-statement/term", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "accept", - "path": "policy-options/policy-statement/term/then", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "reject", - "path": "policy-options/policy-statement/term/then", - "type": "leaf" - } - ], - "name": "then", - "path": "policy-options/policy-statement/term", - "type": "container" - } - ], - "key": "name", - "name": "term", - "ordered-by": "user", - "path": "policy-options/policy-statement", - "type": "list" - } - ], - "key": "name", - "name": "policy-statement", - "path": "policy-options", - "type": "list" - } - ], - "name": "policy-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "enums": [ - { - "id": "internal", - "path": "protocols/bgp/group/type", - "value": 0 - }, - { - "id": "external", - "path": "protocols/bgp/group/type", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "type", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "peer-as", - "path": "protocols/bgp/group", - "type": "leaf" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipaddr-scoped", - "name": "name", - "path": "protocols/bgp/group/neighbor", - "type": "leaf" - }, - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "import", - "ordered-by": "user", - "path": "protocols/bgp/group/neighbor", - "type": "leaf-list" - }, - { - "children": [ - { - "children": [ - { - "name": "unicast", - "path": "protocols/bgp/group/neighbor/family/inet", - "type": "container" - } - ], - "name": "inet", - "path": "protocols/bgp/group/neighbor/family", - "type": "container" - }, - { - "children": [ - { - "name": "unicast", - "path": "protocols/bgp/group/neighbor/family/inet6", - "type": "container" - } - ], - "name": "inet6", - "path": "protocols/bgp/group/neighbor/family", - "type": "container" - } - ], - "name": "family", - "path": "protocols/bgp/group/neighbor", - "type": "container" - }, - { - "base-type": "string", - "leaf-type": "jt:policy-algebra", - "name": "export", - "ordered-by": "user", - "path": "protocols/bgp/group/neighbor", - "type": "leaf-list" - } - ], - "key": "name", - "name": "neighbor", - "ordered-by": "user", - "path": "protocols/bgp/group", - "type": "list" - } - ], - "key": "name", - "name": "group", - "ordered-by": "user", - "path": "protocols/bgp", - "type": "list" - } - ], - "name": "bgp", - "path": "protocols", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "protocols/lldp/interface", - "type": "leaf" - } - ], - "key": "name", - "name": "interface", - "ordered-by": "user", - "path": "protocols/lldp", - "type": "list" - } - ], - "name": "lldp", - "path": "protocols", - "type": "container" - } - ], - "name": "protocols", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "as-number", - "path": "routing-options/autonomous-system", - "type": "leaf" - } - ], - "name": "autonomous-system", - "path": "routing-options", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipprefix", - "name": "name", - "path": "routing-options/static/route", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "discard", - "path": "routing-options/static/route", - "type": "leaf" - } - ], - "key": "name", - "name": "route", - "path": "routing-options/static", - "type": "list" - } - ], - "name": "static", - "path": "routing-options", - "type": "container" - } - ], - "name": "routing-options", - "path": "", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 255, - "min": 1, - "path": "system/host-name" - } - ], - "name": "host-name", - "path": "system", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 128, - "min": 1, - "path": "system/root-authentication/encrypted-password" - } - ], - "name": "encrypted-password", - "path": "system/root-authentication", - "type": "leaf" - } - ], - "name": "root-authentication", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/login/user", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "uid", - "path": "system/login/user", - "type": "leaf", - "types": [ - { - "path": "system/login/user/uid", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "system/login/user/uid", - "ranges": [ - { - "max": 64000, - "min": 100, - "path": "system/login/user/uid" - } - ], - "type": "uint32" - } - ] - }, - { - "leaf-type": "string", - "name": "class", - "path": "system/login/user", - "type": "leaf" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/login/user/authentication/ssh-ed25519", - "type": "leaf" - } - ], - "key": "name", - "name": "ssh-ed25519", - "ordered-by": "user", - "path": "system/login/user/authentication", - "type": "list" - } - ], - "name": "authentication", - "path": "system/login/user", - "type": "container" - } - ], - "key": "name", - "name": "user", - "path": "system/login", - "type": "list" - } - ], - "name": "login", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "name": "ssh", - "path": "system/services/netconf", - "type": "container" - } - ], - "name": "netconf", - "path": "system/services", - "type": "container" - }, - { - "children": [ - { - "enums": [ - { - "id": "allow", - "path": "system/services/ssh/root-login", - "value": 0 - }, - { - "id": "deny", - "path": "system/services/ssh/root-login", - "value": 1 - }, - { - "id": "deny-password", - "path": "system/services/ssh/root-login", - "value": 2 - } - ], - "leaf-type": "enumeration", - "name": "root-login", - "path": "system/services/ssh", - "type": "leaf" - } - ], - "name": "ssh", - "path": "system/services", - "type": "container" - } - ], - "name": "services", - "path": "system", - "type": "container" - }, - { - "default": "UTC", - "leaf-type": "string", - "name": "time-zone", - "path": "system", - "type": "leaf", - "units": "\u003ccontinent\u003e/\u003cmajor-city\u003e or \u003ctime-zone\u003e" - }, - { - "enums": [ - { - "id": "radius", - "path": "system/authentication-order", - "value": 0 - }, - { - "id": "tacplus", - "path": "system/authentication-order", - "value": 1 - }, - { - "id": "password", - "path": "system/authentication-order", - "value": 2 - } - ], - "leaf-type": "enumeration", - "name": "authentication-order", - "ordered-by": "user", - "path": "system", - "type": "leaf-list" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "empty", - "name": "log-out-on-disconnect", - "path": "system/ports/console", - "type": "leaf" - } - ], - "name": "console", - "path": "system/ports", - "type": "container" - } - ], - "name": "ports", - "path": "system", - "type": "container" - }, - { - "children": [ - { - "base-type": "string", - "leaf-type": "jt:ipaddr", - "name": "name", - "path": "system/name-server", - "type": "leaf" - } - ], - "key": "name", - "name": "name-server", - "ordered-by": "user", - "path": "system", - "type": "list" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "name": "name", - "path": "system/processes/daemon-process", - "type": "leaf" - }, - { - "leaf-type": "empty", - "name": "disable", - "path": "system/processes/daemon-process", - "type": "leaf" - } - ], - "key": "name", - "name": "daemon-process", - "ordered-by": "user", - "path": "system/processes", - "type": "list" - } - ], - "name": "processes", - "path": "system", - "type": "container" - } - ], - "name": "system", - "path": "", - "type": "container" - }, - { - "children": [ - { - "leaf-type": "empty", - "name": "preprovisioned", - "path": "virtual-chassis", - "type": "leaf" - }, - { - "children": [ - { - "leaf-type": "union", - "name": "name", - "path": "virtual-chassis/member", - "type": "leaf", - "types": [ - { - "path": "virtual-chassis/member/name", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - }, - { - "path": "virtual-chassis/member/name", - "type": "int32" - } - ] - }, - { - "default": "line-card", - "enums": [ - { - "id": "routing-engine", - "path": "virtual-chassis/member/role", - "value": 0 - }, - { - "id": "line-card", - "path": "virtual-chassis/member/role", - "value": 1 - } - ], - "leaf-type": "enumeration", - "name": "role", - "path": "virtual-chassis/member", - "type": "leaf" - }, - { - "leaf-type": "string", - "lengths": [ - { - "max": 12, - "min": 1, - "path": "virtual-chassis/member/serial-number" - } - ], - "name": "serial-number", - "path": "virtual-chassis/member", - "type": "leaf" - } - ], - "key": "name", - "name": "member", - "ordered-by": "user", - "path": "virtual-chassis", - "type": "list" - } - ], - "name": "virtual-chassis", - "path": "", - "type": "container" - }, - { - "children": [ - { - "children": [ - { - "leaf-type": "string", - "lengths": [ - { - "max": 64, - "min": 2, - "path": "vlans/vlan/name" - } - ], - "name": "name", - "path": "vlans/vlan", - "type": "leaf" - }, - { - "leaf-type": "string", - "name": "vlan-id", - "path": "vlans/vlan", - "type": "leaf" - }, - { - "leaf-type": "union", - "name": "l3-interface", - "path": "vlans/vlan", - "type": "leaf", - "types": [ - { - "path": "vlans/vlan/l3-interface", - "type": "string" - }, - { - "path": "vlans/vlan/l3-interface", - "patterns": [ - "\u003c.*\u003e|$.*" - ], - "type": "string" - } - ] - } - ], - "key": "name", - "name": "vlan", - "path": "vlans", - "type": "list" - } - ], - "name": "vlans", - "path": "", - "type": "container" - } - ], - "config": "true", - "name": "configuration", - "path": "", - "type": "container" - } - ], - "name": "root", - "path": "", - "type": "container" - } -}`// Junos XML Hierarchy - -type xml_Configuration struct { - XMLName xml.Name `xml:"configuration"` - Chassis []xml_Chassis `xml:"chassis,omitempty"` - Firewall []xml_Firewall `xml:"firewall,omitempty"` - Forwarding_options []xml_Forwarding_options `xml:"forwarding-options,omitempty"` - Interfaces []xml_Interfaces `xml:"interfaces,omitempty"` - Policy_options []xml_Policy_options `xml:"policy-options,omitempty"` - Protocols []xml_Protocols `xml:"protocols,omitempty"` - Routing_options []xml_Routing_options `xml:"routing-options,omitempty"` - System []xml_System `xml:"system,omitempty"` - Virtual_chassis []xml_Virtual_chassis `xml:"virtual-chassis,omitempty"` - Vlans []xml_Vlans `xml:"vlans,omitempty"` -} -type xml_Chassis struct { - XMLName xml.Name `xml:"chassis"` - Aggregated_devices []xml_Chassis_Aggregated_devices `xml:"aggregated-devices,omitempty"` - Fpc []xml_Chassis_Fpc `xml:"fpc,omitempty"` -} - -type xml_Firewall struct { - XMLName xml.Name `xml:"firewall"` - Family []xml_Firewall_Family `xml:"family,omitempty"` -} - -type xml_Forwarding_options struct { - XMLName xml.Name `xml:"forwarding-options"` - Storm_control_profiles []xml_Forwarding_options_Storm_control_profiles `xml:"storm-control-profiles,omitempty"` -} - -type xml_Interfaces struct { - XMLName xml.Name `xml:"interfaces"` - Interface []xml_Interfaces_Interface `xml:"interface,omitempty"` -} - -type xml_Policy_options struct { - XMLName xml.Name `xml:"policy-options"` - Policy_statement []xml_Policy_options_Policy_statement `xml:"policy-statement,omitempty"` -} - -type xml_Protocols struct { - XMLName xml.Name `xml:"protocols"` - Bgp []xml_Protocols_Bgp `xml:"bgp,omitempty"` - Lldp []xml_Protocols_Lldp `xml:"lldp,omitempty"` -} - -type xml_Routing_options struct { - XMLName xml.Name `xml:"routing-options"` - Autonomous_system []xml_Routing_options_Autonomous_system `xml:"autonomous-system,omitempty"` - Static []xml_Routing_options_Static `xml:"static,omitempty"` -} - -type xml_System struct { - XMLName xml.Name `xml:"system"` - Host_name *string `xml:"host-name,omitempty"` - Root_authentication []xml_System_Root_authentication `xml:"root-authentication,omitempty"` - Login []xml_System_Login `xml:"login,omitempty"` - Services []xml_System_Services `xml:"services,omitempty"` - Time_zone *string `xml:"time-zone,omitempty"` - Authentication_order []*string `xml:"authentication-order,omitempty"` - Ports []xml_System_Ports `xml:"ports,omitempty"` - Name_server []xml_System_Name_server `xml:"name-server,omitempty"` - Processes []xml_System_Processes `xml:"processes,omitempty"` -} - -type xml_Virtual_chassis struct { - XMLName xml.Name `xml:"virtual-chassis"` - Preprovisioned *string `xml:"preprovisioned,omitempty"` - Member []xml_Virtual_chassis_Member `xml:"member,omitempty"` -} - -type xml_Vlans struct { - XMLName xml.Name `xml:"vlans"` - Vlan []xml_Vlans_Vlan `xml:"vlan,omitempty"` -} - - -type xml_Chassis_Aggregated_devices struct { - XMLName xml.Name `xml:"aggregated-devices"` - Ethernet []xml_Chassis_Aggregated_devices_Ethernet `xml:"ethernet,omitempty"` -} -type xml_Chassis_Fpc struct { - XMLName xml.Name `xml:"fpc"` - Name *string `xml:"name,omitempty"` - Pic []xml_Chassis_Fpc_Pic `xml:"pic,omitempty"` -} -type xml_Firewall_Family struct { - XMLName xml.Name `xml:"family"` - Inet []xml_Firewall_Family_Inet `xml:"inet,omitempty"` - Inet6 []xml_Firewall_Family_Inet6 `xml:"inet6,omitempty"` -} -type xml_Forwarding_options_Storm_control_profiles struct { - XMLName xml.Name `xml:"storm-control-profiles"` - Name *string `xml:"name,omitempty"` - All []xml_Forwarding_options_Storm_control_profiles_All `xml:"all,omitempty"` -} -type xml_Interfaces_Interface struct { - XMLName xml.Name `xml:"interface"` - Name *string `xml:"name,omitempty"` - Mtu *string `xml:"mtu,omitempty"` - Ether_options []xml_Interfaces_Interface_Ether_options `xml:"ether-options,omitempty"` - Aggregated_ether_options []xml_Interfaces_Interface_Aggregated_ether_options `xml:"aggregated-ether-options,omitempty"` - Unit []xml_Interfaces_Interface_Unit `xml:"unit,omitempty"` -} -type xml_Policy_options_Policy_statement struct { - XMLName xml.Name `xml:"policy-statement"` - Name *string `xml:"name,omitempty"` - Term []xml_Policy_options_Policy_statement_Term `xml:"term,omitempty"` -} -type xml_Protocols_Bgp struct { - XMLName xml.Name `xml:"bgp"` - Group []xml_Protocols_Bgp_Group `xml:"group,omitempty"` -} -type xml_Protocols_Lldp struct { - XMLName xml.Name `xml:"lldp"` - Interface []xml_Protocols_Lldp_Interface `xml:"interface,omitempty"` -} -type xml_Routing_options_Autonomous_system struct { - XMLName xml.Name `xml:"autonomous-system"` - As_number *string `xml:"as-number,omitempty"` -} -type xml_Routing_options_Static struct { - XMLName xml.Name `xml:"static"` - Route []xml_Routing_options_Static_Route `xml:"route,omitempty"` -} -type xml_System_Root_authentication struct { - XMLName xml.Name `xml:"root-authentication"` - Encrypted_password *string `xml:"encrypted-password,omitempty"` -} -type xml_System_Login struct { - XMLName xml.Name `xml:"login"` - User []xml_System_Login_User `xml:"user,omitempty"` -} -type xml_System_Services struct { - XMLName xml.Name `xml:"services"` - Netconf []xml_System_Services_Netconf `xml:"netconf,omitempty"` - Ssh []xml_System_Services_Ssh `xml:"ssh,omitempty"` -} -type xml_System_Ports struct { - XMLName xml.Name `xml:"ports"` - Console []xml_System_Ports_Console `xml:"console,omitempty"` -} -type xml_System_Name_server struct { - XMLName xml.Name `xml:"name-server"` - Name *string `xml:"name,omitempty"` -} -type xml_System_Processes struct { - XMLName xml.Name `xml:"processes"` - Daemon_process []xml_System_Processes_Daemon_process `xml:"daemon-process,omitempty"` -} -type xml_Virtual_chassis_Member struct { - XMLName xml.Name `xml:"member"` - Name *string `xml:"name,omitempty"` - Role *string `xml:"role,omitempty"` - Serial_number *string `xml:"serial-number,omitempty"` -} -type xml_Vlans_Vlan struct { - XMLName xml.Name `xml:"vlan"` - Name *string `xml:"name,omitempty"` - Vlan_id *string `xml:"vlan-id,omitempty"` - L3_interface *string `xml:"l3-interface,omitempty"` -} - -type xml_Chassis_Aggregated_devices_Ethernet struct { - XMLName xml.Name `xml:"ethernet"` - Device_count *string `xml:"device-count,omitempty"` -} -type xml_Chassis_Fpc_Pic struct { - XMLName xml.Name `xml:"pic"` - Name *string `xml:"name,omitempty"` - Port []xml_Chassis_Fpc_Pic_Port `xml:"port,omitempty"` -} -type xml_Firewall_Family_Inet struct { - XMLName xml.Name `xml:"inet"` - Filter []xml_Firewall_Family_Inet_Filter `xml:"filter,omitempty"` -} -type xml_Firewall_Family_Inet6 struct { - XMLName xml.Name `xml:"inet6"` - Filter []xml_Firewall_Family_Inet6_Filter `xml:"filter,omitempty"` -} -type xml_Forwarding_options_Storm_control_profiles_All struct { - XMLName xml.Name `xml:"all"` - Bandwidth_level *string `xml:"bandwidth-level,omitempty"` -} -type xml_Interfaces_Interface_Ether_options struct { - XMLName xml.Name `xml:"ether-options"` - Ieee_802_3ad []xml_Interfaces_Interface_Ether_options_Ieee_802_3ad `xml:"ieee-802.3ad,omitempty"` -} -type xml_Interfaces_Interface_Aggregated_ether_options struct { - XMLName xml.Name `xml:"aggregated-ether-options"` - Lacp []xml_Interfaces_Interface_Aggregated_ether_options_Lacp `xml:"lacp,omitempty"` -} -type xml_Interfaces_Interface_Unit struct { - XMLName xml.Name `xml:"unit"` - Name *string `xml:"name,omitempty"` - Family []xml_Interfaces_Interface_Unit_Family `xml:"family,omitempty"` -} -type xml_Policy_options_Policy_statement_Term struct { - XMLName xml.Name `xml:"term"` - Name *string `xml:"name,omitempty"` - From []xml_Policy_options_Policy_statement_Term_From `xml:"from,omitempty"` - Then []xml_Policy_options_Policy_statement_Term_Then `xml:"then,omitempty"` -} -type xml_Protocols_Bgp_Group struct { - XMLName xml.Name `xml:"group"` - Name *string `xml:"name,omitempty"` - Type *string `xml:"type,omitempty"` - Peer_as *string `xml:"peer-as,omitempty"` - Neighbor []xml_Protocols_Bgp_Group_Neighbor `xml:"neighbor,omitempty"` -} -type xml_Protocols_Lldp_Interface struct { - XMLName xml.Name `xml:"interface"` - Name *string `xml:"name,omitempty"` -} -type xml_Routing_options_Static_Route struct { - XMLName xml.Name `xml:"route"` - Name *string `xml:"name,omitempty"` - Discard *string `xml:"discard,omitempty"` -} -type xml_System_Login_User struct { - XMLName xml.Name `xml:"user"` - Name *string `xml:"name,omitempty"` - Uid *string `xml:"uid,omitempty"` - Class *string `xml:"class,omitempty"` - Authentication []xml_System_Login_User_Authentication `xml:"authentication,omitempty"` -} -type xml_System_Services_Netconf struct { - XMLName xml.Name `xml:"netconf"` - Ssh []xml_System_Services_Netconf_Ssh `xml:"ssh,omitempty"` -} -type xml_System_Services_Ssh struct { - XMLName xml.Name `xml:"ssh"` - Root_login *string `xml:"root-login,omitempty"` -} -type xml_System_Ports_Console struct { - XMLName xml.Name `xml:"console"` - Log_out_on_disconnect *string `xml:"log-out-on-disconnect,omitempty"` -} -type xml_System_Processes_Daemon_process struct { - XMLName xml.Name `xml:"daemon-process"` - Name *string `xml:"name,omitempty"` - Disable *string `xml:"disable,omitempty"` -} - -type xml_Chassis_Fpc_Pic_Port struct { - XMLName xml.Name `xml:"port"` - Name *string `xml:"name,omitempty"` - Speed *string `xml:"speed,omitempty"` - Channel_speed *string `xml:"channel-speed,omitempty"` -} -type xml_Firewall_Family_Inet_Filter struct { - XMLName xml.Name `xml:"filter"` - Name *string `xml:"name,omitempty"` - Term []xml_Firewall_Family_Inet_Filter_Term `xml:"term,omitempty"` -} -type xml_Firewall_Family_Inet6_Filter struct { - XMLName xml.Name `xml:"filter"` - Name *string `xml:"name,omitempty"` - Term []xml_Firewall_Family_Inet6_Filter_Term `xml:"term,omitempty"` -} -type xml_Interfaces_Interface_Ether_options_Ieee_802_3ad struct { - XMLName xml.Name `xml:"ieee-802.3ad"` - Bundle *string `xml:"bundle,omitempty"` -} -type xml_Interfaces_Interface_Aggregated_ether_options_Lacp struct { - XMLName xml.Name `xml:"lacp"` - Active *string `xml:"active,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family struct { - XMLName xml.Name `xml:"family"` - Inet []xml_Interfaces_Interface_Unit_Family_Inet `xml:"inet,omitempty"` - Inet6 []xml_Interfaces_Interface_Unit_Family_Inet6 `xml:"inet6,omitempty"` - Ethernet_switching []xml_Interfaces_Interface_Unit_Family_Ethernet_switching `xml:"ethernet-switching,omitempty"` -} -type xml_Policy_options_Policy_statement_Term_From struct { - XMLName xml.Name `xml:"from"` - Route_filter []xml_Policy_options_Policy_statement_Term_From_Route_filter `xml:"route-filter,omitempty"` -} -type xml_Policy_options_Policy_statement_Term_Then struct { - XMLName xml.Name `xml:"then"` - Accept *string `xml:"accept,omitempty"` - Reject *string `xml:"reject,omitempty"` -} -type xml_Protocols_Bgp_Group_Neighbor struct { - XMLName xml.Name `xml:"neighbor"` - Name *string `xml:"name,omitempty"` - Import []*string `xml:"import,omitempty"` - Family []xml_Protocols_Bgp_Group_Neighbor_Family `xml:"family,omitempty"` - Export []*string `xml:"export,omitempty"` -} -type xml_System_Login_User_Authentication struct { - XMLName xml.Name `xml:"authentication"` - Ssh_ed25519 []xml_System_Login_User_Authentication_Ssh_ed25519 `xml:"ssh-ed25519,omitempty"` -} -type xml_System_Services_Netconf_Ssh struct { - XMLName xml.Name `xml:"ssh"` -} - -type xml_Firewall_Family_Inet_Filter_Term struct { - XMLName xml.Name `xml:"term"` - Name *string `xml:"name,omitempty"` - From []xml_Firewall_Family_Inet_Filter_Term_From `xml:"from,omitempty"` - Then []xml_Firewall_Family_Inet_Filter_Term_Then `xml:"then,omitempty"` -} -type xml_Firewall_Family_Inet6_Filter_Term struct { - XMLName xml.Name `xml:"term"` - Name *string `xml:"name,omitempty"` - From []xml_Firewall_Family_Inet6_Filter_Term_From `xml:"from,omitempty"` - Then []xml_Firewall_Family_Inet6_Filter_Term_Then `xml:"then,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet struct { - XMLName xml.Name `xml:"inet"` - Filter []xml_Interfaces_Interface_Unit_Family_Inet_Filter `xml:"filter,omitempty"` - Address []xml_Interfaces_Interface_Unit_Family_Inet_Address `xml:"address,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet6 struct { - XMLName xml.Name `xml:"inet6"` - Filter []xml_Interfaces_Interface_Unit_Family_Inet6_Filter `xml:"filter,omitempty"` - Address []xml_Interfaces_Interface_Unit_Family_Inet6_Address `xml:"address,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Ethernet_switching struct { - XMLName xml.Name `xml:"ethernet-switching"` - Interface_mode *string `xml:"interface-mode,omitempty"` - Vlan []xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan `xml:"vlan,omitempty"` - Storm_control []xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control `xml:"storm-control,omitempty"` -} -type xml_Policy_options_Policy_statement_Term_From_Route_filter struct { - XMLName xml.Name `xml:"route-filter"` - Address *string `xml:"address,omitempty"` - Exact *string `xml:"exact,omitempty"` -} -type xml_Protocols_Bgp_Group_Neighbor_Family struct { - XMLName xml.Name `xml:"family"` - Inet []xml_Protocols_Bgp_Group_Neighbor_Family_Inet `xml:"inet,omitempty"` - Inet6 []xml_Protocols_Bgp_Group_Neighbor_Family_Inet6 `xml:"inet6,omitempty"` -} -type xml_System_Login_User_Authentication_Ssh_ed25519 struct { - XMLName xml.Name `xml:"ssh-ed25519"` - Name *string `xml:"name,omitempty"` -} - -type xml_Firewall_Family_Inet_Filter_Term_From struct { - XMLName xml.Name `xml:"from"` - Source_address []xml_Firewall_Family_Inet_Filter_Term_From_Source_address `xml:"source-address,omitempty"` - Destination_address []xml_Firewall_Family_Inet_Filter_Term_From_Destination_address `xml:"destination-address,omitempty"` - Protocol []*string `xml:"protocol,omitempty"` - Destination_port []*string `xml:"destination-port,omitempty"` -} -type xml_Firewall_Family_Inet_Filter_Term_Then struct { - XMLName xml.Name `xml:"then"` - Accept *string `xml:"accept,omitempty"` - Discard []xml_Firewall_Family_Inet_Filter_Term_Then_Discard `xml:"discard,omitempty"` -} -type xml_Firewall_Family_Inet6_Filter_Term_From struct { - XMLName xml.Name `xml:"from"` - Next_header []*string `xml:"next-header,omitempty"` - Destination_port []*string `xml:"destination-port,omitempty"` -} -type xml_Firewall_Family_Inet6_Filter_Term_Then struct { - XMLName xml.Name `xml:"then"` - Accept *string `xml:"accept,omitempty"` - Discard *string `xml:"discard,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet_Filter struct { - XMLName xml.Name `xml:"filter"` - Input []xml_Interfaces_Interface_Unit_Family_Inet_Filter_Input `xml:"input,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet_Address struct { - XMLName xml.Name `xml:"address"` - Name *string `xml:"name,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet6_Filter struct { - XMLName xml.Name `xml:"filter"` - Input []xml_Interfaces_Interface_Unit_Family_Inet6_Filter_Input `xml:"input,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet6_Address struct { - XMLName xml.Name `xml:"address"` - Name *string `xml:"name,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan struct { - XMLName xml.Name `xml:"vlan"` - Members []*string `xml:"members,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control struct { - XMLName xml.Name `xml:"storm-control"` - Profile_name *string `xml:"profile-name,omitempty"` -} -type xml_Protocols_Bgp_Group_Neighbor_Family_Inet struct { - XMLName xml.Name `xml:"inet"` - Unicast []xml_Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast `xml:"unicast,omitempty"` -} -type xml_Protocols_Bgp_Group_Neighbor_Family_Inet6 struct { - XMLName xml.Name `xml:"inet6"` - Unicast []xml_Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast `xml:"unicast,omitempty"` -} - -type xml_Firewall_Family_Inet_Filter_Term_From_Source_address struct { - XMLName xml.Name `xml:"source-address"` - Name *string `xml:"name,omitempty"` -} -type xml_Firewall_Family_Inet_Filter_Term_From_Destination_address struct { - XMLName xml.Name `xml:"destination-address"` - Name *string `xml:"name,omitempty"` -} -type xml_Firewall_Family_Inet_Filter_Term_Then_Discard struct { - XMLName xml.Name `xml:"discard"` -} -type xml_Interfaces_Interface_Unit_Family_Inet_Filter_Input struct { - XMLName xml.Name `xml:"input"` - Filter_name *string `xml:"filter-name,omitempty"` -} -type xml_Interfaces_Interface_Unit_Family_Inet6_Filter_Input struct { - XMLName xml.Name `xml:"input"` - Filter_name *string `xml:"filter-name,omitempty"` -} -type xml_Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast struct { - XMLName xml.Name `xml:"unicast"` -} -type xml_Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast struct { - XMLName xml.Name `xml:"unicast"` -} - - - - -// Collecting objects from the .tf file. -type ConfigResourceModel struct { - ResourceName types.String `tfsdk:"resource_name"` - Chassis types.List `tfsdk:"chassis"` - Firewall types.List `tfsdk:"firewall"` - Forwarding_options types.List `tfsdk:"forwarding_options"` - Interfaces types.List `tfsdk:"interfaces"` - Policy_options types.List `tfsdk:"policy_options"` - Protocols types.List `tfsdk:"protocols"` - Routing_options types.List `tfsdk:"routing_options"` - System types.List `tfsdk:"system"` - Virtual_chassis types.List `tfsdk:"virtual_chassis"` - Vlans types.List `tfsdk:"vlans"` -} -func (o ConfigResourceModel) AttrTypes() map[string]attr.Type { - return map[string]attr.Type { - "resource_name": types.StringType, - "chassis": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Model{}.AttrTypes()}}, - "firewall": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Model{}.AttrTypes()}}, - "forwarding_options": types.ListType{ElemType: types.ObjectType{AttrTypes: Forwarding_options_Model{}.AttrTypes()}}, - "interfaces": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Model{}.AttrTypes()}}, - "policy_options": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Model{}.AttrTypes()}}, - "protocols": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Model{}.AttrTypes()}}, - "routing_options": types.ListType{ElemType: types.ObjectType{AttrTypes: Routing_options_Model{}.AttrTypes()}}, - "system": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Model{}.AttrTypes()}}, - "virtual_chassis": types.ListType{ElemType: types.ObjectType{AttrTypes: Virtual_chassis_Model{}.AttrTypes()}}, - "vlans": types.ListType{ElemType: types.ObjectType{AttrTypes: Vlans_Model{}.AttrTypes()}}, - } -} -func (o ConfigResourceModel) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "resource_name": schema.StringAttribute { - Required: true, - MarkdownDescription: "xpath is `config.resource_name`", - }, - "chassis": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Model{}.Attributes(), - }, - }, - "firewall": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Model{}.Attributes(), - }, - }, - "forwarding_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Forwarding_options_Model{}.Attributes(), - }, - }, - "interfaces": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Model{}.Attributes(), - }, - }, - "policy_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Model{}.Attributes(), - }, - }, - "protocols": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Model{}.Attributes(), - }, - }, - "routing_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Routing_options_Model{}.Attributes(), - }, - }, - "system": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Model{}.Attributes(), - }, - }, - "virtual_chassis": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Virtual_chassis_Model{}.Attributes(), - }, - }, - "vlans": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Vlans_Model{}.Attributes(), - }, - }, - } -} -type Chassis_Model struct { - Aggregated_devices types.List `tfsdk:"aggregated_devices"` - Fpc types.List `tfsdk:"fpc"` -} -func (o Chassis_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "aggregated_devices": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Model{}.AttrTypes()}}, - "fpc": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Fpc_Model{}.AttrTypes()}}, - } -} -func (o Chassis_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "aggregated_devices": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Aggregated_devices_Model{}.Attributes(), - }, - }, - "fpc": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Fpc_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Model struct { - Family types.List `tfsdk:"family"` -} -func (o Firewall_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "family": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "family": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Model{}.Attributes(), - }, - }, - } -} -type Forwarding_options_Model struct { - Storm_control_profiles types.List `tfsdk:"storm_control_profiles"` -} -func (o Forwarding_options_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "storm_control_profiles": types.ListType{ElemType: types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_Model{}.AttrTypes()}}, - } -} -func (o Forwarding_options_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "storm_control_profiles": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Forwarding_options_Storm_control_profiles_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Model struct { - Interface types.List `tfsdk:"interface"` -} -func (o Interfaces_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "interface": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "interface": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Model struct { - Policy_statement types.List `tfsdk:"policy_statement"` -} -func (o Policy_options_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "policy_statement": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Policy_statement_Model{}.AttrTypes()}}, - } -} -func (o Policy_options_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "policy_statement": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Policy_statement_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Model struct { - Bgp types.List `tfsdk:"bgp"` - Lldp types.List `tfsdk:"lldp"` -} -func (o Protocols_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "bgp": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Model{}.AttrTypes()}}, - "lldp": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Lldp_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "bgp": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Model{}.Attributes(), - }, - }, - "lldp": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Lldp_Model{}.Attributes(), - }, - }, - } -} -type Routing_options_Model struct { - Autonomous_system types.List `tfsdk:"autonomous_system"` - Static types.List `tfsdk:"static"` -} -func (o Routing_options_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "autonomous_system": types.ListType{ElemType: types.ObjectType{AttrTypes: Routing_options_Autonomous_system_Model{}.AttrTypes()}}, - "static": types.ListType{ElemType: types.ObjectType{AttrTypes: Routing_options_Static_Model{}.AttrTypes()}}, - } -} -func (o Routing_options_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "autonomous_system": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Routing_options_Autonomous_system_Model{}.Attributes(), - }, - }, - "static": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Routing_options_Static_Model{}.Attributes(), - }, - }, - } -} -type System_Model struct { - Host_name types.String `tfsdk:"host_name"` - Root_authentication types.List `tfsdk:"root_authentication"` - Login types.List `tfsdk:"login"` - Services types.List `tfsdk:"services"` - Time_zone types.String `tfsdk:"time_zone"` - Authentication_order types.List `tfsdk:"authentication_order"` - Ports types.List `tfsdk:"ports"` - Name_server types.List `tfsdk:"name_server"` - Processes types.List `tfsdk:"processes"` -} -func (o System_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "host_name": types.StringType, - "root_authentication": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Root_authentication_Model{}.AttrTypes()}}, - "login": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Login_Model{}.AttrTypes()}}, - "services": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Services_Model{}.AttrTypes()}}, - "time_zone": types.StringType, - "authentication_order": types.ListType{ElemType: types.StringType}, - "ports": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Ports_Model{}.AttrTypes()}}, - "name_server": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Name_server_Model{}.AttrTypes()}}, - "processes": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Processes_Model{}.AttrTypes()}}, - } -} -func (o System_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "host_name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.System.Host-name`", - }, - "root_authentication": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Root_authentication_Model{}.Attributes(), - }, - }, - "login": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Login_Model{}.Attributes(), - }, - }, - "services": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Services_Model{}.Attributes(), - }, - }, - "time_zone": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.System.Time-zone`", - }, - "authentication_order": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Authentication-order.System`", - }, - "ports": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Ports_Model{}.Attributes(), - }, - }, - "name_server": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Name_server_Model{}.Attributes(), - }, - }, - "processes": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Processes_Model{}.Attributes(), - }, - }, - } -} -type Virtual_chassis_Model struct { - Preprovisioned types.String `tfsdk:"preprovisioned"` - Member types.List `tfsdk:"member"` -} -func (o Virtual_chassis_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "preprovisioned": types.StringType, - "member": types.ListType{ElemType: types.ObjectType{AttrTypes: Virtual_chassis_Member_Model{}.AttrTypes()}}, - } -} -func (o Virtual_chassis_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "preprovisioned": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Virtual-chassis.Preprovisioned`", - }, - "member": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Virtual_chassis_Member_Model{}.Attributes(), - }, - }, - } -} -type Vlans_Model struct { - Vlan types.List `tfsdk:"vlan"` -} -func (o Vlans_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "vlan": types.ListType{ElemType: types.ObjectType{AttrTypes: Vlans_Vlan_Model{}.AttrTypes()}}, - } -} -func (o Vlans_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "vlan": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Vlans_Vlan_Model{}.Attributes(), - }, - }, - } -} - -type Chassis_Aggregated_devices_Model struct { - Ethernet types.List `tfsdk:"ethernet"` -} -func (o Chassis_Aggregated_devices_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "ethernet": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Ethernet_Model{}.AttrTypes()}}, - } -} -func (o Chassis_Aggregated_devices_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "ethernet": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Aggregated_devices_Ethernet_Model{}.Attributes(), - }, - }, - } -} -type Chassis_Fpc_Model struct { - Name types.String `tfsdk:"name"` - Pic types.List `tfsdk:"pic"` -} -func (o Chassis_Fpc_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "pic": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Model{}.AttrTypes()}}, - } -} -func (o Chassis_Fpc_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Fpc`", - }, - "pic": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Fpc_Pic_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Model struct { - Inet types.List `tfsdk:"inet"` - Inet6 types.List `tfsdk:"inet6"` -} -func (o Firewall_Family_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "inet": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Model{}.AttrTypes()}}, - "inet6": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet6_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "inet": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Model{}.Attributes(), - }, - }, - "inet6": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet6_Model{}.Attributes(), - }, - }, - } -} -type Forwarding_options_Storm_control_profiles_Model struct { - Name types.String `tfsdk:"name"` - All types.List `tfsdk:"all"` -} -func (o Forwarding_options_Storm_control_profiles_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "all": types.ListType{ElemType: types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_All_Model{}.AttrTypes()}}, - } -} -func (o Forwarding_options_Storm_control_profiles_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Storm_control_profiles`", - }, - "all": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Forwarding_options_Storm_control_profiles_All_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Model struct { - Name types.String `tfsdk:"name"` - Mtu types.String `tfsdk:"mtu"` - Ether_options types.List `tfsdk:"ether_options"` - Aggregated_ether_options types.List `tfsdk:"aggregated_ether_options"` - Unit types.List `tfsdk:"unit"` -} -func (o Interfaces_Interface_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "mtu": types.StringType, - "ether_options": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Model{}.AttrTypes()}}, - "aggregated_ether_options": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Model{}.AttrTypes()}}, - "unit": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Interface`", - }, - "mtu": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Mtu.Interface`", - }, - "ether_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Ether_options_Model{}.Attributes(), - }, - }, - "aggregated_ether_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Aggregated_ether_options_Model{}.Attributes(), - }, - }, - "unit": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Policy_statement_Model struct { - Name types.String `tfsdk:"name"` - Term types.List `tfsdk:"term"` -} -func (o Policy_options_Policy_statement_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "term": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Model{}.AttrTypes()}}, - } -} -func (o Policy_options_Policy_statement_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Policy_statement`", - }, - "term": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Policy_statement_Term_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Bgp_Model struct { - Group types.List `tfsdk:"group"` -} -func (o Protocols_Bgp_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "group": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Bgp_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "group": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Lldp_Model struct { - Interface types.List `tfsdk:"interface"` -} -func (o Protocols_Lldp_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "interface": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Lldp_Interface_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Lldp_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "interface": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Lldp_Interface_Model{}.Attributes(), - }, - }, - } -} -type Routing_options_Autonomous_system_Model struct { - As_number types.String `tfsdk:"as_number"` -} -func (o Routing_options_Autonomous_system_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "as_number": types.StringType, - } -} -func (o Routing_options_Autonomous_system_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "as_number": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.As-number.Autonomous_system`", - }, - } -} -type Routing_options_Static_Model struct { - Route types.List `tfsdk:"route"` -} -func (o Routing_options_Static_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "route": types.ListType{ElemType: types.ObjectType{AttrTypes: Routing_options_Static_Route_Model{}.AttrTypes()}}, - } -} -func (o Routing_options_Static_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "route": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Routing_options_Static_Route_Model{}.Attributes(), - }, - }, - } -} -type System_Root_authentication_Model struct { - Encrypted_password types.String `tfsdk:"encrypted_password"` -} -func (o System_Root_authentication_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "encrypted_password": types.StringType, - } -} -func (o System_Root_authentication_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "encrypted_password": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Encrypted-password.Root_authentication`", - }, - } -} -type System_Login_Model struct { - User types.List `tfsdk:"user"` -} -func (o System_Login_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "user": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Login_User_Model{}.AttrTypes()}}, - } -} -func (o System_Login_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "user": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Login_User_Model{}.Attributes(), - }, - }, - } -} -type System_Services_Model struct { - Netconf types.List `tfsdk:"netconf"` - Ssh types.List `tfsdk:"ssh"` -} -func (o System_Services_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "netconf": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Services_Netconf_Model{}.AttrTypes()}}, - "ssh": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Services_Ssh_Model{}.AttrTypes()}}, - } -} -func (o System_Services_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "netconf": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Services_Netconf_Model{}.Attributes(), - }, - }, - "ssh": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Services_Ssh_Model{}.Attributes(), - }, - }, - } -} -type System_Ports_Model struct { - Console types.List `tfsdk:"console"` -} -func (o System_Ports_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "console": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Ports_Console_Model{}.AttrTypes()}}, - } -} -func (o System_Ports_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "console": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Ports_Console_Model{}.Attributes(), - }, - }, - } -} -type System_Name_server_Model struct { - Name types.String `tfsdk:"name"` -} -func (o System_Name_server_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o System_Name_server_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Name_server`", - }, - } -} -type System_Processes_Model struct { - Daemon_process types.List `tfsdk:"daemon_process"` -} -func (o System_Processes_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "daemon_process": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Processes_Daemon_process_Model{}.AttrTypes()}}, - } -} -func (o System_Processes_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "daemon_process": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Processes_Daemon_process_Model{}.Attributes(), - }, - }, - } -} -type Virtual_chassis_Member_Model struct { - Name types.String `tfsdk:"name"` - Role types.String `tfsdk:"role"` - Serial_number types.String `tfsdk:"serial_number"` -} -func (o Virtual_chassis_Member_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "role": types.StringType, - "serial_number": types.StringType, - } -} -func (o Virtual_chassis_Member_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Member`", - }, - "role": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Role.Member`", - }, - "serial_number": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Serial-number.Member`", - }, - } -} -type Vlans_Vlan_Model struct { - Name types.String `tfsdk:"name"` - Vlan_id types.String `tfsdk:"vlan_id"` - L3_interface types.String `tfsdk:"l3_interface"` -} -func (o Vlans_Vlan_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "vlan_id": types.StringType, - "l3_interface": types.StringType, - } -} -func (o Vlans_Vlan_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Vlan`", - }, - "vlan_id": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Vlan-id.Vlan`", - }, - "l3_interface": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.L3-interface.Vlan`", - }, - } -} - -type Chassis_Aggregated_devices_Ethernet_Model struct { - Device_count types.String `tfsdk:"device_count"` -} -func (o Chassis_Aggregated_devices_Ethernet_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "device_count": types.StringType, - } -} -func (o Chassis_Aggregated_devices_Ethernet_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "device_count": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Device-count.Ethernet`", - }, - } -} -type Chassis_Fpc_Pic_Model struct { - Name types.String `tfsdk:"name"` - Port types.List `tfsdk:"port"` -} -func (o Chassis_Fpc_Pic_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "port": types.ListType{ElemType: types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Port_Model{}.AttrTypes()}}, - } -} -func (o Chassis_Fpc_Pic_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Pic`", - }, - "port": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Fpc_Pic_Port_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Inet_Model struct { - Filter types.List `tfsdk:"filter"` -} -func (o Firewall_Family_Inet_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Inet6_Model struct { - Filter types.List `tfsdk:"filter"` -} -func (o Firewall_Family_Inet6_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet6_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet6_Filter_Model{}.Attributes(), - }, - }, - } -} -type Forwarding_options_Storm_control_profiles_All_Model struct { - Bandwidth_level types.String `tfsdk:"bandwidth_level"` -} -func (o Forwarding_options_Storm_control_profiles_All_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "bandwidth_level": types.StringType, - } -} -func (o Forwarding_options_Storm_control_profiles_All_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "bandwidth_level": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Bandwidth-level.All`", - }, - } -} -type Interfaces_Interface_Ether_options_Model struct { - Ieee_802_3ad types.List `tfsdk:"ieee_802_3ad"` -} -func (o Interfaces_Interface_Ether_options_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "ieee_802_3ad": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Ieee_802_3ad_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Ether_options_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "ieee_802_3ad": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Ether_options_Ieee_802_3ad_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Aggregated_ether_options_Model struct { - Lacp types.List `tfsdk:"lacp"` -} -func (o Interfaces_Interface_Aggregated_ether_options_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "lacp": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Lacp_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Aggregated_ether_options_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "lacp": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Aggregated_ether_options_Lacp_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Model struct { - Name types.String `tfsdk:"name"` - Family types.List `tfsdk:"family"` -} -func (o Interfaces_Interface_Unit_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "family": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Unit`", - }, - "family": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Policy_statement_Term_Model struct { - Name types.String `tfsdk:"name"` - From types.List `tfsdk:"from"` - Then types.List `tfsdk:"then"` -} -func (o Policy_options_Policy_statement_Term_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "from": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Model{}.AttrTypes()}}, - "then": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Then_Model{}.AttrTypes()}}, - } -} -func (o Policy_options_Policy_statement_Term_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Term`", - }, - "from": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Policy_statement_Term_From_Model{}.Attributes(), - }, - }, - "then": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Policy_statement_Term_Then_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Bgp_Group_Model struct { - Name types.String `tfsdk:"name"` - Type types.String `tfsdk:"type"` - Peer_as types.String `tfsdk:"peer_as"` - Neighbor types.List `tfsdk:"neighbor"` -} -func (o Protocols_Bgp_Group_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "type": types.StringType, - "peer_as": types.StringType, - "neighbor": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Bgp_Group_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Group`", - }, - "type": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Type.Group`", - }, - "peer_as": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Peer-as.Group`", - }, - "neighbor": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Lldp_Interface_Model struct { - Name types.String `tfsdk:"name"` -} -func (o Protocols_Lldp_Interface_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o Protocols_Lldp_Interface_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Interface`", - }, - } -} -type Routing_options_Static_Route_Model struct { - Name types.String `tfsdk:"name"` - Discard types.String `tfsdk:"discard"` -} -func (o Routing_options_Static_Route_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "discard": types.StringType, - } -} -func (o Routing_options_Static_Route_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Route`", - }, - "discard": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Discard.Route`", - }, - } -} -type System_Login_User_Model struct { - Name types.String `tfsdk:"name"` - Uid types.String `tfsdk:"uid"` - Class types.String `tfsdk:"class"` - Authentication types.List `tfsdk:"authentication"` -} -func (o System_Login_User_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "uid": types.StringType, - "class": types.StringType, - "authentication": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Login_User_Authentication_Model{}.AttrTypes()}}, - } -} -func (o System_Login_User_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.User`", - }, - "uid": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Uid.User`", - }, - "class": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Class.User`", - }, - "authentication": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Login_User_Authentication_Model{}.Attributes(), - }, - }, - } -} -type System_Services_Netconf_Model struct { - Ssh types.List `tfsdk:"ssh"` -} -func (o System_Services_Netconf_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "ssh": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Services_Netconf_Ssh_Model{}.AttrTypes()}}, - } -} -func (o System_Services_Netconf_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "ssh": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Services_Netconf_Ssh_Model{}.Attributes(), - }, - }, - } -} -type System_Services_Ssh_Model struct { - Root_login types.String `tfsdk:"root_login"` -} -func (o System_Services_Ssh_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "root_login": types.StringType, - } -} -func (o System_Services_Ssh_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "root_login": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Root-login.Ssh`", - }, - } -} -type System_Ports_Console_Model struct { - Log_out_on_disconnect types.String `tfsdk:"log_out_on_disconnect"` -} -func (o System_Ports_Console_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "log_out_on_disconnect": types.StringType, - } -} -func (o System_Ports_Console_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "log_out_on_disconnect": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Log-out-on-disconnect.Console`", - }, - } -} -type System_Processes_Daemon_process_Model struct { - Name types.String `tfsdk:"name"` - Disable types.String `tfsdk:"disable"` -} -func (o System_Processes_Daemon_process_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "disable": types.StringType, - } -} -func (o System_Processes_Daemon_process_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Daemon_process`", - }, - "disable": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Disable.Daemon_process`", - }, - } -} - -type Chassis_Fpc_Pic_Port_Model struct { - Name types.String `tfsdk:"name"` - Speed types.String `tfsdk:"speed"` - Channel_speed types.String `tfsdk:"channel_speed"` -} -func (o Chassis_Fpc_Pic_Port_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "speed": types.StringType, - "channel_speed": types.StringType, - } -} -func (o Chassis_Fpc_Pic_Port_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Port`", - }, - "speed": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Speed.Port`", - }, - "channel_speed": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Channel-speed.Port`", - }, - } -} -type Firewall_Family_Inet_Filter_Model struct { - Name types.String `tfsdk:"name"` - Term types.List `tfsdk:"term"` -} -func (o Firewall_Family_Inet_Filter_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "term": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet_Filter_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Filter`", - }, - "term": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Inet6_Filter_Model struct { - Name types.String `tfsdk:"name"` - Term types.List `tfsdk:"term"` -} -func (o Firewall_Family_Inet6_Filter_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "term": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet6_Filter_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Filter`", - }, - "term": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet6_Filter_Term_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Ether_options_Ieee_802_3ad_Model struct { - Bundle types.String `tfsdk:"bundle"` -} -func (o Interfaces_Interface_Ether_options_Ieee_802_3ad_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "bundle": types.StringType, - } -} -func (o Interfaces_Interface_Ether_options_Ieee_802_3ad_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "bundle": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Bundle.Ieee_802_3ad`", - }, - } -} -type Interfaces_Interface_Aggregated_ether_options_Lacp_Model struct { - Active types.String `tfsdk:"active"` -} -func (o Interfaces_Interface_Aggregated_ether_options_Lacp_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "active": types.StringType, - } -} -func (o Interfaces_Interface_Aggregated_ether_options_Lacp_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "active": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Active.Lacp`", - }, - } -} -type Interfaces_Interface_Unit_Family_Model struct { - Inet types.List `tfsdk:"inet"` - Inet6 types.List `tfsdk:"inet6"` - Ethernet_switching types.List `tfsdk:"ethernet_switching"` -} -func (o Interfaces_Interface_Unit_Family_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "inet": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Model{}.AttrTypes()}}, - "inet6": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Model{}.AttrTypes()}}, - "ethernet_switching": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "inet": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet_Model{}.Attributes(), - }, - }, - "inet6": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet6_Model{}.Attributes(), - }, - }, - "ethernet_switching": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Ethernet_switching_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Policy_statement_Term_From_Model struct { - Route_filter types.List `tfsdk:"route_filter"` -} -func (o Policy_options_Policy_statement_Term_From_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "route_filter": types.ListType{ElemType: types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Route_filter_Model{}.AttrTypes()}}, - } -} -func (o Policy_options_Policy_statement_Term_From_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "route_filter": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Policy_statement_Term_From_Route_filter_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Policy_statement_Term_Then_Model struct { - Accept types.String `tfsdk:"accept"` - Reject types.String `tfsdk:"reject"` -} -func (o Policy_options_Policy_statement_Term_Then_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "accept": types.StringType, - "reject": types.StringType, - } -} -func (o Policy_options_Policy_statement_Term_Then_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "accept": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Accept.Then`", - }, - "reject": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Reject.Then`", - }, - } -} -type Protocols_Bgp_Group_Neighbor_Model struct { - Name types.String `tfsdk:"name"` - Import types.List `tfsdk:"import"` - Family types.List `tfsdk:"family"` - Export types.List `tfsdk:"export"` -} -func (o Protocols_Bgp_Group_Neighbor_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "import": types.ListType{ElemType: types.StringType}, - "family": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Model{}.AttrTypes()}}, - "export": types.ListType{ElemType: types.StringType}, - } -} -func (o Protocols_Bgp_Group_Neighbor_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Neighbor`", - }, - "import": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Import.Neighbor`", - }, - "family": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Family_Model{}.Attributes(), - }, - }, - "export": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Export.Neighbor`", - }, - } -} -type System_Login_User_Authentication_Model struct { - Ssh_ed25519 types.List `tfsdk:"ssh_ed25519"` -} -func (o System_Login_User_Authentication_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "ssh_ed25519": types.ListType{ElemType: types.ObjectType{AttrTypes: System_Login_User_Authentication_Ssh_ed25519_Model{}.AttrTypes()}}, - } -} -func (o System_Login_User_Authentication_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "ssh_ed25519": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Login_User_Authentication_Ssh_ed25519_Model{}.Attributes(), - }, - }, - } -} -type System_Services_Netconf_Ssh_Model struct { -} -func (o System_Services_Netconf_Ssh_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - } -} -func (o System_Services_Netconf_Ssh_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - } -} - -type Firewall_Family_Inet_Filter_Term_Model struct { - Name types.String `tfsdk:"name"` - From types.List `tfsdk:"from"` - Then types.List `tfsdk:"then"` -} -func (o Firewall_Family_Inet_Filter_Term_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "from": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Model{}.AttrTypes()}}, - "then": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet_Filter_Term_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Term`", - }, - "from": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_From_Model{}.Attributes(), - }, - }, - "then": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_Then_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Inet6_Filter_Term_Model struct { - Name types.String `tfsdk:"name"` - From types.List `tfsdk:"from"` - Then types.List `tfsdk:"then"` -} -func (o Firewall_Family_Inet6_Filter_Term_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - "from": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_From_Model{}.AttrTypes()}}, - "then": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Then_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet6_Filter_Term_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Term`", - }, - "from": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet6_Filter_Term_From_Model{}.Attributes(), - }, - }, - "then": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet6_Filter_Term_Then_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Family_Inet_Model struct { - Filter types.List `tfsdk:"filter"` - Address types.List `tfsdk:"address"` -} -func (o Interfaces_Interface_Unit_Family_Inet_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Model{}.AttrTypes()}}, - "address": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Address_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Inet_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet_Filter_Model{}.Attributes(), - }, - }, - "address": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet_Address_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Family_Inet6_Model struct { - Filter types.List `tfsdk:"filter"` - Address types.List `tfsdk:"address"` -} -func (o Interfaces_Interface_Unit_Family_Inet6_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Model{}.AttrTypes()}}, - "address": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Address_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Inet6_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet6_Filter_Model{}.Attributes(), - }, - }, - "address": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet6_Address_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Family_Ethernet_switching_Model struct { - Interface_mode types.String `tfsdk:"interface_mode"` - Vlan types.List `tfsdk:"vlan"` - Storm_control types.List `tfsdk:"storm_control"` -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "interface_mode": types.StringType, - "vlan": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model{}.AttrTypes()}}, - "storm_control": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "interface_mode": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Interface-mode.Ethernet_switching`", - }, - "vlan": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model{}.Attributes(), - }, - }, - "storm_control": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model{}.Attributes(), - }, - }, - } -} -type Policy_options_Policy_statement_Term_From_Route_filter_Model struct { - Address types.String `tfsdk:"address"` - Exact types.String `tfsdk:"exact"` -} -func (o Policy_options_Policy_statement_Term_From_Route_filter_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "address": types.StringType, - "exact": types.StringType, - } -} -func (o Policy_options_Policy_statement_Term_From_Route_filter_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "address": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Address.Route_filter`", - }, - "exact": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Exact.Route_filter`", - }, - } -} -type Protocols_Bgp_Group_Neighbor_Family_Model struct { - Inet types.List `tfsdk:"inet"` - Inet6 types.List `tfsdk:"inet6"` -} -func (o Protocols_Bgp_Group_Neighbor_Family_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "inet": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Model{}.AttrTypes()}}, - "inet6": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Bgp_Group_Neighbor_Family_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "inet": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Family_Inet_Model{}.Attributes(), - }, - }, - "inet6": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Model{}.Attributes(), - }, - }, - } -} -type System_Login_User_Authentication_Ssh_ed25519_Model struct { - Name types.String `tfsdk:"name"` -} -func (o System_Login_User_Authentication_Ssh_ed25519_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o System_Login_User_Authentication_Ssh_ed25519_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Ssh_ed25519`", - }, - } -} - -type Firewall_Family_Inet_Filter_Term_From_Model struct { - Source_address types.List `tfsdk:"source_address"` - Destination_address types.List `tfsdk:"destination_address"` - Protocol types.List `tfsdk:"protocol"` - Destination_port types.List `tfsdk:"destination_port"` -} -func (o Firewall_Family_Inet_Filter_Term_From_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "source_address": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Source_address_Model{}.AttrTypes()}}, - "destination_address": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Destination_address_Model{}.AttrTypes()}}, - "protocol": types.ListType{ElemType: types.StringType}, - "destination_port": types.ListType{ElemType: types.StringType}, - } -} -func (o Firewall_Family_Inet_Filter_Term_From_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "source_address": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_From_Source_address_Model{}.Attributes(), - }, - }, - "destination_address": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_From_Destination_address_Model{}.Attributes(), - }, - }, - "protocol": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Protocol.From`", - }, - "destination_port": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Destination-port.From`", - }, - } -} -type Firewall_Family_Inet_Filter_Term_Then_Model struct { - Accept types.String `tfsdk:"accept"` - Discard types.List `tfsdk:"discard"` -} -func (o Firewall_Family_Inet_Filter_Term_Then_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "accept": types.StringType, - "discard": types.ListType{ElemType: types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Discard_Model{}.AttrTypes()}}, - } -} -func (o Firewall_Family_Inet_Filter_Term_Then_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "accept": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Accept.Then`", - }, - "discard": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Family_Inet_Filter_Term_Then_Discard_Model{}.Attributes(), - }, - }, - } -} -type Firewall_Family_Inet6_Filter_Term_From_Model struct { - Next_header types.List `tfsdk:"next_header"` - Destination_port types.List `tfsdk:"destination_port"` -} -func (o Firewall_Family_Inet6_Filter_Term_From_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "next_header": types.ListType{ElemType: types.StringType}, - "destination_port": types.ListType{ElemType: types.StringType}, - } -} -func (o Firewall_Family_Inet6_Filter_Term_From_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "next_header": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Next-header.From`", - }, - "destination_port": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Destination-port.From`", - }, - } -} -type Firewall_Family_Inet6_Filter_Term_Then_Model struct { - Accept types.String `tfsdk:"accept"` - Discard types.String `tfsdk:"discard"` -} -func (o Firewall_Family_Inet6_Filter_Term_Then_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "accept": types.StringType, - "discard": types.StringType, - } -} -func (o Firewall_Family_Inet6_Filter_Term_Then_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "accept": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Accept.Then`", - }, - "discard": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Discard.Then`", - }, - } -} -type Interfaces_Interface_Unit_Family_Inet_Filter_Model struct { - Input types.List `tfsdk:"input"` -} -func (o Interfaces_Interface_Unit_Family_Inet_Filter_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "input": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Inet_Filter_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "input": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Family_Inet_Address_Model struct { - Name types.String `tfsdk:"name"` -} -func (o Interfaces_Interface_Unit_Family_Inet_Address_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o Interfaces_Interface_Unit_Family_Inet_Address_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Address`", - }, - } -} -type Interfaces_Interface_Unit_Family_Inet6_Filter_Model struct { - Input types.List `tfsdk:"input"` -} -func (o Interfaces_Interface_Unit_Family_Inet6_Filter_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "input": types.ListType{ElemType: types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model{}.AttrTypes()}}, - } -} -func (o Interfaces_Interface_Unit_Family_Inet6_Filter_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "input": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model{}.Attributes(), - }, - }, - } -} -type Interfaces_Interface_Unit_Family_Inet6_Address_Model struct { - Name types.String `tfsdk:"name"` -} -func (o Interfaces_Interface_Unit_Family_Inet6_Address_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o Interfaces_Interface_Unit_Family_Inet6_Address_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Address`", - }, - } -} -type Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model struct { - Members types.List `tfsdk:"members"` -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "members": types.ListType{ElemType: types.StringType}, - } -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "members": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - MarkdownDescription: "xpath is `config.Members.Vlan`", - }, - } -} -type Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model struct { - Profile_name types.String `tfsdk:"profile_name"` -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "profile_name": types.StringType, - } -} -func (o Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "profile_name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Profile-name.Storm_control`", - }, - } -} -type Protocols_Bgp_Group_Neighbor_Family_Inet_Model struct { - Unicast types.List `tfsdk:"unicast"` -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "unicast": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "unicast": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model{}.Attributes(), - }, - }, - } -} -type Protocols_Bgp_Group_Neighbor_Family_Inet6_Model struct { - Unicast types.List `tfsdk:"unicast"` -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet6_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "unicast": types.ListType{ElemType: types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model{}.AttrTypes()}}, - } -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet6_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "unicast": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model{}.Attributes(), - }, - }, - } -} - -type Firewall_Family_Inet_Filter_Term_From_Source_address_Model struct { - Name types.String `tfsdk:"name"` -} -func (o Firewall_Family_Inet_Filter_Term_From_Source_address_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o Firewall_Family_Inet_Filter_Term_From_Source_address_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Source_address`", - }, - } -} -type Firewall_Family_Inet_Filter_Term_From_Destination_address_Model struct { - Name types.String `tfsdk:"name"` -} -func (o Firewall_Family_Inet_Filter_Term_From_Destination_address_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "name": types.StringType, - } -} -func (o Firewall_Family_Inet_Filter_Term_From_Destination_address_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Name.Destination_address`", - }, - } -} -type Firewall_Family_Inet_Filter_Term_Then_Discard_Model struct { -} -func (o Firewall_Family_Inet_Filter_Term_Then_Discard_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - } -} -func (o Firewall_Family_Inet_Filter_Term_Then_Discard_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - } -} -type Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model struct { - Filter_name types.String `tfsdk:"filter_name"` -} -func (o Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter_name": types.StringType, - } -} -func (o Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter_name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Filter-name.Input`", - }, - } -} -type Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model struct { - Filter_name types.String `tfsdk:"filter_name"` -} -func (o Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - "filter_name": types.StringType, - } -} -func (o Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - "filter_name": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "xpath is `config.Filter-name.Input`", - }, - } -} -type Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model struct { -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - } -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - } -} -type Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model struct { -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model) AttrTypes() map[string]attr.Type { - return map[string]attr.Type{ - } -} -func (o Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model) Attributes() map[string]schema.Attribute { - return map[string]schema.Attribute{ - } -} - - - -// Collects the data for the CRUD work. -type configResource struct { - client ProviderConfig -} - -func (r *configResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - r.client = req.ProviderData.(ProviderConfig) -} -// Metadata implements resource.Resource. -func (r *configResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "terraform-provider-" + req.ProviderTypeName -} -// Schema implements resource.Resource. -func (r *configResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "resource_name": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "chassis": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Chassis_Model{}.Attributes(), - }, - }, - "firewall": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Firewall_Model{}.Attributes(), - }, - }, - "forwarding_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Forwarding_options_Model{}.Attributes(), - }, - }, - "interfaces": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Interfaces_Model{}.Attributes(), - }, - }, - "policy_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Policy_options_Model{}.Attributes(), - }, - }, - "protocols": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Protocols_Model{}.Attributes(), - }, - }, - "routing_options": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Routing_options_Model{}.Attributes(), - }, - }, - "system": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: System_Model{}.Attributes(), - }, - }, - "virtual_chassis": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Virtual_chassis_Model{}.Attributes(), - }, - }, - "vlans": schema.ListNestedAttribute{ - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: Vlans_Model{}.Attributes(), - }, - }, - }, - } -} - -func modelToConfig(ctx context.Context, model ConfigResourceModel, diags *diag.Diagnostics) xml_Configuration { - var cfg xml_Configuration - - - var var_chassis []Chassis_Model - if model.Chassis.IsNull() { - var_chassis = []Chassis_Model{} - } else { - diags.Append(model.Chassis.ElementsAs(ctx, &var_chassis, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Chassis = make([]xml_Chassis, len(var_chassis)) - for i_chassis, v_chassis := range var_chassis { - var var_chassis_aggregated_devices []Chassis_Aggregated_devices_Model - diags.Append(v_chassis.Aggregated_devices.ElementsAs(ctx, &var_chassis_aggregated_devices, false)...) - if diags.HasError() { - return cfg - } - cfg.Chassis[i_chassis].Aggregated_devices = make([]xml_Chassis_Aggregated_devices, len(var_chassis_aggregated_devices)) - - for i_chassis_aggregated_devices, v_chassis_aggregated_devices := range var_chassis_aggregated_devices { - var var_chassis_aggregated_devices_ethernet []Chassis_Aggregated_devices_Ethernet_Model - diags.Append(v_chassis_aggregated_devices.Ethernet.ElementsAs(ctx, &var_chassis_aggregated_devices_ethernet, false)...) - if diags.HasError() { - return cfg - } - cfg.Chassis[i_chassis].Aggregated_devices[i_chassis_aggregated_devices].Ethernet = make([]xml_Chassis_Aggregated_devices_Ethernet, len(var_chassis_aggregated_devices_ethernet)) - - for i_chassis_aggregated_devices_ethernet, v_chassis_aggregated_devices_ethernet := range var_chassis_aggregated_devices_ethernet { - cfg.Chassis[i_chassis].Aggregated_devices[i_chassis_aggregated_devices].Ethernet[i_chassis_aggregated_devices_ethernet].Device_count = v_chassis_aggregated_devices_ethernet.Device_count.ValueStringPointer() - } - } - var var_chassis_fpc []Chassis_Fpc_Model - diags.Append(v_chassis.Fpc.ElementsAs(ctx, &var_chassis_fpc, false)...) - if diags.HasError() { - return cfg - } - cfg.Chassis[i_chassis].Fpc = make([]xml_Chassis_Fpc, len(var_chassis_fpc)) - - for i_chassis_fpc, v_chassis_fpc := range var_chassis_fpc { - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Name = v_chassis_fpc.Name.ValueStringPointer() - var var_chassis_fpc_pic []Chassis_Fpc_Pic_Model - diags.Append(v_chassis_fpc.Pic.ElementsAs(ctx, &var_chassis_fpc_pic, false)...) - if diags.HasError() { - return cfg - } - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic = make([]xml_Chassis_Fpc_Pic, len(var_chassis_fpc_pic)) - - for i_chassis_fpc_pic, v_chassis_fpc_pic := range var_chassis_fpc_pic { - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic[i_chassis_fpc_pic].Name = v_chassis_fpc_pic.Name.ValueStringPointer() - var var_chassis_fpc_pic_port []Chassis_Fpc_Pic_Port_Model - diags.Append(v_chassis_fpc_pic.Port.ElementsAs(ctx, &var_chassis_fpc_pic_port, false)...) - if diags.HasError() { - return cfg - } - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic[i_chassis_fpc_pic].Port = make([]xml_Chassis_Fpc_Pic_Port, len(var_chassis_fpc_pic_port)) - - for i_chassis_fpc_pic_port, v_chassis_fpc_pic_port := range var_chassis_fpc_pic_port { - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic[i_chassis_fpc_pic].Port[i_chassis_fpc_pic_port].Name = v_chassis_fpc_pic_port.Name.ValueStringPointer() - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic[i_chassis_fpc_pic].Port[i_chassis_fpc_pic_port].Speed = v_chassis_fpc_pic_port.Speed.ValueStringPointer() - cfg.Chassis[i_chassis].Fpc[i_chassis_fpc].Pic[i_chassis_fpc_pic].Port[i_chassis_fpc_pic_port].Channel_speed = v_chassis_fpc_pic_port.Channel_speed.ValueStringPointer() - } - } - } - } - - var var_firewall []Firewall_Model - if model.Firewall.IsNull() { - var_firewall = []Firewall_Model{} - } else { - diags.Append(model.Firewall.ElementsAs(ctx, &var_firewall, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Firewall = make([]xml_Firewall, len(var_firewall)) - for i_firewall, v_firewall := range var_firewall { - var var_firewall_family []Firewall_Family_Model - diags.Append(v_firewall.Family.ElementsAs(ctx, &var_firewall_family, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family = make([]xml_Firewall_Family, len(var_firewall_family)) - - for i_firewall_family, v_firewall_family := range var_firewall_family { - var var_firewall_family_inet []Firewall_Family_Inet_Model - diags.Append(v_firewall_family.Inet.ElementsAs(ctx, &var_firewall_family_inet, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet = make([]xml_Firewall_Family_Inet, len(var_firewall_family_inet)) - - for i_firewall_family_inet, v_firewall_family_inet := range var_firewall_family_inet { - var var_firewall_family_inet_filter []Firewall_Family_Inet_Filter_Model - diags.Append(v_firewall_family_inet.Filter.ElementsAs(ctx, &var_firewall_family_inet_filter, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter = make([]xml_Firewall_Family_Inet_Filter, len(var_firewall_family_inet_filter)) - - for i_firewall_family_inet_filter, v_firewall_family_inet_filter := range var_firewall_family_inet_filter { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Name = v_firewall_family_inet_filter.Name.ValueStringPointer() - var var_firewall_family_inet_filter_term []Firewall_Family_Inet_Filter_Term_Model - diags.Append(v_firewall_family_inet_filter.Term.ElementsAs(ctx, &var_firewall_family_inet_filter_term, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term = make([]xml_Firewall_Family_Inet_Filter_Term, len(var_firewall_family_inet_filter_term)) - - for i_firewall_family_inet_filter_term, v_firewall_family_inet_filter_term := range var_firewall_family_inet_filter_term { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].Name = v_firewall_family_inet_filter_term.Name.ValueStringPointer() - var var_firewall_family_inet_filter_term_from []Firewall_Family_Inet_Filter_Term_From_Model - diags.Append(v_firewall_family_inet_filter_term.From.ElementsAs(ctx, &var_firewall_family_inet_filter_term_from, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From = make([]xml_Firewall_Family_Inet_Filter_Term_From, len(var_firewall_family_inet_filter_term_from)) - - for i_firewall_family_inet_filter_term_from, v_firewall_family_inet_filter_term_from := range var_firewall_family_inet_filter_term_from { - var var_firewall_family_inet_filter_term_from_source_address []Firewall_Family_Inet_Filter_Term_From_Source_address_Model - diags.Append(v_firewall_family_inet_filter_term_from.Source_address.ElementsAs(ctx, &var_firewall_family_inet_filter_term_from_source_address, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Source_address = make([]xml_Firewall_Family_Inet_Filter_Term_From_Source_address, len(var_firewall_family_inet_filter_term_from_source_address)) - - for i_firewall_family_inet_filter_term_from_source_address, v_firewall_family_inet_filter_term_from_source_address := range var_firewall_family_inet_filter_term_from_source_address { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Source_address[i_firewall_family_inet_filter_term_from_source_address].Name = v_firewall_family_inet_filter_term_from_source_address.Name.ValueStringPointer() - } - var var_firewall_family_inet_filter_term_from_destination_address []Firewall_Family_Inet_Filter_Term_From_Destination_address_Model - diags.Append(v_firewall_family_inet_filter_term_from.Destination_address.ElementsAs(ctx, &var_firewall_family_inet_filter_term_from_destination_address, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Destination_address = make([]xml_Firewall_Family_Inet_Filter_Term_From_Destination_address, len(var_firewall_family_inet_filter_term_from_destination_address)) - - for i_firewall_family_inet_filter_term_from_destination_address, v_firewall_family_inet_filter_term_from_destination_address := range var_firewall_family_inet_filter_term_from_destination_address { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Destination_address[i_firewall_family_inet_filter_term_from_destination_address].Name = v_firewall_family_inet_filter_term_from_destination_address.Name.ValueStringPointer() - } - var var_firewall_family_inet_filter_term_from_protocol []string - diags.Append(v_firewall_family_inet_filter_term_from.Protocol.ElementsAs(ctx, &var_firewall_family_inet_filter_term_from_protocol, false)...) - if diags.HasError() { - return cfg - } - for _, v_firewall_family_inet_filter_term_from_protocol := range var_firewall_family_inet_filter_term_from_protocol { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Protocol = append(cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Protocol, &v_firewall_family_inet_filter_term_from_protocol) - } - var var_firewall_family_inet_filter_term_from_destination_port []string - diags.Append(v_firewall_family_inet_filter_term_from.Destination_port.ElementsAs(ctx, &var_firewall_family_inet_filter_term_from_destination_port, false)...) - if diags.HasError() { - return cfg - } - for _, v_firewall_family_inet_filter_term_from_destination_port := range var_firewall_family_inet_filter_term_from_destination_port { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Destination_port = append(cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].From[i_firewall_family_inet_filter_term_from].Destination_port, &v_firewall_family_inet_filter_term_from_destination_port) - } - } - var var_firewall_family_inet_filter_term_then []Firewall_Family_Inet_Filter_Term_Then_Model - diags.Append(v_firewall_family_inet_filter_term.Then.ElementsAs(ctx, &var_firewall_family_inet_filter_term_then, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].Then = make([]xml_Firewall_Family_Inet_Filter_Term_Then, len(var_firewall_family_inet_filter_term_then)) - - for i_firewall_family_inet_filter_term_then, v_firewall_family_inet_filter_term_then := range var_firewall_family_inet_filter_term_then { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].Then[i_firewall_family_inet_filter_term_then].Accept = v_firewall_family_inet_filter_term_then.Accept.ValueStringPointer() - var var_firewall_family_inet_filter_term_then_discard []Firewall_Family_Inet_Filter_Term_Then_Discard_Model - diags.Append(v_firewall_family_inet_filter_term_then.Discard.ElementsAs(ctx, &var_firewall_family_inet_filter_term_then_discard, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet[i_firewall_family_inet].Filter[i_firewall_family_inet_filter].Term[i_firewall_family_inet_filter_term].Then[i_firewall_family_inet_filter_term_then].Discard = make([]xml_Firewall_Family_Inet_Filter_Term_Then_Discard, len(var_firewall_family_inet_filter_term_then_discard)) - - } - } - } - } - var var_firewall_family_inet6 []Firewall_Family_Inet6_Model - diags.Append(v_firewall_family.Inet6.ElementsAs(ctx, &var_firewall_family_inet6, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6 = make([]xml_Firewall_Family_Inet6, len(var_firewall_family_inet6)) - - for i_firewall_family_inet6, v_firewall_family_inet6 := range var_firewall_family_inet6 { - var var_firewall_family_inet6_filter []Firewall_Family_Inet6_Filter_Model - diags.Append(v_firewall_family_inet6.Filter.ElementsAs(ctx, &var_firewall_family_inet6_filter, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter = make([]xml_Firewall_Family_Inet6_Filter, len(var_firewall_family_inet6_filter)) - - for i_firewall_family_inet6_filter, v_firewall_family_inet6_filter := range var_firewall_family_inet6_filter { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Name = v_firewall_family_inet6_filter.Name.ValueStringPointer() - var var_firewall_family_inet6_filter_term []Firewall_Family_Inet6_Filter_Term_Model - diags.Append(v_firewall_family_inet6_filter.Term.ElementsAs(ctx, &var_firewall_family_inet6_filter_term, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term = make([]xml_Firewall_Family_Inet6_Filter_Term, len(var_firewall_family_inet6_filter_term)) - - for i_firewall_family_inet6_filter_term, v_firewall_family_inet6_filter_term := range var_firewall_family_inet6_filter_term { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].Name = v_firewall_family_inet6_filter_term.Name.ValueStringPointer() - var var_firewall_family_inet6_filter_term_from []Firewall_Family_Inet6_Filter_Term_From_Model - diags.Append(v_firewall_family_inet6_filter_term.From.ElementsAs(ctx, &var_firewall_family_inet6_filter_term_from, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].From = make([]xml_Firewall_Family_Inet6_Filter_Term_From, len(var_firewall_family_inet6_filter_term_from)) - - for i_firewall_family_inet6_filter_term_from, v_firewall_family_inet6_filter_term_from := range var_firewall_family_inet6_filter_term_from { - var var_firewall_family_inet6_filter_term_from_next_header []string - diags.Append(v_firewall_family_inet6_filter_term_from.Next_header.ElementsAs(ctx, &var_firewall_family_inet6_filter_term_from_next_header, false)...) - if diags.HasError() { - return cfg - } - for _, v_firewall_family_inet6_filter_term_from_next_header := range var_firewall_family_inet6_filter_term_from_next_header { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].From[i_firewall_family_inet6_filter_term_from].Next_header = append(cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].From[i_firewall_family_inet6_filter_term_from].Next_header, &v_firewall_family_inet6_filter_term_from_next_header) - } - var var_firewall_family_inet6_filter_term_from_destination_port []string - diags.Append(v_firewall_family_inet6_filter_term_from.Destination_port.ElementsAs(ctx, &var_firewall_family_inet6_filter_term_from_destination_port, false)...) - if diags.HasError() { - return cfg - } - for _, v_firewall_family_inet6_filter_term_from_destination_port := range var_firewall_family_inet6_filter_term_from_destination_port { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].From[i_firewall_family_inet6_filter_term_from].Destination_port = append(cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].From[i_firewall_family_inet6_filter_term_from].Destination_port, &v_firewall_family_inet6_filter_term_from_destination_port) - } - } - var var_firewall_family_inet6_filter_term_then []Firewall_Family_Inet6_Filter_Term_Then_Model - diags.Append(v_firewall_family_inet6_filter_term.Then.ElementsAs(ctx, &var_firewall_family_inet6_filter_term_then, false)...) - if diags.HasError() { - return cfg - } - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].Then = make([]xml_Firewall_Family_Inet6_Filter_Term_Then, len(var_firewall_family_inet6_filter_term_then)) - - for i_firewall_family_inet6_filter_term_then, v_firewall_family_inet6_filter_term_then := range var_firewall_family_inet6_filter_term_then { - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].Then[i_firewall_family_inet6_filter_term_then].Accept = v_firewall_family_inet6_filter_term_then.Accept.ValueStringPointer() - cfg.Firewall[i_firewall].Family[i_firewall_family].Inet6[i_firewall_family_inet6].Filter[i_firewall_family_inet6_filter].Term[i_firewall_family_inet6_filter_term].Then[i_firewall_family_inet6_filter_term_then].Discard = v_firewall_family_inet6_filter_term_then.Discard.ValueStringPointer() - } - } - } - } - } - } - - var var_forwarding_options []Forwarding_options_Model - if model.Forwarding_options.IsNull() { - var_forwarding_options = []Forwarding_options_Model{} - } else { - diags.Append(model.Forwarding_options.ElementsAs(ctx, &var_forwarding_options, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Forwarding_options = make([]xml_Forwarding_options, len(var_forwarding_options)) - for i_forwarding_options, v_forwarding_options := range var_forwarding_options { - var var_forwarding_options_storm_control_profiles []Forwarding_options_Storm_control_profiles_Model - diags.Append(v_forwarding_options.Storm_control_profiles.ElementsAs(ctx, &var_forwarding_options_storm_control_profiles, false)...) - if diags.HasError() { - return cfg - } - cfg.Forwarding_options[i_forwarding_options].Storm_control_profiles = make([]xml_Forwarding_options_Storm_control_profiles, len(var_forwarding_options_storm_control_profiles)) - - for i_forwarding_options_storm_control_profiles, v_forwarding_options_storm_control_profiles := range var_forwarding_options_storm_control_profiles { - cfg.Forwarding_options[i_forwarding_options].Storm_control_profiles[i_forwarding_options_storm_control_profiles].Name = v_forwarding_options_storm_control_profiles.Name.ValueStringPointer() - var var_forwarding_options_storm_control_profiles_all []Forwarding_options_Storm_control_profiles_All_Model - diags.Append(v_forwarding_options_storm_control_profiles.All.ElementsAs(ctx, &var_forwarding_options_storm_control_profiles_all, false)...) - if diags.HasError() { - return cfg - } - cfg.Forwarding_options[i_forwarding_options].Storm_control_profiles[i_forwarding_options_storm_control_profiles].All = make([]xml_Forwarding_options_Storm_control_profiles_All, len(var_forwarding_options_storm_control_profiles_all)) - - for i_forwarding_options_storm_control_profiles_all, v_forwarding_options_storm_control_profiles_all := range var_forwarding_options_storm_control_profiles_all { - cfg.Forwarding_options[i_forwarding_options].Storm_control_profiles[i_forwarding_options_storm_control_profiles].All[i_forwarding_options_storm_control_profiles_all].Bandwidth_level = v_forwarding_options_storm_control_profiles_all.Bandwidth_level.ValueStringPointer() - } - } - } - - var var_interfaces []Interfaces_Model - if model.Interfaces.IsNull() { - var_interfaces = []Interfaces_Model{} - } else { - diags.Append(model.Interfaces.ElementsAs(ctx, &var_interfaces, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Interfaces = make([]xml_Interfaces, len(var_interfaces)) - for i_interfaces, v_interfaces := range var_interfaces { - var var_interfaces_interface []Interfaces_Interface_Model - diags.Append(v_interfaces.Interface.ElementsAs(ctx, &var_interfaces_interface, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface = make([]xml_Interfaces_Interface, len(var_interfaces_interface)) - - for i_interfaces_interface, v_interfaces_interface := range var_interfaces_interface { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Name = v_interfaces_interface.Name.ValueStringPointer() - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Mtu = v_interfaces_interface.Mtu.ValueStringPointer() - var var_interfaces_interface_ether_options []Interfaces_Interface_Ether_options_Model - diags.Append(v_interfaces_interface.Ether_options.ElementsAs(ctx, &var_interfaces_interface_ether_options, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Ether_options = make([]xml_Interfaces_Interface_Ether_options, len(var_interfaces_interface_ether_options)) - - for i_interfaces_interface_ether_options, v_interfaces_interface_ether_options := range var_interfaces_interface_ether_options { - var var_interfaces_interface_ether_options_ieee_802_3ad []Interfaces_Interface_Ether_options_Ieee_802_3ad_Model - diags.Append(v_interfaces_interface_ether_options.Ieee_802_3ad.ElementsAs(ctx, &var_interfaces_interface_ether_options_ieee_802_3ad, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Ether_options[i_interfaces_interface_ether_options].Ieee_802_3ad = make([]xml_Interfaces_Interface_Ether_options_Ieee_802_3ad, len(var_interfaces_interface_ether_options_ieee_802_3ad)) - - for i_interfaces_interface_ether_options_ieee_802_3ad, v_interfaces_interface_ether_options_ieee_802_3ad := range var_interfaces_interface_ether_options_ieee_802_3ad { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Ether_options[i_interfaces_interface_ether_options].Ieee_802_3ad[i_interfaces_interface_ether_options_ieee_802_3ad].Bundle = v_interfaces_interface_ether_options_ieee_802_3ad.Bundle.ValueStringPointer() - } - } - var var_interfaces_interface_aggregated_ether_options []Interfaces_Interface_Aggregated_ether_options_Model - diags.Append(v_interfaces_interface.Aggregated_ether_options.ElementsAs(ctx, &var_interfaces_interface_aggregated_ether_options, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Aggregated_ether_options = make([]xml_Interfaces_Interface_Aggregated_ether_options, len(var_interfaces_interface_aggregated_ether_options)) - - for i_interfaces_interface_aggregated_ether_options, v_interfaces_interface_aggregated_ether_options := range var_interfaces_interface_aggregated_ether_options { - var var_interfaces_interface_aggregated_ether_options_lacp []Interfaces_Interface_Aggregated_ether_options_Lacp_Model - diags.Append(v_interfaces_interface_aggregated_ether_options.Lacp.ElementsAs(ctx, &var_interfaces_interface_aggregated_ether_options_lacp, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Aggregated_ether_options[i_interfaces_interface_aggregated_ether_options].Lacp = make([]xml_Interfaces_Interface_Aggregated_ether_options_Lacp, len(var_interfaces_interface_aggregated_ether_options_lacp)) - - for i_interfaces_interface_aggregated_ether_options_lacp, v_interfaces_interface_aggregated_ether_options_lacp := range var_interfaces_interface_aggregated_ether_options_lacp { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Aggregated_ether_options[i_interfaces_interface_aggregated_ether_options].Lacp[i_interfaces_interface_aggregated_ether_options_lacp].Active = v_interfaces_interface_aggregated_ether_options_lacp.Active.ValueStringPointer() - } - } - var var_interfaces_interface_unit []Interfaces_Interface_Unit_Model - diags.Append(v_interfaces_interface.Unit.ElementsAs(ctx, &var_interfaces_interface_unit, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit = make([]xml_Interfaces_Interface_Unit, len(var_interfaces_interface_unit)) - - for i_interfaces_interface_unit, v_interfaces_interface_unit := range var_interfaces_interface_unit { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Name = v_interfaces_interface_unit.Name.ValueStringPointer() - var var_interfaces_interface_unit_family []Interfaces_Interface_Unit_Family_Model - diags.Append(v_interfaces_interface_unit.Family.ElementsAs(ctx, &var_interfaces_interface_unit_family, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family = make([]xml_Interfaces_Interface_Unit_Family, len(var_interfaces_interface_unit_family)) - - for i_interfaces_interface_unit_family, v_interfaces_interface_unit_family := range var_interfaces_interface_unit_family { - var var_interfaces_interface_unit_family_inet []Interfaces_Interface_Unit_Family_Inet_Model - diags.Append(v_interfaces_interface_unit_family.Inet.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet = make([]xml_Interfaces_Interface_Unit_Family_Inet, len(var_interfaces_interface_unit_family_inet)) - - for i_interfaces_interface_unit_family_inet, v_interfaces_interface_unit_family_inet := range var_interfaces_interface_unit_family_inet { - var var_interfaces_interface_unit_family_inet_filter []Interfaces_Interface_Unit_Family_Inet_Filter_Model - diags.Append(v_interfaces_interface_unit_family_inet.Filter.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet_filter, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet[i_interfaces_interface_unit_family_inet].Filter = make([]xml_Interfaces_Interface_Unit_Family_Inet_Filter, len(var_interfaces_interface_unit_family_inet_filter)) - - for i_interfaces_interface_unit_family_inet_filter, v_interfaces_interface_unit_family_inet_filter := range var_interfaces_interface_unit_family_inet_filter { - var var_interfaces_interface_unit_family_inet_filter_input []Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model - diags.Append(v_interfaces_interface_unit_family_inet_filter.Input.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet_filter_input, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet[i_interfaces_interface_unit_family_inet].Filter[i_interfaces_interface_unit_family_inet_filter].Input = make([]xml_Interfaces_Interface_Unit_Family_Inet_Filter_Input, len(var_interfaces_interface_unit_family_inet_filter_input)) - - for i_interfaces_interface_unit_family_inet_filter_input, v_interfaces_interface_unit_family_inet_filter_input := range var_interfaces_interface_unit_family_inet_filter_input { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet[i_interfaces_interface_unit_family_inet].Filter[i_interfaces_interface_unit_family_inet_filter].Input[i_interfaces_interface_unit_family_inet_filter_input].Filter_name = v_interfaces_interface_unit_family_inet_filter_input.Filter_name.ValueStringPointer() - } - } - var var_interfaces_interface_unit_family_inet_address []Interfaces_Interface_Unit_Family_Inet_Address_Model - diags.Append(v_interfaces_interface_unit_family_inet.Address.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet_address, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet[i_interfaces_interface_unit_family_inet].Address = make([]xml_Interfaces_Interface_Unit_Family_Inet_Address, len(var_interfaces_interface_unit_family_inet_address)) - - for i_interfaces_interface_unit_family_inet_address, v_interfaces_interface_unit_family_inet_address := range var_interfaces_interface_unit_family_inet_address { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet[i_interfaces_interface_unit_family_inet].Address[i_interfaces_interface_unit_family_inet_address].Name = v_interfaces_interface_unit_family_inet_address.Name.ValueStringPointer() - } - } - var var_interfaces_interface_unit_family_inet6 []Interfaces_Interface_Unit_Family_Inet6_Model - diags.Append(v_interfaces_interface_unit_family.Inet6.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet6, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6 = make([]xml_Interfaces_Interface_Unit_Family_Inet6, len(var_interfaces_interface_unit_family_inet6)) - - for i_interfaces_interface_unit_family_inet6, v_interfaces_interface_unit_family_inet6 := range var_interfaces_interface_unit_family_inet6 { - var var_interfaces_interface_unit_family_inet6_filter []Interfaces_Interface_Unit_Family_Inet6_Filter_Model - diags.Append(v_interfaces_interface_unit_family_inet6.Filter.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet6_filter, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6[i_interfaces_interface_unit_family_inet6].Filter = make([]xml_Interfaces_Interface_Unit_Family_Inet6_Filter, len(var_interfaces_interface_unit_family_inet6_filter)) - - for i_interfaces_interface_unit_family_inet6_filter, v_interfaces_interface_unit_family_inet6_filter := range var_interfaces_interface_unit_family_inet6_filter { - var var_interfaces_interface_unit_family_inet6_filter_input []Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model - diags.Append(v_interfaces_interface_unit_family_inet6_filter.Input.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet6_filter_input, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6[i_interfaces_interface_unit_family_inet6].Filter[i_interfaces_interface_unit_family_inet6_filter].Input = make([]xml_Interfaces_Interface_Unit_Family_Inet6_Filter_Input, len(var_interfaces_interface_unit_family_inet6_filter_input)) - - for i_interfaces_interface_unit_family_inet6_filter_input, v_interfaces_interface_unit_family_inet6_filter_input := range var_interfaces_interface_unit_family_inet6_filter_input { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6[i_interfaces_interface_unit_family_inet6].Filter[i_interfaces_interface_unit_family_inet6_filter].Input[i_interfaces_interface_unit_family_inet6_filter_input].Filter_name = v_interfaces_interface_unit_family_inet6_filter_input.Filter_name.ValueStringPointer() - } - } - var var_interfaces_interface_unit_family_inet6_address []Interfaces_Interface_Unit_Family_Inet6_Address_Model - diags.Append(v_interfaces_interface_unit_family_inet6.Address.ElementsAs(ctx, &var_interfaces_interface_unit_family_inet6_address, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6[i_interfaces_interface_unit_family_inet6].Address = make([]xml_Interfaces_Interface_Unit_Family_Inet6_Address, len(var_interfaces_interface_unit_family_inet6_address)) - - for i_interfaces_interface_unit_family_inet6_address, v_interfaces_interface_unit_family_inet6_address := range var_interfaces_interface_unit_family_inet6_address { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Inet6[i_interfaces_interface_unit_family_inet6].Address[i_interfaces_interface_unit_family_inet6_address].Name = v_interfaces_interface_unit_family_inet6_address.Name.ValueStringPointer() - } - } - var var_interfaces_interface_unit_family_ethernet_switching []Interfaces_Interface_Unit_Family_Ethernet_switching_Model - diags.Append(v_interfaces_interface_unit_family.Ethernet_switching.ElementsAs(ctx, &var_interfaces_interface_unit_family_ethernet_switching, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching = make([]xml_Interfaces_Interface_Unit_Family_Ethernet_switching, len(var_interfaces_interface_unit_family_ethernet_switching)) - - for i_interfaces_interface_unit_family_ethernet_switching, v_interfaces_interface_unit_family_ethernet_switching := range var_interfaces_interface_unit_family_ethernet_switching { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Interface_mode = v_interfaces_interface_unit_family_ethernet_switching.Interface_mode.ValueStringPointer() - var var_interfaces_interface_unit_family_ethernet_switching_vlan []Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model - diags.Append(v_interfaces_interface_unit_family_ethernet_switching.Vlan.ElementsAs(ctx, &var_interfaces_interface_unit_family_ethernet_switching_vlan, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Vlan = make([]xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan, len(var_interfaces_interface_unit_family_ethernet_switching_vlan)) - - for i_interfaces_interface_unit_family_ethernet_switching_vlan, v_interfaces_interface_unit_family_ethernet_switching_vlan := range var_interfaces_interface_unit_family_ethernet_switching_vlan { - var var_interfaces_interface_unit_family_ethernet_switching_vlan_members []string - diags.Append(v_interfaces_interface_unit_family_ethernet_switching_vlan.Members.ElementsAs(ctx, &var_interfaces_interface_unit_family_ethernet_switching_vlan_members, false)...) - if diags.HasError() { - return cfg - } - for _, v_interfaces_interface_unit_family_ethernet_switching_vlan_members := range var_interfaces_interface_unit_family_ethernet_switching_vlan_members { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Vlan[i_interfaces_interface_unit_family_ethernet_switching_vlan].Members = append(cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Vlan[i_interfaces_interface_unit_family_ethernet_switching_vlan].Members, &v_interfaces_interface_unit_family_ethernet_switching_vlan_members) - } - } - var var_interfaces_interface_unit_family_ethernet_switching_storm_control []Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model - diags.Append(v_interfaces_interface_unit_family_ethernet_switching.Storm_control.ElementsAs(ctx, &var_interfaces_interface_unit_family_ethernet_switching_storm_control, false)...) - if diags.HasError() { - return cfg - } - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Storm_control = make([]xml_Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control, len(var_interfaces_interface_unit_family_ethernet_switching_storm_control)) - - for i_interfaces_interface_unit_family_ethernet_switching_storm_control, v_interfaces_interface_unit_family_ethernet_switching_storm_control := range var_interfaces_interface_unit_family_ethernet_switching_storm_control { - cfg.Interfaces[i_interfaces].Interface[i_interfaces_interface].Unit[i_interfaces_interface_unit].Family[i_interfaces_interface_unit_family].Ethernet_switching[i_interfaces_interface_unit_family_ethernet_switching].Storm_control[i_interfaces_interface_unit_family_ethernet_switching_storm_control].Profile_name = v_interfaces_interface_unit_family_ethernet_switching_storm_control.Profile_name.ValueStringPointer() - } - } - } - } - } - } - - var var_policy_options []Policy_options_Model - if model.Policy_options.IsNull() { - var_policy_options = []Policy_options_Model{} - } else { - diags.Append(model.Policy_options.ElementsAs(ctx, &var_policy_options, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Policy_options = make([]xml_Policy_options, len(var_policy_options)) - for i_policy_options, v_policy_options := range var_policy_options { - var var_policy_options_policy_statement []Policy_options_Policy_statement_Model - diags.Append(v_policy_options.Policy_statement.ElementsAs(ctx, &var_policy_options_policy_statement, false)...) - if diags.HasError() { - return cfg - } - cfg.Policy_options[i_policy_options].Policy_statement = make([]xml_Policy_options_Policy_statement, len(var_policy_options_policy_statement)) - - for i_policy_options_policy_statement, v_policy_options_policy_statement := range var_policy_options_policy_statement { - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Name = v_policy_options_policy_statement.Name.ValueStringPointer() - var var_policy_options_policy_statement_term []Policy_options_Policy_statement_Term_Model - diags.Append(v_policy_options_policy_statement.Term.ElementsAs(ctx, &var_policy_options_policy_statement_term, false)...) - if diags.HasError() { - return cfg - } - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term = make([]xml_Policy_options_Policy_statement_Term, len(var_policy_options_policy_statement_term)) - - for i_policy_options_policy_statement_term, v_policy_options_policy_statement_term := range var_policy_options_policy_statement_term { - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].Name = v_policy_options_policy_statement_term.Name.ValueStringPointer() - var var_policy_options_policy_statement_term_from []Policy_options_Policy_statement_Term_From_Model - diags.Append(v_policy_options_policy_statement_term.From.ElementsAs(ctx, &var_policy_options_policy_statement_term_from, false)...) - if diags.HasError() { - return cfg - } - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].From = make([]xml_Policy_options_Policy_statement_Term_From, len(var_policy_options_policy_statement_term_from)) - - for i_policy_options_policy_statement_term_from, v_policy_options_policy_statement_term_from := range var_policy_options_policy_statement_term_from { - var var_policy_options_policy_statement_term_from_route_filter []Policy_options_Policy_statement_Term_From_Route_filter_Model - diags.Append(v_policy_options_policy_statement_term_from.Route_filter.ElementsAs(ctx, &var_policy_options_policy_statement_term_from_route_filter, false)...) - if diags.HasError() { - return cfg - } - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].From[i_policy_options_policy_statement_term_from].Route_filter = make([]xml_Policy_options_Policy_statement_Term_From_Route_filter, len(var_policy_options_policy_statement_term_from_route_filter)) - - for i_policy_options_policy_statement_term_from_route_filter, v_policy_options_policy_statement_term_from_route_filter := range var_policy_options_policy_statement_term_from_route_filter { - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].From[i_policy_options_policy_statement_term_from].Route_filter[i_policy_options_policy_statement_term_from_route_filter].Address = v_policy_options_policy_statement_term_from_route_filter.Address.ValueStringPointer() - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].From[i_policy_options_policy_statement_term_from].Route_filter[i_policy_options_policy_statement_term_from_route_filter].Exact = v_policy_options_policy_statement_term_from_route_filter.Exact.ValueStringPointer() - } - } - var var_policy_options_policy_statement_term_then []Policy_options_Policy_statement_Term_Then_Model - diags.Append(v_policy_options_policy_statement_term.Then.ElementsAs(ctx, &var_policy_options_policy_statement_term_then, false)...) - if diags.HasError() { - return cfg - } - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].Then = make([]xml_Policy_options_Policy_statement_Term_Then, len(var_policy_options_policy_statement_term_then)) - - for i_policy_options_policy_statement_term_then, v_policy_options_policy_statement_term_then := range var_policy_options_policy_statement_term_then { - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].Then[i_policy_options_policy_statement_term_then].Accept = v_policy_options_policy_statement_term_then.Accept.ValueStringPointer() - cfg.Policy_options[i_policy_options].Policy_statement[i_policy_options_policy_statement].Term[i_policy_options_policy_statement_term].Then[i_policy_options_policy_statement_term_then].Reject = v_policy_options_policy_statement_term_then.Reject.ValueStringPointer() - } - } - } - } - - var var_protocols []Protocols_Model - if model.Protocols.IsNull() { - var_protocols = []Protocols_Model{} - } else { - diags.Append(model.Protocols.ElementsAs(ctx, &var_protocols, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Protocols = make([]xml_Protocols, len(var_protocols)) - for i_protocols, v_protocols := range var_protocols { - var var_protocols_bgp []Protocols_Bgp_Model - diags.Append(v_protocols.Bgp.ElementsAs(ctx, &var_protocols_bgp, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp = make([]xml_Protocols_Bgp, len(var_protocols_bgp)) - - for i_protocols_bgp, v_protocols_bgp := range var_protocols_bgp { - var var_protocols_bgp_group []Protocols_Bgp_Group_Model - diags.Append(v_protocols_bgp.Group.ElementsAs(ctx, &var_protocols_bgp_group, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group = make([]xml_Protocols_Bgp_Group, len(var_protocols_bgp_group)) - - for i_protocols_bgp_group, v_protocols_bgp_group := range var_protocols_bgp_group { - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Name = v_protocols_bgp_group.Name.ValueStringPointer() - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Type = v_protocols_bgp_group.Type.ValueStringPointer() - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Peer_as = v_protocols_bgp_group.Peer_as.ValueStringPointer() - var var_protocols_bgp_group_neighbor []Protocols_Bgp_Group_Neighbor_Model - diags.Append(v_protocols_bgp_group.Neighbor.ElementsAs(ctx, &var_protocols_bgp_group_neighbor, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor = make([]xml_Protocols_Bgp_Group_Neighbor, len(var_protocols_bgp_group_neighbor)) - - for i_protocols_bgp_group_neighbor, v_protocols_bgp_group_neighbor := range var_protocols_bgp_group_neighbor { - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Name = v_protocols_bgp_group_neighbor.Name.ValueStringPointer() - var var_protocols_bgp_group_neighbor_import []string - diags.Append(v_protocols_bgp_group_neighbor.Import.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_import, false)...) - if diags.HasError() { - return cfg - } - for _, v_protocols_bgp_group_neighbor_import := range var_protocols_bgp_group_neighbor_import { - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Import = append(cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Import, &v_protocols_bgp_group_neighbor_import) - } - var var_protocols_bgp_group_neighbor_family []Protocols_Bgp_Group_Neighbor_Family_Model - diags.Append(v_protocols_bgp_group_neighbor.Family.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_family, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Family = make([]xml_Protocols_Bgp_Group_Neighbor_Family, len(var_protocols_bgp_group_neighbor_family)) - - for i_protocols_bgp_group_neighbor_family, v_protocols_bgp_group_neighbor_family := range var_protocols_bgp_group_neighbor_family { - var var_protocols_bgp_group_neighbor_family_inet []Protocols_Bgp_Group_Neighbor_Family_Inet_Model - diags.Append(v_protocols_bgp_group_neighbor_family.Inet.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_family_inet, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Family[i_protocols_bgp_group_neighbor_family].Inet = make([]xml_Protocols_Bgp_Group_Neighbor_Family_Inet, len(var_protocols_bgp_group_neighbor_family_inet)) - - for i_protocols_bgp_group_neighbor_family_inet, v_protocols_bgp_group_neighbor_family_inet := range var_protocols_bgp_group_neighbor_family_inet { - var var_protocols_bgp_group_neighbor_family_inet_unicast []Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model - diags.Append(v_protocols_bgp_group_neighbor_family_inet.Unicast.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_family_inet_unicast, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Family[i_protocols_bgp_group_neighbor_family].Inet[i_protocols_bgp_group_neighbor_family_inet].Unicast = make([]xml_Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast, len(var_protocols_bgp_group_neighbor_family_inet_unicast)) - - } - var var_protocols_bgp_group_neighbor_family_inet6 []Protocols_Bgp_Group_Neighbor_Family_Inet6_Model - diags.Append(v_protocols_bgp_group_neighbor_family.Inet6.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_family_inet6, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Family[i_protocols_bgp_group_neighbor_family].Inet6 = make([]xml_Protocols_Bgp_Group_Neighbor_Family_Inet6, len(var_protocols_bgp_group_neighbor_family_inet6)) - - for i_protocols_bgp_group_neighbor_family_inet6, v_protocols_bgp_group_neighbor_family_inet6 := range var_protocols_bgp_group_neighbor_family_inet6 { - var var_protocols_bgp_group_neighbor_family_inet6_unicast []Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model - diags.Append(v_protocols_bgp_group_neighbor_family_inet6.Unicast.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_family_inet6_unicast, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Family[i_protocols_bgp_group_neighbor_family].Inet6[i_protocols_bgp_group_neighbor_family_inet6].Unicast = make([]xml_Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast, len(var_protocols_bgp_group_neighbor_family_inet6_unicast)) - - } - } - var var_protocols_bgp_group_neighbor_export []string - diags.Append(v_protocols_bgp_group_neighbor.Export.ElementsAs(ctx, &var_protocols_bgp_group_neighbor_export, false)...) - if diags.HasError() { - return cfg - } - for _, v_protocols_bgp_group_neighbor_export := range var_protocols_bgp_group_neighbor_export { - cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Export = append(cfg.Protocols[i_protocols].Bgp[i_protocols_bgp].Group[i_protocols_bgp_group].Neighbor[i_protocols_bgp_group_neighbor].Export, &v_protocols_bgp_group_neighbor_export) - } - } - } - } - var var_protocols_lldp []Protocols_Lldp_Model - diags.Append(v_protocols.Lldp.ElementsAs(ctx, &var_protocols_lldp, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Lldp = make([]xml_Protocols_Lldp, len(var_protocols_lldp)) - - for i_protocols_lldp, v_protocols_lldp := range var_protocols_lldp { - var var_protocols_lldp_interface []Protocols_Lldp_Interface_Model - diags.Append(v_protocols_lldp.Interface.ElementsAs(ctx, &var_protocols_lldp_interface, false)...) - if diags.HasError() { - return cfg - } - cfg.Protocols[i_protocols].Lldp[i_protocols_lldp].Interface = make([]xml_Protocols_Lldp_Interface, len(var_protocols_lldp_interface)) - - for i_protocols_lldp_interface, v_protocols_lldp_interface := range var_protocols_lldp_interface { - cfg.Protocols[i_protocols].Lldp[i_protocols_lldp].Interface[i_protocols_lldp_interface].Name = v_protocols_lldp_interface.Name.ValueStringPointer() - } - } - } - - var var_routing_options []Routing_options_Model - if model.Routing_options.IsNull() { - var_routing_options = []Routing_options_Model{} - } else { - diags.Append(model.Routing_options.ElementsAs(ctx, &var_routing_options, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Routing_options = make([]xml_Routing_options, len(var_routing_options)) - for i_routing_options, v_routing_options := range var_routing_options { - var var_routing_options_autonomous_system []Routing_options_Autonomous_system_Model - diags.Append(v_routing_options.Autonomous_system.ElementsAs(ctx, &var_routing_options_autonomous_system, false)...) - if diags.HasError() { - return cfg - } - cfg.Routing_options[i_routing_options].Autonomous_system = make([]xml_Routing_options_Autonomous_system, len(var_routing_options_autonomous_system)) - - for i_routing_options_autonomous_system, v_routing_options_autonomous_system := range var_routing_options_autonomous_system { - cfg.Routing_options[i_routing_options].Autonomous_system[i_routing_options_autonomous_system].As_number = v_routing_options_autonomous_system.As_number.ValueStringPointer() - } - var var_routing_options_static []Routing_options_Static_Model - diags.Append(v_routing_options.Static.ElementsAs(ctx, &var_routing_options_static, false)...) - if diags.HasError() { - return cfg - } - cfg.Routing_options[i_routing_options].Static = make([]xml_Routing_options_Static, len(var_routing_options_static)) - - for i_routing_options_static, v_routing_options_static := range var_routing_options_static { - var var_routing_options_static_route []Routing_options_Static_Route_Model - diags.Append(v_routing_options_static.Route.ElementsAs(ctx, &var_routing_options_static_route, false)...) - if diags.HasError() { - return cfg - } - cfg.Routing_options[i_routing_options].Static[i_routing_options_static].Route = make([]xml_Routing_options_Static_Route, len(var_routing_options_static_route)) - - for i_routing_options_static_route, v_routing_options_static_route := range var_routing_options_static_route { - cfg.Routing_options[i_routing_options].Static[i_routing_options_static].Route[i_routing_options_static_route].Name = v_routing_options_static_route.Name.ValueStringPointer() - cfg.Routing_options[i_routing_options].Static[i_routing_options_static].Route[i_routing_options_static_route].Discard = v_routing_options_static_route.Discard.ValueStringPointer() - } - } - } - - var var_system []System_Model - if model.System.IsNull() { - var_system = []System_Model{} - } else { - diags.Append(model.System.ElementsAs(ctx, &var_system, false)...) - if diags.HasError() { - return cfg - } - } - cfg.System = make([]xml_System, len(var_system)) - for i_system, v_system := range var_system { - cfg.System[i_system].Host_name = v_system.Host_name.ValueStringPointer() - var var_system_root_authentication []System_Root_authentication_Model - diags.Append(v_system.Root_authentication.ElementsAs(ctx, &var_system_root_authentication, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Root_authentication = make([]xml_System_Root_authentication, len(var_system_root_authentication)) - - for i_system_root_authentication, v_system_root_authentication := range var_system_root_authentication { - cfg.System[i_system].Root_authentication[i_system_root_authentication].Encrypted_password = v_system_root_authentication.Encrypted_password.ValueStringPointer() - } - var var_system_login []System_Login_Model - diags.Append(v_system.Login.ElementsAs(ctx, &var_system_login, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Login = make([]xml_System_Login, len(var_system_login)) - - for i_system_login, v_system_login := range var_system_login { - var var_system_login_user []System_Login_User_Model - diags.Append(v_system_login.User.ElementsAs(ctx, &var_system_login_user, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Login[i_system_login].User = make([]xml_System_Login_User, len(var_system_login_user)) - - for i_system_login_user, v_system_login_user := range var_system_login_user { - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Name = v_system_login_user.Name.ValueStringPointer() - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Uid = v_system_login_user.Uid.ValueStringPointer() - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Class = v_system_login_user.Class.ValueStringPointer() - var var_system_login_user_authentication []System_Login_User_Authentication_Model - diags.Append(v_system_login_user.Authentication.ElementsAs(ctx, &var_system_login_user_authentication, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Authentication = make([]xml_System_Login_User_Authentication, len(var_system_login_user_authentication)) - - for i_system_login_user_authentication, v_system_login_user_authentication := range var_system_login_user_authentication { - var var_system_login_user_authentication_ssh_ed25519 []System_Login_User_Authentication_Ssh_ed25519_Model - diags.Append(v_system_login_user_authentication.Ssh_ed25519.ElementsAs(ctx, &var_system_login_user_authentication_ssh_ed25519, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Authentication[i_system_login_user_authentication].Ssh_ed25519 = make([]xml_System_Login_User_Authentication_Ssh_ed25519, len(var_system_login_user_authentication_ssh_ed25519)) - - for i_system_login_user_authentication_ssh_ed25519, v_system_login_user_authentication_ssh_ed25519 := range var_system_login_user_authentication_ssh_ed25519 { - cfg.System[i_system].Login[i_system_login].User[i_system_login_user].Authentication[i_system_login_user_authentication].Ssh_ed25519[i_system_login_user_authentication_ssh_ed25519].Name = v_system_login_user_authentication_ssh_ed25519.Name.ValueStringPointer() - } - } - } - } - var var_system_services []System_Services_Model - diags.Append(v_system.Services.ElementsAs(ctx, &var_system_services, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Services = make([]xml_System_Services, len(var_system_services)) - - for i_system_services, v_system_services := range var_system_services { - var var_system_services_netconf []System_Services_Netconf_Model - diags.Append(v_system_services.Netconf.ElementsAs(ctx, &var_system_services_netconf, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Services[i_system_services].Netconf = make([]xml_System_Services_Netconf, len(var_system_services_netconf)) - - for i_system_services_netconf, v_system_services_netconf := range var_system_services_netconf { - var var_system_services_netconf_ssh []System_Services_Netconf_Ssh_Model - diags.Append(v_system_services_netconf.Ssh.ElementsAs(ctx, &var_system_services_netconf_ssh, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Services[i_system_services].Netconf[i_system_services_netconf].Ssh = make([]xml_System_Services_Netconf_Ssh, len(var_system_services_netconf_ssh)) - - } - var var_system_services_ssh []System_Services_Ssh_Model - diags.Append(v_system_services.Ssh.ElementsAs(ctx, &var_system_services_ssh, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Services[i_system_services].Ssh = make([]xml_System_Services_Ssh, len(var_system_services_ssh)) - - for i_system_services_ssh, v_system_services_ssh := range var_system_services_ssh { - cfg.System[i_system].Services[i_system_services].Ssh[i_system_services_ssh].Root_login = v_system_services_ssh.Root_login.ValueStringPointer() - } - } - cfg.System[i_system].Time_zone = v_system.Time_zone.ValueStringPointer() - var var_system_authentication_order []string - diags.Append(v_system.Authentication_order.ElementsAs(ctx, &var_system_authentication_order, false)...) - if diags.HasError() { - return cfg - } - for _, v_system_authentication_order := range var_system_authentication_order { - cfg.System[i_system].Authentication_order = append(cfg.System[i_system].Authentication_order, &v_system_authentication_order) - } - var var_system_ports []System_Ports_Model - diags.Append(v_system.Ports.ElementsAs(ctx, &var_system_ports, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Ports = make([]xml_System_Ports, len(var_system_ports)) - - for i_system_ports, v_system_ports := range var_system_ports { - var var_system_ports_console []System_Ports_Console_Model - diags.Append(v_system_ports.Console.ElementsAs(ctx, &var_system_ports_console, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Ports[i_system_ports].Console = make([]xml_System_Ports_Console, len(var_system_ports_console)) - - for i_system_ports_console, v_system_ports_console := range var_system_ports_console { - cfg.System[i_system].Ports[i_system_ports].Console[i_system_ports_console].Log_out_on_disconnect = v_system_ports_console.Log_out_on_disconnect.ValueStringPointer() - } - } - var var_system_name_server []System_Name_server_Model - diags.Append(v_system.Name_server.ElementsAs(ctx, &var_system_name_server, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Name_server = make([]xml_System_Name_server, len(var_system_name_server)) - - for i_system_name_server, v_system_name_server := range var_system_name_server { - cfg.System[i_system].Name_server[i_system_name_server].Name = v_system_name_server.Name.ValueStringPointer() - } - var var_system_processes []System_Processes_Model - diags.Append(v_system.Processes.ElementsAs(ctx, &var_system_processes, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Processes = make([]xml_System_Processes, len(var_system_processes)) - - for i_system_processes, v_system_processes := range var_system_processes { - var var_system_processes_daemon_process []System_Processes_Daemon_process_Model - diags.Append(v_system_processes.Daemon_process.ElementsAs(ctx, &var_system_processes_daemon_process, false)...) - if diags.HasError() { - return cfg - } - cfg.System[i_system].Processes[i_system_processes].Daemon_process = make([]xml_System_Processes_Daemon_process, len(var_system_processes_daemon_process)) - - for i_system_processes_daemon_process, v_system_processes_daemon_process := range var_system_processes_daemon_process { - cfg.System[i_system].Processes[i_system_processes].Daemon_process[i_system_processes_daemon_process].Name = v_system_processes_daemon_process.Name.ValueStringPointer() - cfg.System[i_system].Processes[i_system_processes].Daemon_process[i_system_processes_daemon_process].Disable = v_system_processes_daemon_process.Disable.ValueStringPointer() - } - } - } - - var var_virtual_chassis []Virtual_chassis_Model - if model.Virtual_chassis.IsNull() { - var_virtual_chassis = []Virtual_chassis_Model{} - } else { - diags.Append(model.Virtual_chassis.ElementsAs(ctx, &var_virtual_chassis, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Virtual_chassis = make([]xml_Virtual_chassis, len(var_virtual_chassis)) - for i_virtual_chassis, v_virtual_chassis := range var_virtual_chassis { - cfg.Virtual_chassis[i_virtual_chassis].Preprovisioned = v_virtual_chassis.Preprovisioned.ValueStringPointer() - var var_virtual_chassis_member []Virtual_chassis_Member_Model - diags.Append(v_virtual_chassis.Member.ElementsAs(ctx, &var_virtual_chassis_member, false)...) - if diags.HasError() { - return cfg - } - cfg.Virtual_chassis[i_virtual_chassis].Member = make([]xml_Virtual_chassis_Member, len(var_virtual_chassis_member)) - - for i_virtual_chassis_member, v_virtual_chassis_member := range var_virtual_chassis_member { - cfg.Virtual_chassis[i_virtual_chassis].Member[i_virtual_chassis_member].Name = v_virtual_chassis_member.Name.ValueStringPointer() - cfg.Virtual_chassis[i_virtual_chassis].Member[i_virtual_chassis_member].Role = v_virtual_chassis_member.Role.ValueStringPointer() - cfg.Virtual_chassis[i_virtual_chassis].Member[i_virtual_chassis_member].Serial_number = v_virtual_chassis_member.Serial_number.ValueStringPointer() - } - } - - var var_vlans []Vlans_Model - if model.Vlans.IsNull() { - var_vlans = []Vlans_Model{} - } else { - diags.Append(model.Vlans.ElementsAs(ctx, &var_vlans, false)...) - if diags.HasError() { - return cfg - } - } - cfg.Vlans = make([]xml_Vlans, len(var_vlans)) - for i_vlans, v_vlans := range var_vlans { - var var_vlans_vlan []Vlans_Vlan_Model - diags.Append(v_vlans.Vlan.ElementsAs(ctx, &var_vlans_vlan, false)...) - if diags.HasError() { - return cfg - } - cfg.Vlans[i_vlans].Vlan = make([]xml_Vlans_Vlan, len(var_vlans_vlan)) - - for i_vlans_vlan, v_vlans_vlan := range var_vlans_vlan { - cfg.Vlans[i_vlans].Vlan[i_vlans_vlan].Name = v_vlans_vlan.Name.ValueStringPointer() - cfg.Vlans[i_vlans].Vlan[i_vlans_vlan].Vlan_id = v_vlans_vlan.Vlan_id.ValueStringPointer() - cfg.Vlans[i_vlans].Vlan[i_vlans_vlan].L3_interface = v_vlans_vlan.L3_interface.ValueStringPointer() - } - } - - - return cfg -} - -func marshalConfigXML(config xml_Configuration) ([]byte, error) { - var buf bytes.Buffer - buf.WriteString(xml.Header) - enc := xml.NewEncoder(&buf) - enc.Indent("", " ") - if err := enc.Encode(config); err != nil { - return nil, err - } - if err := enc.Flush(); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func alignConfigToReference(ctx context.Context, config xml_Configuration, reference ConfigResourceModel, diags *diag.Diagnostics) (xml_Configuration, error) { - referenceConfig := modelToConfig(ctx, reference, diags) - if diags.HasError() { - return xml_Configuration{}, nil - } - - currentXML, err := marshalConfigXML(config) - if err != nil { - return xml_Configuration{}, err - } - referenceXML, err := marshalConfigXML(referenceConfig) - if err != nil { - return xml_Configuration{}, err - } - - idx, err := patch.UnmarshalTrimmedSchemaIndex(TrimmedSchemaJSON) - if err != nil { - return xml_Configuration{}, err - } - - alignedXML, err := patch.AlignXMLOrderToReference(currentXML, referenceXML, idx) - if err != nil { - return xml_Configuration{}, err - } - - var aligned xml_Configuration - if err := xml.Unmarshal(alignedXML, &aligned); err != nil { - return xml_Configuration{}, err - } - - return aligned, nil -} - -func (r *configResource) readStateFromDevice(ctx context.Context, reference ConfigResourceModel, diags *diag.Diagnostics) (ConfigResourceModel, bool) { - // PATCHED (fabric): trust the apply — return the plan/prior state as the - // resource state instead of reading the whole device back (avoids - // "provider produced inconsistent result"). See apply_patches.py. - return reference, true -} - -func mergeReadStateWithReference(ctx context.Context, observed ConfigResourceModel, reference ConfigResourceModel) ConfigResourceModel { - // resource_name is Terraform-only (not in device XML), always preserve from plan. - observed.ResourceName = reference.ResourceName - - // For each top-level list attribute, if device readback returned null but the - // plan had a value, preserve the plan value. This handles containers whose - // XML structs are empty (no modeled children) so the Go XML unmarshaler - // returns nil and configToModel maps them to types.ListNull. - if observed.Chassis.IsNull() && !reference.Chassis.IsNull() { - observed.Chassis = reference.Chassis - } - if observed.Firewall.IsNull() && !reference.Firewall.IsNull() { - observed.Firewall = reference.Firewall - } - if observed.Forwarding_options.IsNull() && !reference.Forwarding_options.IsNull() { - observed.Forwarding_options = reference.Forwarding_options - } - if observed.Interfaces.IsNull() && !reference.Interfaces.IsNull() { - observed.Interfaces = reference.Interfaces - } - if observed.Policy_options.IsNull() && !reference.Policy_options.IsNull() { - observed.Policy_options = reference.Policy_options - } - if observed.Protocols.IsNull() && !reference.Protocols.IsNull() { - observed.Protocols = reference.Protocols - } - if observed.Routing_options.IsNull() && !reference.Routing_options.IsNull() { - observed.Routing_options = reference.Routing_options - } - if observed.System.IsNull() && !reference.System.IsNull() { - observed.System = reference.System - } - if observed.Virtual_chassis.IsNull() && !reference.Virtual_chassis.IsNull() { - observed.Virtual_chassis = reference.Virtual_chassis - } - if observed.Vlans.IsNull() && !reference.Vlans.IsNull() { - observed.Vlans = reference.Vlans - } - - return observed -} - - - - -// Create implements resource.Resource. -func (r *configResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - - var plan ConfigResourceModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - // Check for errors - if resp.Diagnostics.HasError() { - return - } - config := modelToConfig(ctx, plan, &resp.Diagnostics) - if resp.Diagnostics.HasError() { - return - } - err := r.client.SendDirectTransaction(config, false) - if err != nil { - resp.Diagnostics.AddError("Failed while applying configuration", err.Error()) - return - } - commit_err := r.client.SendCommit() - if commit_err != nil { - resp.Diagnostics.AddError("Failed while committing configuration", commit_err.Error()) - return - } - state, ok := r.readStateFromDevice(ctx, plan, &resp.Diagnostics) - if !ok { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - - - - -func configToModel(ctx context.Context, config xml_Configuration) ConfigResourceModel { - var state ConfigResourceModel - // Initialize chassis as Null; only materialize when we have elements - state.Chassis = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Model{}.AttrTypes()}) - - // Build list from device - chassis_List := make([]Chassis_Model, - len(config.Chassis)) - for i_chassis, v_chassis := range config.Chassis { - var chassis_model Chassis_Model - - // Build aggregated-devices list - chassis_aggregated_devices_List := make([]Chassis_Aggregated_devices_Model, len(v_chassis.Aggregated_devices)) - - - for i_chassis_aggregated_devices, v_chassis_aggregated_devices := range v_chassis.Aggregated_devices { - var chassis_aggregated_devices_model Chassis_Aggregated_devices_Model - - chassis_aggregated_devices_List[i_chassis_aggregated_devices] = - chassis_aggregated_devices_model - - // Build ethernet list - chassis_aggregated_devices_ethernet_List := make([]Chassis_Aggregated_devices_Ethernet_Model, len(v_chassis_aggregated_devices.Ethernet)) - - - for i_chassis_aggregated_devices_ethernet, v_chassis_aggregated_devices_ethernet := range v_chassis_aggregated_devices.Ethernet { - var chassis_aggregated_devices_ethernet_model Chassis_Aggregated_devices_Ethernet_Model - // leaf - if v_chassis_aggregated_devices_ethernet.Device_count == nil { - chassis_aggregated_devices_ethernet_model.Device_count = - types.StringNull() - } else { - chassis_aggregated_devices_ethernet_model.Device_count = - types.StringPointerValue(v_chassis_aggregated_devices_ethernet.Device_count) - } - - chassis_aggregated_devices_ethernet_List[i_chassis_aggregated_devices_ethernet] = - chassis_aggregated_devices_ethernet_model - } - - // Write ethernet field as Null when empty, else concrete list - if len(chassis_aggregated_devices_ethernet_List) == 0 { - chassis_aggregated_devices_model.Ethernet = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Ethernet_Model{}.AttrTypes()}) - } else { - chassis_aggregated_devices_model.Ethernet, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Ethernet_Model{}.AttrTypes()}, - chassis_aggregated_devices_ethernet_List, - ) - } - chassis_aggregated_devices_List[i_chassis_aggregated_devices] = chassis_aggregated_devices_model - } - - // Write aggregated-devices field as Null when empty, else concrete list - if len(chassis_aggregated_devices_List) == 0 { - chassis_model.Aggregated_devices = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Model{}.AttrTypes()}) - } else { - chassis_model.Aggregated_devices, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Aggregated_devices_Model{}.AttrTypes()}, - chassis_aggregated_devices_List, - ) - } - chassis_List[i_chassis] = chassis_model - - // Build fpc list - chassis_fpc_List := make([]Chassis_Fpc_Model, len(v_chassis.Fpc)) - - - for i_chassis_fpc, v_chassis_fpc := range v_chassis.Fpc { - var chassis_fpc_model Chassis_Fpc_Model - // leaf - if v_chassis_fpc.Name == nil { - chassis_fpc_model.Name = - types.StringNull() - } else { - chassis_fpc_model.Name = - types.StringPointerValue(v_chassis_fpc.Name) - } - - chassis_fpc_List[i_chassis_fpc] = - chassis_fpc_model - - chassis_fpc_List[i_chassis_fpc] = - chassis_fpc_model - - // Build pic list - chassis_fpc_pic_List := make([]Chassis_Fpc_Pic_Model, len(v_chassis_fpc.Pic)) - - - for i_chassis_fpc_pic, v_chassis_fpc_pic := range v_chassis_fpc.Pic { - var chassis_fpc_pic_model Chassis_Fpc_Pic_Model - // leaf - if v_chassis_fpc_pic.Name == nil { - chassis_fpc_pic_model.Name = - types.StringNull() - } else { - chassis_fpc_pic_model.Name = - types.StringPointerValue(v_chassis_fpc_pic.Name) - } - - chassis_fpc_pic_List[i_chassis_fpc_pic] = - chassis_fpc_pic_model - - chassis_fpc_pic_List[i_chassis_fpc_pic] = - chassis_fpc_pic_model - - // Build port list - chassis_fpc_pic_port_List := make([]Chassis_Fpc_Pic_Port_Model, len(v_chassis_fpc_pic.Port)) - - - for i_chassis_fpc_pic_port, v_chassis_fpc_pic_port := range v_chassis_fpc_pic.Port { - var chassis_fpc_pic_port_model Chassis_Fpc_Pic_Port_Model - // leaf - if v_chassis_fpc_pic_port.Name == nil { - chassis_fpc_pic_port_model.Name = - types.StringNull() - } else { - chassis_fpc_pic_port_model.Name = - types.StringPointerValue(v_chassis_fpc_pic_port.Name) - } - - chassis_fpc_pic_port_List[i_chassis_fpc_pic_port] = - chassis_fpc_pic_port_model - // leaf - if v_chassis_fpc_pic_port.Speed == nil { - chassis_fpc_pic_port_model.Speed = - types.StringNull() - } else { - chassis_fpc_pic_port_model.Speed = - types.StringPointerValue(v_chassis_fpc_pic_port.Speed) - } - - chassis_fpc_pic_port_List[i_chassis_fpc_pic_port] = - chassis_fpc_pic_port_model - // leaf - if v_chassis_fpc_pic_port.Channel_speed == nil { - chassis_fpc_pic_port_model.Channel_speed = - types.StringNull() - } else { - chassis_fpc_pic_port_model.Channel_speed = - types.StringPointerValue(v_chassis_fpc_pic_port.Channel_speed) - } - - chassis_fpc_pic_port_List[i_chassis_fpc_pic_port] = - chassis_fpc_pic_port_model - } - - // Write port field as Null when empty, else concrete list - if len(chassis_fpc_pic_port_List) == 0 { - chassis_fpc_pic_model.Port = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Port_Model{}.AttrTypes()}) - } else { - chassis_fpc_pic_model.Port, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Port_Model{}.AttrTypes()}, - chassis_fpc_pic_port_List, - ) - } - chassis_fpc_pic_List[i_chassis_fpc_pic] = chassis_fpc_pic_model - } - - // Write pic field as Null when empty, else concrete list - if len(chassis_fpc_pic_List) == 0 { - chassis_fpc_model.Pic = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Model{}.AttrTypes()}) - } else { - chassis_fpc_model.Pic, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Fpc_Pic_Model{}.AttrTypes()}, - chassis_fpc_pic_List, - ) - } - chassis_fpc_List[i_chassis_fpc] = chassis_fpc_model - } - - // Write fpc field as Null when empty, else concrete list - if len(chassis_fpc_List) == 0 { - chassis_model.Fpc = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Fpc_Model{}.AttrTypes()}) - } else { - chassis_model.Fpc, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Fpc_Model{}.AttrTypes()}, - chassis_fpc_List, - ) - } - chassis_List[i_chassis] = chassis_model - - chassis_List[i_chassis] = chassis_model - } - - // Write parent list as Null when empty - if len(chassis_List) == 0 { - state.Chassis = - types.ListNull(types.ObjectType{AttrTypes: Chassis_Model{}.AttrTypes()}) - } else { - state.Chassis, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Chassis_Model{}.AttrTypes()}, - chassis_List, - ) - } - // Initialize firewall as Null; only materialize when we have elements - state.Firewall = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Model{}.AttrTypes()}) - - // Build list from device - firewall_List := make([]Firewall_Model, - len(config.Firewall)) - for i_firewall, v_firewall := range config.Firewall { - var firewall_model Firewall_Model - - // Build family list - firewall_family_List := make([]Firewall_Family_Model, len(v_firewall.Family)) - - - for i_firewall_family, v_firewall_family := range v_firewall.Family { - var firewall_family_model Firewall_Family_Model - - firewall_family_List[i_firewall_family] = - firewall_family_model - - // Build inet list - firewall_family_inet_List := make([]Firewall_Family_Inet_Model, len(v_firewall_family.Inet)) - - - for i_firewall_family_inet, v_firewall_family_inet := range v_firewall_family.Inet { - var firewall_family_inet_model Firewall_Family_Inet_Model - - firewall_family_inet_List[i_firewall_family_inet] = - firewall_family_inet_model - - // Build filter list - firewall_family_inet_filter_List := make([]Firewall_Family_Inet_Filter_Model, len(v_firewall_family_inet.Filter)) - - - for i_firewall_family_inet_filter, v_firewall_family_inet_filter := range v_firewall_family_inet.Filter { - var firewall_family_inet_filter_model Firewall_Family_Inet_Filter_Model - // leaf - if v_firewall_family_inet_filter.Name == nil { - firewall_family_inet_filter_model.Name = - types.StringNull() - } else { - firewall_family_inet_filter_model.Name = - types.StringPointerValue(v_firewall_family_inet_filter.Name) - } - - firewall_family_inet_filter_List[i_firewall_family_inet_filter] = - firewall_family_inet_filter_model - - firewall_family_inet_filter_List[i_firewall_family_inet_filter] = - firewall_family_inet_filter_model - - // Build term list - firewall_family_inet_filter_term_List := make([]Firewall_Family_Inet_Filter_Term_Model, len(v_firewall_family_inet_filter.Term)) - - - for i_firewall_family_inet_filter_term, v_firewall_family_inet_filter_term := range v_firewall_family_inet_filter.Term { - var firewall_family_inet_filter_term_model Firewall_Family_Inet_Filter_Term_Model - // leaf - if v_firewall_family_inet_filter_term.Name == nil { - firewall_family_inet_filter_term_model.Name = - types.StringNull() - } else { - firewall_family_inet_filter_term_model.Name = - types.StringPointerValue(v_firewall_family_inet_filter_term.Name) - } - - firewall_family_inet_filter_term_List[i_firewall_family_inet_filter_term] = - firewall_family_inet_filter_term_model - - firewall_family_inet_filter_term_List[i_firewall_family_inet_filter_term] = - firewall_family_inet_filter_term_model - - // Build from list - firewall_family_inet_filter_term_from_List := make([]Firewall_Family_Inet_Filter_Term_From_Model, len(v_firewall_family_inet_filter_term.From)) - - - for i_firewall_family_inet_filter_term_from, v_firewall_family_inet_filter_term_from := range v_firewall_family_inet_filter_term.From { - var firewall_family_inet_filter_term_from_model Firewall_Family_Inet_Filter_Term_From_Model - - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = - firewall_family_inet_filter_term_from_model - - // Build source-address list - firewall_family_inet_filter_term_from_source_address_List := make([]Firewall_Family_Inet_Filter_Term_From_Source_address_Model, len(v_firewall_family_inet_filter_term_from.Source_address)) - - - for i_firewall_family_inet_filter_term_from_source_address, v_firewall_family_inet_filter_term_from_source_address := range v_firewall_family_inet_filter_term_from.Source_address { - var firewall_family_inet_filter_term_from_source_address_model Firewall_Family_Inet_Filter_Term_From_Source_address_Model - // leaf - if v_firewall_family_inet_filter_term_from_source_address.Name == nil { - firewall_family_inet_filter_term_from_source_address_model.Name = - types.StringNull() - } else { - firewall_family_inet_filter_term_from_source_address_model.Name = - types.StringPointerValue(v_firewall_family_inet_filter_term_from_source_address.Name) - } - - firewall_family_inet_filter_term_from_source_address_List[i_firewall_family_inet_filter_term_from_source_address] = - firewall_family_inet_filter_term_from_source_address_model - } - - // Write source-address field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_from_source_address_List) == 0 { - firewall_family_inet_filter_term_from_model.Source_address = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Source_address_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_term_from_model.Source_address, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Source_address_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_from_source_address_List, - ) - } - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = firewall_family_inet_filter_term_from_model - - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = - firewall_family_inet_filter_term_from_model - - // Build destination-address list - firewall_family_inet_filter_term_from_destination_address_List := make([]Firewall_Family_Inet_Filter_Term_From_Destination_address_Model, len(v_firewall_family_inet_filter_term_from.Destination_address)) - - - for i_firewall_family_inet_filter_term_from_destination_address, v_firewall_family_inet_filter_term_from_destination_address := range v_firewall_family_inet_filter_term_from.Destination_address { - var firewall_family_inet_filter_term_from_destination_address_model Firewall_Family_Inet_Filter_Term_From_Destination_address_Model - // leaf - if v_firewall_family_inet_filter_term_from_destination_address.Name == nil { - firewall_family_inet_filter_term_from_destination_address_model.Name = - types.StringNull() - } else { - firewall_family_inet_filter_term_from_destination_address_model.Name = - types.StringPointerValue(v_firewall_family_inet_filter_term_from_destination_address.Name) - } - - firewall_family_inet_filter_term_from_destination_address_List[i_firewall_family_inet_filter_term_from_destination_address] = - firewall_family_inet_filter_term_from_destination_address_model - } - - // Write destination-address field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_from_destination_address_List) == 0 { - firewall_family_inet_filter_term_from_model.Destination_address = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Destination_address_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_term_from_model.Destination_address, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Destination_address_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_from_destination_address_List, - ) - } - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = firewall_family_inet_filter_term_from_model - // leaf-list - if v_firewall_family_inet_filter_term_from.Protocol == nil || - len(v_firewall_family_inet_filter_term_from.Protocol) == 0 { - firewall_family_inet_filter_term_from_model.Protocol = - types.ListNull(types.StringType) - } else { - src_firewall_family_inet_filter_term_protocol := - v_firewall_family_inet_filter_term_from.Protocol - vals_firewall_family_inet_filter_term_protocol := make([]*string, len(src_firewall_family_inet_filter_term_protocol)) - copy(vals_firewall_family_inet_filter_term_protocol, src_firewall_family_inet_filter_term_protocol) - firewall_family_inet_filter_term_from_model.Protocol, _ = - types.ListValueFrom(ctx, types.StringType, vals_firewall_family_inet_filter_term_protocol) - } - - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = - firewall_family_inet_filter_term_from_model - // leaf-list - if v_firewall_family_inet_filter_term_from.Destination_port == nil || - len(v_firewall_family_inet_filter_term_from.Destination_port) == 0 { - firewall_family_inet_filter_term_from_model.Destination_port = - types.ListNull(types.StringType) - } else { - src_firewall_family_inet_filter_term_destination_port := - v_firewall_family_inet_filter_term_from.Destination_port - vals_firewall_family_inet_filter_term_destination_port := make([]*string, len(src_firewall_family_inet_filter_term_destination_port)) - copy(vals_firewall_family_inet_filter_term_destination_port, src_firewall_family_inet_filter_term_destination_port) - firewall_family_inet_filter_term_from_model.Destination_port, _ = - types.ListValueFrom(ctx, types.StringType, vals_firewall_family_inet_filter_term_destination_port) - } - - firewall_family_inet_filter_term_from_List[i_firewall_family_inet_filter_term_from] = - firewall_family_inet_filter_term_from_model - } - - // Write from field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_from_List) == 0 { - firewall_family_inet_filter_term_model.From = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_term_model.From, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_From_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_from_List, - ) - } - firewall_family_inet_filter_term_List[i_firewall_family_inet_filter_term] = firewall_family_inet_filter_term_model - - firewall_family_inet_filter_term_List[i_firewall_family_inet_filter_term] = - firewall_family_inet_filter_term_model - - // Build then list - firewall_family_inet_filter_term_then_List := make([]Firewall_Family_Inet_Filter_Term_Then_Model, len(v_firewall_family_inet_filter_term.Then)) - - - for i_firewall_family_inet_filter_term_then, v_firewall_family_inet_filter_term_then := range v_firewall_family_inet_filter_term.Then { - var firewall_family_inet_filter_term_then_model Firewall_Family_Inet_Filter_Term_Then_Model - // leaf - if v_firewall_family_inet_filter_term_then.Accept == nil { - firewall_family_inet_filter_term_then_model.Accept = - types.StringNull() - } else { - firewall_family_inet_filter_term_then_model.Accept = - types.StringPointerValue(v_firewall_family_inet_filter_term_then.Accept) - } - - firewall_family_inet_filter_term_then_List[i_firewall_family_inet_filter_term_then] = - firewall_family_inet_filter_term_then_model - - firewall_family_inet_filter_term_then_List[i_firewall_family_inet_filter_term_then] = - firewall_family_inet_filter_term_then_model - - // Build discard list - firewall_family_inet_filter_term_then_discard_List := make([]Firewall_Family_Inet_Filter_Term_Then_Discard_Model, len(v_firewall_family_inet_filter_term_then.Discard)) - - - - // Write discard field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_then_discard_List) == 0 { - firewall_family_inet_filter_term_then_model.Discard = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Discard_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_term_then_model.Discard, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Discard_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_then_discard_List, - ) - } - firewall_family_inet_filter_term_then_List[i_firewall_family_inet_filter_term_then] = firewall_family_inet_filter_term_then_model - } - - // Write then field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_then_List) == 0 { - firewall_family_inet_filter_term_model.Then = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_term_model.Then, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Then_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_then_List, - ) - } - firewall_family_inet_filter_term_List[i_firewall_family_inet_filter_term] = firewall_family_inet_filter_term_model - } - - // Write term field as Null when empty, else concrete list - if len(firewall_family_inet_filter_term_List) == 0 { - firewall_family_inet_filter_model.Term = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Model{}.AttrTypes()}) - } else { - firewall_family_inet_filter_model.Term, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Term_Model{}.AttrTypes()}, - firewall_family_inet_filter_term_List, - ) - } - firewall_family_inet_filter_List[i_firewall_family_inet_filter] = firewall_family_inet_filter_model - } - - // Write filter field as Null when empty, else concrete list - if len(firewall_family_inet_filter_List) == 0 { - firewall_family_inet_model.Filter = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Model{}.AttrTypes()}) - } else { - firewall_family_inet_model.Filter, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Filter_Model{}.AttrTypes()}, - firewall_family_inet_filter_List, - ) - } - firewall_family_inet_List[i_firewall_family_inet] = firewall_family_inet_model - } - - // Write inet field as Null when empty, else concrete list - if len(firewall_family_inet_List) == 0 { - firewall_family_model.Inet = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet_Model{}.AttrTypes()}) - } else { - firewall_family_model.Inet, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet_Model{}.AttrTypes()}, - firewall_family_inet_List, - ) - } - firewall_family_List[i_firewall_family] = firewall_family_model - - firewall_family_List[i_firewall_family] = - firewall_family_model - - // Build inet6 list - firewall_family_inet6_List := make([]Firewall_Family_Inet6_Model, len(v_firewall_family.Inet6)) - - - for i_firewall_family_inet6, v_firewall_family_inet6 := range v_firewall_family.Inet6 { - var firewall_family_inet6_model Firewall_Family_Inet6_Model - - firewall_family_inet6_List[i_firewall_family_inet6] = - firewall_family_inet6_model - - // Build filter list - firewall_family_inet6_filter_List := make([]Firewall_Family_Inet6_Filter_Model, len(v_firewall_family_inet6.Filter)) - - - for i_firewall_family_inet6_filter, v_firewall_family_inet6_filter := range v_firewall_family_inet6.Filter { - var firewall_family_inet6_filter_model Firewall_Family_Inet6_Filter_Model - // leaf - if v_firewall_family_inet6_filter.Name == nil { - firewall_family_inet6_filter_model.Name = - types.StringNull() - } else { - firewall_family_inet6_filter_model.Name = - types.StringPointerValue(v_firewall_family_inet6_filter.Name) - } - - firewall_family_inet6_filter_List[i_firewall_family_inet6_filter] = - firewall_family_inet6_filter_model - - firewall_family_inet6_filter_List[i_firewall_family_inet6_filter] = - firewall_family_inet6_filter_model - - // Build term list - firewall_family_inet6_filter_term_List := make([]Firewall_Family_Inet6_Filter_Term_Model, len(v_firewall_family_inet6_filter.Term)) - - - for i_firewall_family_inet6_filter_term, v_firewall_family_inet6_filter_term := range v_firewall_family_inet6_filter.Term { - var firewall_family_inet6_filter_term_model Firewall_Family_Inet6_Filter_Term_Model - // leaf - if v_firewall_family_inet6_filter_term.Name == nil { - firewall_family_inet6_filter_term_model.Name = - types.StringNull() - } else { - firewall_family_inet6_filter_term_model.Name = - types.StringPointerValue(v_firewall_family_inet6_filter_term.Name) - } - - firewall_family_inet6_filter_term_List[i_firewall_family_inet6_filter_term] = - firewall_family_inet6_filter_term_model - - firewall_family_inet6_filter_term_List[i_firewall_family_inet6_filter_term] = - firewall_family_inet6_filter_term_model - - // Build from list - firewall_family_inet6_filter_term_from_List := make([]Firewall_Family_Inet6_Filter_Term_From_Model, len(v_firewall_family_inet6_filter_term.From)) - - - for i_firewall_family_inet6_filter_term_from, v_firewall_family_inet6_filter_term_from := range v_firewall_family_inet6_filter_term.From { - var firewall_family_inet6_filter_term_from_model Firewall_Family_Inet6_Filter_Term_From_Model - // leaf-list - if v_firewall_family_inet6_filter_term_from.Next_header == nil || - len(v_firewall_family_inet6_filter_term_from.Next_header) == 0 { - firewall_family_inet6_filter_term_from_model.Next_header = - types.ListNull(types.StringType) - } else { - src_firewall_family_inet6_filter_term_next_header := - v_firewall_family_inet6_filter_term_from.Next_header - vals_firewall_family_inet6_filter_term_next_header := make([]*string, len(src_firewall_family_inet6_filter_term_next_header)) - copy(vals_firewall_family_inet6_filter_term_next_header, src_firewall_family_inet6_filter_term_next_header) - firewall_family_inet6_filter_term_from_model.Next_header, _ = - types.ListValueFrom(ctx, types.StringType, vals_firewall_family_inet6_filter_term_next_header) - } - - firewall_family_inet6_filter_term_from_List[i_firewall_family_inet6_filter_term_from] = - firewall_family_inet6_filter_term_from_model - // leaf-list - if v_firewall_family_inet6_filter_term_from.Destination_port == nil || - len(v_firewall_family_inet6_filter_term_from.Destination_port) == 0 { - firewall_family_inet6_filter_term_from_model.Destination_port = - types.ListNull(types.StringType) - } else { - src_firewall_family_inet6_filter_term_destination_port := - v_firewall_family_inet6_filter_term_from.Destination_port - vals_firewall_family_inet6_filter_term_destination_port := make([]*string, len(src_firewall_family_inet6_filter_term_destination_port)) - copy(vals_firewall_family_inet6_filter_term_destination_port, src_firewall_family_inet6_filter_term_destination_port) - firewall_family_inet6_filter_term_from_model.Destination_port, _ = - types.ListValueFrom(ctx, types.StringType, vals_firewall_family_inet6_filter_term_destination_port) - } - - firewall_family_inet6_filter_term_from_List[i_firewall_family_inet6_filter_term_from] = - firewall_family_inet6_filter_term_from_model - } - - // Write from field as Null when empty, else concrete list - if len(firewall_family_inet6_filter_term_from_List) == 0 { - firewall_family_inet6_filter_term_model.From = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_From_Model{}.AttrTypes()}) - } else { - firewall_family_inet6_filter_term_model.From, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_From_Model{}.AttrTypes()}, - firewall_family_inet6_filter_term_from_List, - ) - } - firewall_family_inet6_filter_term_List[i_firewall_family_inet6_filter_term] = firewall_family_inet6_filter_term_model - - firewall_family_inet6_filter_term_List[i_firewall_family_inet6_filter_term] = - firewall_family_inet6_filter_term_model - - // Build then list - firewall_family_inet6_filter_term_then_List := make([]Firewall_Family_Inet6_Filter_Term_Then_Model, len(v_firewall_family_inet6_filter_term.Then)) - - - for i_firewall_family_inet6_filter_term_then, v_firewall_family_inet6_filter_term_then := range v_firewall_family_inet6_filter_term.Then { - var firewall_family_inet6_filter_term_then_model Firewall_Family_Inet6_Filter_Term_Then_Model - // leaf - if v_firewall_family_inet6_filter_term_then.Accept == nil { - firewall_family_inet6_filter_term_then_model.Accept = - types.StringNull() - } else { - firewall_family_inet6_filter_term_then_model.Accept = - types.StringPointerValue(v_firewall_family_inet6_filter_term_then.Accept) - } - - firewall_family_inet6_filter_term_then_List[i_firewall_family_inet6_filter_term_then] = - firewall_family_inet6_filter_term_then_model - // leaf - if v_firewall_family_inet6_filter_term_then.Discard == nil { - firewall_family_inet6_filter_term_then_model.Discard = - types.StringNull() - } else { - firewall_family_inet6_filter_term_then_model.Discard = - types.StringPointerValue(v_firewall_family_inet6_filter_term_then.Discard) - } - - firewall_family_inet6_filter_term_then_List[i_firewall_family_inet6_filter_term_then] = - firewall_family_inet6_filter_term_then_model - } - - // Write then field as Null when empty, else concrete list - if len(firewall_family_inet6_filter_term_then_List) == 0 { - firewall_family_inet6_filter_term_model.Then = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Then_Model{}.AttrTypes()}) - } else { - firewall_family_inet6_filter_term_model.Then, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Then_Model{}.AttrTypes()}, - firewall_family_inet6_filter_term_then_List, - ) - } - firewall_family_inet6_filter_term_List[i_firewall_family_inet6_filter_term] = firewall_family_inet6_filter_term_model - } - - // Write term field as Null when empty, else concrete list - if len(firewall_family_inet6_filter_term_List) == 0 { - firewall_family_inet6_filter_model.Term = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Model{}.AttrTypes()}) - } else { - firewall_family_inet6_filter_model.Term, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Term_Model{}.AttrTypes()}, - firewall_family_inet6_filter_term_List, - ) - } - firewall_family_inet6_filter_List[i_firewall_family_inet6_filter] = firewall_family_inet6_filter_model - } - - // Write filter field as Null when empty, else concrete list - if len(firewall_family_inet6_filter_List) == 0 { - firewall_family_inet6_model.Filter = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Model{}.AttrTypes()}) - } else { - firewall_family_inet6_model.Filter, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet6_Filter_Model{}.AttrTypes()}, - firewall_family_inet6_filter_List, - ) - } - firewall_family_inet6_List[i_firewall_family_inet6] = firewall_family_inet6_model - } - - // Write inet6 field as Null when empty, else concrete list - if len(firewall_family_inet6_List) == 0 { - firewall_family_model.Inet6 = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Inet6_Model{}.AttrTypes()}) - } else { - firewall_family_model.Inet6, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Inet6_Model{}.AttrTypes()}, - firewall_family_inet6_List, - ) - } - firewall_family_List[i_firewall_family] = firewall_family_model - } - - // Write family field as Null when empty, else concrete list - if len(firewall_family_List) == 0 { - firewall_model.Family = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Family_Model{}.AttrTypes()}) - } else { - firewall_model.Family, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Family_Model{}.AttrTypes()}, - firewall_family_List, - ) - } - firewall_List[i_firewall] = firewall_model - - firewall_List[i_firewall] = firewall_model - } - - // Write parent list as Null when empty - if len(firewall_List) == 0 { - state.Firewall = - types.ListNull(types.ObjectType{AttrTypes: Firewall_Model{}.AttrTypes()}) - } else { - state.Firewall, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Firewall_Model{}.AttrTypes()}, - firewall_List, - ) - } - // Initialize forwarding-options as Null; only materialize when we have elements - state.Forwarding_options = - types.ListNull(types.ObjectType{AttrTypes: Forwarding_options_Model{}.AttrTypes()}) - - // Build list from device - forwarding_options_List := make([]Forwarding_options_Model, - len(config.Forwarding_options)) - for i_forwarding_options, v_forwarding_options := range config.Forwarding_options { - var forwarding_options_model Forwarding_options_Model - - // Build storm-control-profiles list - forwarding_options_storm_control_profiles_List := make([]Forwarding_options_Storm_control_profiles_Model, len(v_forwarding_options.Storm_control_profiles)) - - - for i_forwarding_options_storm_control_profiles, v_forwarding_options_storm_control_profiles := range v_forwarding_options.Storm_control_profiles { - var forwarding_options_storm_control_profiles_model Forwarding_options_Storm_control_profiles_Model - // leaf - if v_forwarding_options_storm_control_profiles.Name == nil { - forwarding_options_storm_control_profiles_model.Name = - types.StringNull() - } else { - forwarding_options_storm_control_profiles_model.Name = - types.StringPointerValue(v_forwarding_options_storm_control_profiles.Name) - } - - forwarding_options_storm_control_profiles_List[i_forwarding_options_storm_control_profiles] = - forwarding_options_storm_control_profiles_model - - forwarding_options_storm_control_profiles_List[i_forwarding_options_storm_control_profiles] = - forwarding_options_storm_control_profiles_model - - // Build all list - forwarding_options_storm_control_profiles_all_List := make([]Forwarding_options_Storm_control_profiles_All_Model, len(v_forwarding_options_storm_control_profiles.All)) - - - for i_forwarding_options_storm_control_profiles_all, v_forwarding_options_storm_control_profiles_all := range v_forwarding_options_storm_control_profiles.All { - var forwarding_options_storm_control_profiles_all_model Forwarding_options_Storm_control_profiles_All_Model - // leaf - if v_forwarding_options_storm_control_profiles_all.Bandwidth_level == nil { - forwarding_options_storm_control_profiles_all_model.Bandwidth_level = - types.StringNull() - } else { - forwarding_options_storm_control_profiles_all_model.Bandwidth_level = - types.StringPointerValue(v_forwarding_options_storm_control_profiles_all.Bandwidth_level) - } - - forwarding_options_storm_control_profiles_all_List[i_forwarding_options_storm_control_profiles_all] = - forwarding_options_storm_control_profiles_all_model - } - - // Write all field as Null when empty, else concrete list - if len(forwarding_options_storm_control_profiles_all_List) == 0 { - forwarding_options_storm_control_profiles_model.All = - types.ListNull(types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_All_Model{}.AttrTypes()}) - } else { - forwarding_options_storm_control_profiles_model.All, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_All_Model{}.AttrTypes()}, - forwarding_options_storm_control_profiles_all_List, - ) - } - forwarding_options_storm_control_profiles_List[i_forwarding_options_storm_control_profiles] = forwarding_options_storm_control_profiles_model - } - - // Write storm-control-profiles field as Null when empty, else concrete list - if len(forwarding_options_storm_control_profiles_List) == 0 { - forwarding_options_model.Storm_control_profiles = - types.ListNull(types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_Model{}.AttrTypes()}) - } else { - forwarding_options_model.Storm_control_profiles, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Forwarding_options_Storm_control_profiles_Model{}.AttrTypes()}, - forwarding_options_storm_control_profiles_List, - ) - } - forwarding_options_List[i_forwarding_options] = forwarding_options_model - - forwarding_options_List[i_forwarding_options] = forwarding_options_model - } - - // Write parent list as Null when empty - if len(forwarding_options_List) == 0 { - state.Forwarding_options = - types.ListNull(types.ObjectType{AttrTypes: Forwarding_options_Model{}.AttrTypes()}) - } else { - state.Forwarding_options, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Forwarding_options_Model{}.AttrTypes()}, - forwarding_options_List, - ) - } - // Initialize interfaces as Null; only materialize when we have elements - state.Interfaces = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Model{}.AttrTypes()}) - - // Build list from device - interfaces_List := make([]Interfaces_Model, - len(config.Interfaces)) - for i_interfaces, v_interfaces := range config.Interfaces { - var interfaces_model Interfaces_Model - - // Build interface list - interfaces_interface_List := make([]Interfaces_Interface_Model, len(v_interfaces.Interface)) - - - for i_interfaces_interface, v_interfaces_interface := range v_interfaces.Interface { - var interfaces_interface_model Interfaces_Interface_Model - // leaf - if v_interfaces_interface.Name == nil { - interfaces_interface_model.Name = - types.StringNull() - } else { - interfaces_interface_model.Name = - types.StringPointerValue(v_interfaces_interface.Name) - } - - interfaces_interface_List[i_interfaces_interface] = - interfaces_interface_model - // leaf - if v_interfaces_interface.Mtu == nil { - interfaces_interface_model.Mtu = - types.StringNull() - } else { - interfaces_interface_model.Mtu = - types.StringPointerValue(v_interfaces_interface.Mtu) - } - - interfaces_interface_List[i_interfaces_interface] = - interfaces_interface_model - - interfaces_interface_List[i_interfaces_interface] = - interfaces_interface_model - - // Build ether-options list - interfaces_interface_ether_options_List := make([]Interfaces_Interface_Ether_options_Model, len(v_interfaces_interface.Ether_options)) - - - for i_interfaces_interface_ether_options, v_interfaces_interface_ether_options := range v_interfaces_interface.Ether_options { - var interfaces_interface_ether_options_model Interfaces_Interface_Ether_options_Model - - interfaces_interface_ether_options_List[i_interfaces_interface_ether_options] = - interfaces_interface_ether_options_model - - // Build ieee-802.3ad list - interfaces_interface_ether_options_ieee_802_3ad_List := make([]Interfaces_Interface_Ether_options_Ieee_802_3ad_Model, len(v_interfaces_interface_ether_options.Ieee_802_3ad)) - - - for i_interfaces_interface_ether_options_ieee_802_3ad, v_interfaces_interface_ether_options_ieee_802_3ad := range v_interfaces_interface_ether_options.Ieee_802_3ad { - var interfaces_interface_ether_options_ieee_802_3ad_model Interfaces_Interface_Ether_options_Ieee_802_3ad_Model - // leaf - if v_interfaces_interface_ether_options_ieee_802_3ad.Bundle == nil { - interfaces_interface_ether_options_ieee_802_3ad_model.Bundle = - types.StringNull() - } else { - interfaces_interface_ether_options_ieee_802_3ad_model.Bundle = - types.StringPointerValue(v_interfaces_interface_ether_options_ieee_802_3ad.Bundle) - } - - interfaces_interface_ether_options_ieee_802_3ad_List[i_interfaces_interface_ether_options_ieee_802_3ad] = - interfaces_interface_ether_options_ieee_802_3ad_model - } - - // Write ieee-802.3ad field as Null when empty, else concrete list - if len(interfaces_interface_ether_options_ieee_802_3ad_List) == 0 { - interfaces_interface_ether_options_model.Ieee_802_3ad = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Ieee_802_3ad_Model{}.AttrTypes()}) - } else { - interfaces_interface_ether_options_model.Ieee_802_3ad, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Ieee_802_3ad_Model{}.AttrTypes()}, - interfaces_interface_ether_options_ieee_802_3ad_List, - ) - } - interfaces_interface_ether_options_List[i_interfaces_interface_ether_options] = interfaces_interface_ether_options_model - } - - // Write ether-options field as Null when empty, else concrete list - if len(interfaces_interface_ether_options_List) == 0 { - interfaces_interface_model.Ether_options = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Model{}.AttrTypes()}) - } else { - interfaces_interface_model.Ether_options, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Ether_options_Model{}.AttrTypes()}, - interfaces_interface_ether_options_List, - ) - } - interfaces_interface_List[i_interfaces_interface] = interfaces_interface_model - - interfaces_interface_List[i_interfaces_interface] = - interfaces_interface_model - - // Build aggregated-ether-options list - interfaces_interface_aggregated_ether_options_List := make([]Interfaces_Interface_Aggregated_ether_options_Model, len(v_interfaces_interface.Aggregated_ether_options)) - - - for i_interfaces_interface_aggregated_ether_options, v_interfaces_interface_aggregated_ether_options := range v_interfaces_interface.Aggregated_ether_options { - var interfaces_interface_aggregated_ether_options_model Interfaces_Interface_Aggregated_ether_options_Model - - interfaces_interface_aggregated_ether_options_List[i_interfaces_interface_aggregated_ether_options] = - interfaces_interface_aggregated_ether_options_model - - // Build lacp list - interfaces_interface_aggregated_ether_options_lacp_List := make([]Interfaces_Interface_Aggregated_ether_options_Lacp_Model, len(v_interfaces_interface_aggregated_ether_options.Lacp)) - - - for i_interfaces_interface_aggregated_ether_options_lacp, v_interfaces_interface_aggregated_ether_options_lacp := range v_interfaces_interface_aggregated_ether_options.Lacp { - var interfaces_interface_aggregated_ether_options_lacp_model Interfaces_Interface_Aggregated_ether_options_Lacp_Model - // leaf - if v_interfaces_interface_aggregated_ether_options_lacp.Active == nil { - interfaces_interface_aggregated_ether_options_lacp_model.Active = - types.StringNull() - } else { - interfaces_interface_aggregated_ether_options_lacp_model.Active = - types.StringPointerValue(v_interfaces_interface_aggregated_ether_options_lacp.Active) - } - - interfaces_interface_aggregated_ether_options_lacp_List[i_interfaces_interface_aggregated_ether_options_lacp] = - interfaces_interface_aggregated_ether_options_lacp_model - } - - // Write lacp field as Null when empty, else concrete list - if len(interfaces_interface_aggregated_ether_options_lacp_List) == 0 { - interfaces_interface_aggregated_ether_options_model.Lacp = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Lacp_Model{}.AttrTypes()}) - } else { - interfaces_interface_aggregated_ether_options_model.Lacp, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Lacp_Model{}.AttrTypes()}, - interfaces_interface_aggregated_ether_options_lacp_List, - ) - } - interfaces_interface_aggregated_ether_options_List[i_interfaces_interface_aggregated_ether_options] = interfaces_interface_aggregated_ether_options_model - } - - // Write aggregated-ether-options field as Null when empty, else concrete list - if len(interfaces_interface_aggregated_ether_options_List) == 0 { - interfaces_interface_model.Aggregated_ether_options = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Model{}.AttrTypes()}) - } else { - interfaces_interface_model.Aggregated_ether_options, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Aggregated_ether_options_Model{}.AttrTypes()}, - interfaces_interface_aggregated_ether_options_List, - ) - } - interfaces_interface_List[i_interfaces_interface] = interfaces_interface_model - - interfaces_interface_List[i_interfaces_interface] = - interfaces_interface_model - - // Build unit list - interfaces_interface_unit_List := make([]Interfaces_Interface_Unit_Model, len(v_interfaces_interface.Unit)) - - - for i_interfaces_interface_unit, v_interfaces_interface_unit := range v_interfaces_interface.Unit { - var interfaces_interface_unit_model Interfaces_Interface_Unit_Model - // leaf - if v_interfaces_interface_unit.Name == nil { - interfaces_interface_unit_model.Name = - types.StringNull() - } else { - interfaces_interface_unit_model.Name = - types.StringPointerValue(v_interfaces_interface_unit.Name) - } - - interfaces_interface_unit_List[i_interfaces_interface_unit] = - interfaces_interface_unit_model - - interfaces_interface_unit_List[i_interfaces_interface_unit] = - interfaces_interface_unit_model - - // Build family list - interfaces_interface_unit_family_List := make([]Interfaces_Interface_Unit_Family_Model, len(v_interfaces_interface_unit.Family)) - - - for i_interfaces_interface_unit_family, v_interfaces_interface_unit_family := range v_interfaces_interface_unit.Family { - var interfaces_interface_unit_family_model Interfaces_Interface_Unit_Family_Model - - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = - interfaces_interface_unit_family_model - - // Build inet list - interfaces_interface_unit_family_inet_List := make([]Interfaces_Interface_Unit_Family_Inet_Model, len(v_interfaces_interface_unit_family.Inet)) - - - for i_interfaces_interface_unit_family_inet, v_interfaces_interface_unit_family_inet := range v_interfaces_interface_unit_family.Inet { - var interfaces_interface_unit_family_inet_model Interfaces_Interface_Unit_Family_Inet_Model - - interfaces_interface_unit_family_inet_List[i_interfaces_interface_unit_family_inet] = - interfaces_interface_unit_family_inet_model - - // Build filter list - interfaces_interface_unit_family_inet_filter_List := make([]Interfaces_Interface_Unit_Family_Inet_Filter_Model, len(v_interfaces_interface_unit_family_inet.Filter)) - - - for i_interfaces_interface_unit_family_inet_filter, v_interfaces_interface_unit_family_inet_filter := range v_interfaces_interface_unit_family_inet.Filter { - var interfaces_interface_unit_family_inet_filter_model Interfaces_Interface_Unit_Family_Inet_Filter_Model - - interfaces_interface_unit_family_inet_filter_List[i_interfaces_interface_unit_family_inet_filter] = - interfaces_interface_unit_family_inet_filter_model - - // Build input list - interfaces_interface_unit_family_inet_filter_input_List := make([]Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model, len(v_interfaces_interface_unit_family_inet_filter.Input)) - - - for i_interfaces_interface_unit_family_inet_filter_input, v_interfaces_interface_unit_family_inet_filter_input := range v_interfaces_interface_unit_family_inet_filter.Input { - var interfaces_interface_unit_family_inet_filter_input_model Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model - // leaf - if v_interfaces_interface_unit_family_inet_filter_input.Filter_name == nil { - interfaces_interface_unit_family_inet_filter_input_model.Filter_name = - types.StringNull() - } else { - interfaces_interface_unit_family_inet_filter_input_model.Filter_name = - types.StringPointerValue(v_interfaces_interface_unit_family_inet_filter_input.Filter_name) - } - - interfaces_interface_unit_family_inet_filter_input_List[i_interfaces_interface_unit_family_inet_filter_input] = - interfaces_interface_unit_family_inet_filter_input_model - } - - // Write input field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet_filter_input_List) == 0 { - interfaces_interface_unit_family_inet_filter_model.Input = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet_filter_model.Input, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Input_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet_filter_input_List, - ) - } - interfaces_interface_unit_family_inet_filter_List[i_interfaces_interface_unit_family_inet_filter] = interfaces_interface_unit_family_inet_filter_model - } - - // Write filter field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet_filter_List) == 0 { - interfaces_interface_unit_family_inet_model.Filter = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet_model.Filter, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Filter_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet_filter_List, - ) - } - interfaces_interface_unit_family_inet_List[i_interfaces_interface_unit_family_inet] = interfaces_interface_unit_family_inet_model - - interfaces_interface_unit_family_inet_List[i_interfaces_interface_unit_family_inet] = - interfaces_interface_unit_family_inet_model - - // Build address list - interfaces_interface_unit_family_inet_address_List := make([]Interfaces_Interface_Unit_Family_Inet_Address_Model, len(v_interfaces_interface_unit_family_inet.Address)) - - - for i_interfaces_interface_unit_family_inet_address, v_interfaces_interface_unit_family_inet_address := range v_interfaces_interface_unit_family_inet.Address { - var interfaces_interface_unit_family_inet_address_model Interfaces_Interface_Unit_Family_Inet_Address_Model - // leaf - if v_interfaces_interface_unit_family_inet_address.Name == nil { - interfaces_interface_unit_family_inet_address_model.Name = - types.StringNull() - } else { - interfaces_interface_unit_family_inet_address_model.Name = - types.StringPointerValue(v_interfaces_interface_unit_family_inet_address.Name) - } - - interfaces_interface_unit_family_inet_address_List[i_interfaces_interface_unit_family_inet_address] = - interfaces_interface_unit_family_inet_address_model - } - - // Write address field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet_address_List) == 0 { - interfaces_interface_unit_family_inet_model.Address = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Address_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet_model.Address, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Address_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet_address_List, - ) - } - interfaces_interface_unit_family_inet_List[i_interfaces_interface_unit_family_inet] = interfaces_interface_unit_family_inet_model - } - - // Write inet field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet_List) == 0 { - interfaces_interface_unit_family_model.Inet = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_model.Inet, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet_List, - ) - } - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = interfaces_interface_unit_family_model - - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = - interfaces_interface_unit_family_model - - // Build inet6 list - interfaces_interface_unit_family_inet6_List := make([]Interfaces_Interface_Unit_Family_Inet6_Model, len(v_interfaces_interface_unit_family.Inet6)) - - - for i_interfaces_interface_unit_family_inet6, v_interfaces_interface_unit_family_inet6 := range v_interfaces_interface_unit_family.Inet6 { - var interfaces_interface_unit_family_inet6_model Interfaces_Interface_Unit_Family_Inet6_Model - - interfaces_interface_unit_family_inet6_List[i_interfaces_interface_unit_family_inet6] = - interfaces_interface_unit_family_inet6_model - - // Build filter list - interfaces_interface_unit_family_inet6_filter_List := make([]Interfaces_Interface_Unit_Family_Inet6_Filter_Model, len(v_interfaces_interface_unit_family_inet6.Filter)) - - - for i_interfaces_interface_unit_family_inet6_filter, v_interfaces_interface_unit_family_inet6_filter := range v_interfaces_interface_unit_family_inet6.Filter { - var interfaces_interface_unit_family_inet6_filter_model Interfaces_Interface_Unit_Family_Inet6_Filter_Model - - interfaces_interface_unit_family_inet6_filter_List[i_interfaces_interface_unit_family_inet6_filter] = - interfaces_interface_unit_family_inet6_filter_model - - // Build input list - interfaces_interface_unit_family_inet6_filter_input_List := make([]Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model, len(v_interfaces_interface_unit_family_inet6_filter.Input)) - - - for i_interfaces_interface_unit_family_inet6_filter_input, v_interfaces_interface_unit_family_inet6_filter_input := range v_interfaces_interface_unit_family_inet6_filter.Input { - var interfaces_interface_unit_family_inet6_filter_input_model Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model - // leaf - if v_interfaces_interface_unit_family_inet6_filter_input.Filter_name == nil { - interfaces_interface_unit_family_inet6_filter_input_model.Filter_name = - types.StringNull() - } else { - interfaces_interface_unit_family_inet6_filter_input_model.Filter_name = - types.StringPointerValue(v_interfaces_interface_unit_family_inet6_filter_input.Filter_name) - } - - interfaces_interface_unit_family_inet6_filter_input_List[i_interfaces_interface_unit_family_inet6_filter_input] = - interfaces_interface_unit_family_inet6_filter_input_model - } - - // Write input field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet6_filter_input_List) == 0 { - interfaces_interface_unit_family_inet6_filter_model.Input = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet6_filter_model.Input, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Input_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet6_filter_input_List, - ) - } - interfaces_interface_unit_family_inet6_filter_List[i_interfaces_interface_unit_family_inet6_filter] = interfaces_interface_unit_family_inet6_filter_model - } - - // Write filter field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet6_filter_List) == 0 { - interfaces_interface_unit_family_inet6_model.Filter = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet6_model.Filter, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Filter_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet6_filter_List, - ) - } - interfaces_interface_unit_family_inet6_List[i_interfaces_interface_unit_family_inet6] = interfaces_interface_unit_family_inet6_model - - interfaces_interface_unit_family_inet6_List[i_interfaces_interface_unit_family_inet6] = - interfaces_interface_unit_family_inet6_model - - // Build address list - interfaces_interface_unit_family_inet6_address_List := make([]Interfaces_Interface_Unit_Family_Inet6_Address_Model, len(v_interfaces_interface_unit_family_inet6.Address)) - - - for i_interfaces_interface_unit_family_inet6_address, v_interfaces_interface_unit_family_inet6_address := range v_interfaces_interface_unit_family_inet6.Address { - var interfaces_interface_unit_family_inet6_address_model Interfaces_Interface_Unit_Family_Inet6_Address_Model - // leaf - if v_interfaces_interface_unit_family_inet6_address.Name == nil { - interfaces_interface_unit_family_inet6_address_model.Name = - types.StringNull() - } else { - interfaces_interface_unit_family_inet6_address_model.Name = - types.StringPointerValue(v_interfaces_interface_unit_family_inet6_address.Name) - } - - interfaces_interface_unit_family_inet6_address_List[i_interfaces_interface_unit_family_inet6_address] = - interfaces_interface_unit_family_inet6_address_model - } - - // Write address field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet6_address_List) == 0 { - interfaces_interface_unit_family_inet6_model.Address = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Address_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_inet6_model.Address, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Address_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet6_address_List, - ) - } - interfaces_interface_unit_family_inet6_List[i_interfaces_interface_unit_family_inet6] = interfaces_interface_unit_family_inet6_model - } - - // Write inet6 field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_inet6_List) == 0 { - interfaces_interface_unit_family_model.Inet6 = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_model.Inet6, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Inet6_Model{}.AttrTypes()}, - interfaces_interface_unit_family_inet6_List, - ) - } - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = interfaces_interface_unit_family_model - - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = - interfaces_interface_unit_family_model - - // Build ethernet-switching list - interfaces_interface_unit_family_ethernet_switching_List := make([]Interfaces_Interface_Unit_Family_Ethernet_switching_Model, len(v_interfaces_interface_unit_family.Ethernet_switching)) - - - for i_interfaces_interface_unit_family_ethernet_switching, v_interfaces_interface_unit_family_ethernet_switching := range v_interfaces_interface_unit_family.Ethernet_switching { - var interfaces_interface_unit_family_ethernet_switching_model Interfaces_Interface_Unit_Family_Ethernet_switching_Model - // leaf - if v_interfaces_interface_unit_family_ethernet_switching.Interface_mode == nil { - interfaces_interface_unit_family_ethernet_switching_model.Interface_mode = - types.StringNull() - } else { - interfaces_interface_unit_family_ethernet_switching_model.Interface_mode = - types.StringPointerValue(v_interfaces_interface_unit_family_ethernet_switching.Interface_mode) - } - - interfaces_interface_unit_family_ethernet_switching_List[i_interfaces_interface_unit_family_ethernet_switching] = - interfaces_interface_unit_family_ethernet_switching_model - - interfaces_interface_unit_family_ethernet_switching_List[i_interfaces_interface_unit_family_ethernet_switching] = - interfaces_interface_unit_family_ethernet_switching_model - - // Build vlan list - interfaces_interface_unit_family_ethernet_switching_vlan_List := make([]Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model, len(v_interfaces_interface_unit_family_ethernet_switching.Vlan)) - - - for i_interfaces_interface_unit_family_ethernet_switching_vlan, v_interfaces_interface_unit_family_ethernet_switching_vlan := range v_interfaces_interface_unit_family_ethernet_switching.Vlan { - var interfaces_interface_unit_family_ethernet_switching_vlan_model Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model - // leaf-list - if v_interfaces_interface_unit_family_ethernet_switching_vlan.Members == nil || - len(v_interfaces_interface_unit_family_ethernet_switching_vlan.Members) == 0 { - interfaces_interface_unit_family_ethernet_switching_vlan_model.Members = - types.ListNull(types.StringType) - } else { - src_interfaces_interface_unit_family_ethernet_switching_members := - v_interfaces_interface_unit_family_ethernet_switching_vlan.Members - vals_interfaces_interface_unit_family_ethernet_switching_members := make([]*string, len(src_interfaces_interface_unit_family_ethernet_switching_members)) - copy(vals_interfaces_interface_unit_family_ethernet_switching_members, src_interfaces_interface_unit_family_ethernet_switching_members) - interfaces_interface_unit_family_ethernet_switching_vlan_model.Members, _ = - types.ListValueFrom(ctx, types.StringType, vals_interfaces_interface_unit_family_ethernet_switching_members) - } - - interfaces_interface_unit_family_ethernet_switching_vlan_List[i_interfaces_interface_unit_family_ethernet_switching_vlan] = - interfaces_interface_unit_family_ethernet_switching_vlan_model - } - - // Write vlan field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_ethernet_switching_vlan_List) == 0 { - interfaces_interface_unit_family_ethernet_switching_model.Vlan = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_ethernet_switching_model.Vlan, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Vlan_Model{}.AttrTypes()}, - interfaces_interface_unit_family_ethernet_switching_vlan_List, - ) - } - interfaces_interface_unit_family_ethernet_switching_List[i_interfaces_interface_unit_family_ethernet_switching] = interfaces_interface_unit_family_ethernet_switching_model - - interfaces_interface_unit_family_ethernet_switching_List[i_interfaces_interface_unit_family_ethernet_switching] = - interfaces_interface_unit_family_ethernet_switching_model - - // Build storm-control list - interfaces_interface_unit_family_ethernet_switching_storm_control_List := make([]Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model, len(v_interfaces_interface_unit_family_ethernet_switching.Storm_control)) - - - for i_interfaces_interface_unit_family_ethernet_switching_storm_control, v_interfaces_interface_unit_family_ethernet_switching_storm_control := range v_interfaces_interface_unit_family_ethernet_switching.Storm_control { - var interfaces_interface_unit_family_ethernet_switching_storm_control_model Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model - // leaf - if v_interfaces_interface_unit_family_ethernet_switching_storm_control.Profile_name == nil { - interfaces_interface_unit_family_ethernet_switching_storm_control_model.Profile_name = - types.StringNull() - } else { - interfaces_interface_unit_family_ethernet_switching_storm_control_model.Profile_name = - types.StringPointerValue(v_interfaces_interface_unit_family_ethernet_switching_storm_control.Profile_name) - } - - interfaces_interface_unit_family_ethernet_switching_storm_control_List[i_interfaces_interface_unit_family_ethernet_switching_storm_control] = - interfaces_interface_unit_family_ethernet_switching_storm_control_model - } - - // Write storm-control field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_ethernet_switching_storm_control_List) == 0 { - interfaces_interface_unit_family_ethernet_switching_model.Storm_control = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_ethernet_switching_model.Storm_control, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Storm_control_Model{}.AttrTypes()}, - interfaces_interface_unit_family_ethernet_switching_storm_control_List, - ) - } - interfaces_interface_unit_family_ethernet_switching_List[i_interfaces_interface_unit_family_ethernet_switching] = interfaces_interface_unit_family_ethernet_switching_model - } - - // Write ethernet-switching field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_ethernet_switching_List) == 0 { - interfaces_interface_unit_family_model.Ethernet_switching = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_family_model.Ethernet_switching, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Ethernet_switching_Model{}.AttrTypes()}, - interfaces_interface_unit_family_ethernet_switching_List, - ) - } - interfaces_interface_unit_family_List[i_interfaces_interface_unit_family] = interfaces_interface_unit_family_model - } - - // Write family field as Null when empty, else concrete list - if len(interfaces_interface_unit_family_List) == 0 { - interfaces_interface_unit_model.Family = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Model{}.AttrTypes()}) - } else { - interfaces_interface_unit_model.Family, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Family_Model{}.AttrTypes()}, - interfaces_interface_unit_family_List, - ) - } - interfaces_interface_unit_List[i_interfaces_interface_unit] = interfaces_interface_unit_model - } - - // Write unit field as Null when empty, else concrete list - if len(interfaces_interface_unit_List) == 0 { - interfaces_interface_model.Unit = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Model{}.AttrTypes()}) - } else { - interfaces_interface_model.Unit, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Unit_Model{}.AttrTypes()}, - interfaces_interface_unit_List, - ) - } - interfaces_interface_List[i_interfaces_interface] = interfaces_interface_model - } - - // Write interface field as Null when empty, else concrete list - if len(interfaces_interface_List) == 0 { - interfaces_model.Interface = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Interface_Model{}.AttrTypes()}) - } else { - interfaces_model.Interface, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Interface_Model{}.AttrTypes()}, - interfaces_interface_List, - ) - } - interfaces_List[i_interfaces] = interfaces_model - - interfaces_List[i_interfaces] = interfaces_model - } - - // Write parent list as Null when empty - if len(interfaces_List) == 0 { - state.Interfaces = - types.ListNull(types.ObjectType{AttrTypes: Interfaces_Model{}.AttrTypes()}) - } else { - state.Interfaces, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Interfaces_Model{}.AttrTypes()}, - interfaces_List, - ) - } - // Initialize policy-options as Null; only materialize when we have elements - state.Policy_options = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Model{}.AttrTypes()}) - - // Build list from device - policy_options_List := make([]Policy_options_Model, - len(config.Policy_options)) - for i_policy_options, v_policy_options := range config.Policy_options { - var policy_options_model Policy_options_Model - - // Build policy-statement list - policy_options_policy_statement_List := make([]Policy_options_Policy_statement_Model, len(v_policy_options.Policy_statement)) - - - for i_policy_options_policy_statement, v_policy_options_policy_statement := range v_policy_options.Policy_statement { - var policy_options_policy_statement_model Policy_options_Policy_statement_Model - // leaf - if v_policy_options_policy_statement.Name == nil { - policy_options_policy_statement_model.Name = - types.StringNull() - } else { - policy_options_policy_statement_model.Name = - types.StringPointerValue(v_policy_options_policy_statement.Name) - } - - policy_options_policy_statement_List[i_policy_options_policy_statement] = - policy_options_policy_statement_model - - policy_options_policy_statement_List[i_policy_options_policy_statement] = - policy_options_policy_statement_model - - // Build term list - policy_options_policy_statement_term_List := make([]Policy_options_Policy_statement_Term_Model, len(v_policy_options_policy_statement.Term)) - - - for i_policy_options_policy_statement_term, v_policy_options_policy_statement_term := range v_policy_options_policy_statement.Term { - var policy_options_policy_statement_term_model Policy_options_Policy_statement_Term_Model - // leaf - if v_policy_options_policy_statement_term.Name == nil { - policy_options_policy_statement_term_model.Name = - types.StringNull() - } else { - policy_options_policy_statement_term_model.Name = - types.StringPointerValue(v_policy_options_policy_statement_term.Name) - } - - policy_options_policy_statement_term_List[i_policy_options_policy_statement_term] = - policy_options_policy_statement_term_model - - policy_options_policy_statement_term_List[i_policy_options_policy_statement_term] = - policy_options_policy_statement_term_model - - // Build from list - policy_options_policy_statement_term_from_List := make([]Policy_options_Policy_statement_Term_From_Model, len(v_policy_options_policy_statement_term.From)) - - - for i_policy_options_policy_statement_term_from, v_policy_options_policy_statement_term_from := range v_policy_options_policy_statement_term.From { - var policy_options_policy_statement_term_from_model Policy_options_Policy_statement_Term_From_Model - - policy_options_policy_statement_term_from_List[i_policy_options_policy_statement_term_from] = - policy_options_policy_statement_term_from_model - - // Build route-filter list - policy_options_policy_statement_term_from_route_filter_List := make([]Policy_options_Policy_statement_Term_From_Route_filter_Model, len(v_policy_options_policy_statement_term_from.Route_filter)) - - - for i_policy_options_policy_statement_term_from_route_filter, v_policy_options_policy_statement_term_from_route_filter := range v_policy_options_policy_statement_term_from.Route_filter { - var policy_options_policy_statement_term_from_route_filter_model Policy_options_Policy_statement_Term_From_Route_filter_Model - // leaf - if v_policy_options_policy_statement_term_from_route_filter.Address == nil { - policy_options_policy_statement_term_from_route_filter_model.Address = - types.StringNull() - } else { - policy_options_policy_statement_term_from_route_filter_model.Address = - types.StringPointerValue(v_policy_options_policy_statement_term_from_route_filter.Address) - } - - policy_options_policy_statement_term_from_route_filter_List[i_policy_options_policy_statement_term_from_route_filter] = - policy_options_policy_statement_term_from_route_filter_model - // leaf - if v_policy_options_policy_statement_term_from_route_filter.Exact == nil { - policy_options_policy_statement_term_from_route_filter_model.Exact = - types.StringNull() - } else { - policy_options_policy_statement_term_from_route_filter_model.Exact = - types.StringPointerValue(v_policy_options_policy_statement_term_from_route_filter.Exact) - } - - policy_options_policy_statement_term_from_route_filter_List[i_policy_options_policy_statement_term_from_route_filter] = - policy_options_policy_statement_term_from_route_filter_model - } - - // Write route-filter field as Null when empty, else concrete list - if len(policy_options_policy_statement_term_from_route_filter_List) == 0 { - policy_options_policy_statement_term_from_model.Route_filter = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Route_filter_Model{}.AttrTypes()}) - } else { - policy_options_policy_statement_term_from_model.Route_filter, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Route_filter_Model{}.AttrTypes()}, - policy_options_policy_statement_term_from_route_filter_List, - ) - } - policy_options_policy_statement_term_from_List[i_policy_options_policy_statement_term_from] = policy_options_policy_statement_term_from_model - } - - // Write from field as Null when empty, else concrete list - if len(policy_options_policy_statement_term_from_List) == 0 { - policy_options_policy_statement_term_model.From = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Model{}.AttrTypes()}) - } else { - policy_options_policy_statement_term_model.From, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_From_Model{}.AttrTypes()}, - policy_options_policy_statement_term_from_List, - ) - } - policy_options_policy_statement_term_List[i_policy_options_policy_statement_term] = policy_options_policy_statement_term_model - - policy_options_policy_statement_term_List[i_policy_options_policy_statement_term] = - policy_options_policy_statement_term_model - - // Build then list - policy_options_policy_statement_term_then_List := make([]Policy_options_Policy_statement_Term_Then_Model, len(v_policy_options_policy_statement_term.Then)) - - - for i_policy_options_policy_statement_term_then, v_policy_options_policy_statement_term_then := range v_policy_options_policy_statement_term.Then { - var policy_options_policy_statement_term_then_model Policy_options_Policy_statement_Term_Then_Model - // leaf - if v_policy_options_policy_statement_term_then.Accept == nil { - policy_options_policy_statement_term_then_model.Accept = - types.StringNull() - } else { - policy_options_policy_statement_term_then_model.Accept = - types.StringPointerValue(v_policy_options_policy_statement_term_then.Accept) - } - - policy_options_policy_statement_term_then_List[i_policy_options_policy_statement_term_then] = - policy_options_policy_statement_term_then_model - // leaf - if v_policy_options_policy_statement_term_then.Reject == nil { - policy_options_policy_statement_term_then_model.Reject = - types.StringNull() - } else { - policy_options_policy_statement_term_then_model.Reject = - types.StringPointerValue(v_policy_options_policy_statement_term_then.Reject) - } - - policy_options_policy_statement_term_then_List[i_policy_options_policy_statement_term_then] = - policy_options_policy_statement_term_then_model - } - - // Write then field as Null when empty, else concrete list - if len(policy_options_policy_statement_term_then_List) == 0 { - policy_options_policy_statement_term_model.Then = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Then_Model{}.AttrTypes()}) - } else { - policy_options_policy_statement_term_model.Then, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Then_Model{}.AttrTypes()}, - policy_options_policy_statement_term_then_List, - ) - } - policy_options_policy_statement_term_List[i_policy_options_policy_statement_term] = policy_options_policy_statement_term_model - } - - // Write term field as Null when empty, else concrete list - if len(policy_options_policy_statement_term_List) == 0 { - policy_options_policy_statement_model.Term = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Model{}.AttrTypes()}) - } else { - policy_options_policy_statement_model.Term, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Policy_statement_Term_Model{}.AttrTypes()}, - policy_options_policy_statement_term_List, - ) - } - policy_options_policy_statement_List[i_policy_options_policy_statement] = policy_options_policy_statement_model - } - - // Write policy-statement field as Null when empty, else concrete list - if len(policy_options_policy_statement_List) == 0 { - policy_options_model.Policy_statement = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Policy_statement_Model{}.AttrTypes()}) - } else { - policy_options_model.Policy_statement, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Policy_statement_Model{}.AttrTypes()}, - policy_options_policy_statement_List, - ) - } - policy_options_List[i_policy_options] = policy_options_model - - policy_options_List[i_policy_options] = policy_options_model - } - - // Write parent list as Null when empty - if len(policy_options_List) == 0 { - state.Policy_options = - types.ListNull(types.ObjectType{AttrTypes: Policy_options_Model{}.AttrTypes()}) - } else { - state.Policy_options, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Policy_options_Model{}.AttrTypes()}, - policy_options_List, - ) - } - // Initialize protocols as Null; only materialize when we have elements - state.Protocols = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Model{}.AttrTypes()}) - - // Build list from device - protocols_List := make([]Protocols_Model, - len(config.Protocols)) - for i_protocols, v_protocols := range config.Protocols { - var protocols_model Protocols_Model - - // Build bgp list - protocols_bgp_List := make([]Protocols_Bgp_Model, len(v_protocols.Bgp)) - - - for i_protocols_bgp, v_protocols_bgp := range v_protocols.Bgp { - var protocols_bgp_model Protocols_Bgp_Model - - protocols_bgp_List[i_protocols_bgp] = - protocols_bgp_model - - // Build group list - protocols_bgp_group_List := make([]Protocols_Bgp_Group_Model, len(v_protocols_bgp.Group)) - - - for i_protocols_bgp_group, v_protocols_bgp_group := range v_protocols_bgp.Group { - var protocols_bgp_group_model Protocols_Bgp_Group_Model - // leaf - if v_protocols_bgp_group.Name == nil { - protocols_bgp_group_model.Name = - types.StringNull() - } else { - protocols_bgp_group_model.Name = - types.StringPointerValue(v_protocols_bgp_group.Name) - } - - protocols_bgp_group_List[i_protocols_bgp_group] = - protocols_bgp_group_model - // leaf - if v_protocols_bgp_group.Type == nil { - protocols_bgp_group_model.Type = - types.StringNull() - } else { - protocols_bgp_group_model.Type = - types.StringPointerValue(v_protocols_bgp_group.Type) - } - - protocols_bgp_group_List[i_protocols_bgp_group] = - protocols_bgp_group_model - // leaf - if v_protocols_bgp_group.Peer_as == nil { - protocols_bgp_group_model.Peer_as = - types.StringNull() - } else { - protocols_bgp_group_model.Peer_as = - types.StringPointerValue(v_protocols_bgp_group.Peer_as) - } - - protocols_bgp_group_List[i_protocols_bgp_group] = - protocols_bgp_group_model - - protocols_bgp_group_List[i_protocols_bgp_group] = - protocols_bgp_group_model - - // Build neighbor list - protocols_bgp_group_neighbor_List := make([]Protocols_Bgp_Group_Neighbor_Model, len(v_protocols_bgp_group.Neighbor)) - - - for i_protocols_bgp_group_neighbor, v_protocols_bgp_group_neighbor := range v_protocols_bgp_group.Neighbor { - var protocols_bgp_group_neighbor_model Protocols_Bgp_Group_Neighbor_Model - // leaf - if v_protocols_bgp_group_neighbor.Name == nil { - protocols_bgp_group_neighbor_model.Name = - types.StringNull() - } else { - protocols_bgp_group_neighbor_model.Name = - types.StringPointerValue(v_protocols_bgp_group_neighbor.Name) - } - - protocols_bgp_group_neighbor_List[i_protocols_bgp_group_neighbor] = - protocols_bgp_group_neighbor_model - // leaf-list - if v_protocols_bgp_group_neighbor.Import == nil || - len(v_protocols_bgp_group_neighbor.Import) == 0 { - protocols_bgp_group_neighbor_model.Import = - types.ListNull(types.StringType) - } else { - src_protocols_bgp_group_import := - v_protocols_bgp_group_neighbor.Import - vals_protocols_bgp_group_import := make([]*string, len(src_protocols_bgp_group_import)) - copy(vals_protocols_bgp_group_import, src_protocols_bgp_group_import) - protocols_bgp_group_neighbor_model.Import, _ = - types.ListValueFrom(ctx, types.StringType, vals_protocols_bgp_group_import) - } - - protocols_bgp_group_neighbor_List[i_protocols_bgp_group_neighbor] = - protocols_bgp_group_neighbor_model - - protocols_bgp_group_neighbor_List[i_protocols_bgp_group_neighbor] = - protocols_bgp_group_neighbor_model - - // Build family list - protocols_bgp_group_neighbor_family_List := make([]Protocols_Bgp_Group_Neighbor_Family_Model, len(v_protocols_bgp_group_neighbor.Family)) - - - for i_protocols_bgp_group_neighbor_family, v_protocols_bgp_group_neighbor_family := range v_protocols_bgp_group_neighbor.Family { - var protocols_bgp_group_neighbor_family_model Protocols_Bgp_Group_Neighbor_Family_Model - - protocols_bgp_group_neighbor_family_List[i_protocols_bgp_group_neighbor_family] = - protocols_bgp_group_neighbor_family_model - - // Build inet list - protocols_bgp_group_neighbor_family_inet_List := make([]Protocols_Bgp_Group_Neighbor_Family_Inet_Model, len(v_protocols_bgp_group_neighbor_family.Inet)) - - - for i_protocols_bgp_group_neighbor_family_inet, v_protocols_bgp_group_neighbor_family_inet := range v_protocols_bgp_group_neighbor_family.Inet { - var protocols_bgp_group_neighbor_family_inet_model Protocols_Bgp_Group_Neighbor_Family_Inet_Model - - protocols_bgp_group_neighbor_family_inet_List[i_protocols_bgp_group_neighbor_family_inet] = - protocols_bgp_group_neighbor_family_inet_model - - // Build unicast list - protocols_bgp_group_neighbor_family_inet_unicast_List := make([]Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model, len(v_protocols_bgp_group_neighbor_family_inet.Unicast)) - - - - // Write unicast field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_family_inet_unicast_List) == 0 { - protocols_bgp_group_neighbor_family_inet_model.Unicast = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_neighbor_family_inet_model.Unicast, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Unicast_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_family_inet_unicast_List, - ) - } - protocols_bgp_group_neighbor_family_inet_List[i_protocols_bgp_group_neighbor_family_inet] = protocols_bgp_group_neighbor_family_inet_model - } - - // Write inet field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_family_inet_List) == 0 { - protocols_bgp_group_neighbor_family_model.Inet = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_neighbor_family_model.Inet, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_family_inet_List, - ) - } - protocols_bgp_group_neighbor_family_List[i_protocols_bgp_group_neighbor_family] = protocols_bgp_group_neighbor_family_model - - protocols_bgp_group_neighbor_family_List[i_protocols_bgp_group_neighbor_family] = - protocols_bgp_group_neighbor_family_model - - // Build inet6 list - protocols_bgp_group_neighbor_family_inet6_List := make([]Protocols_Bgp_Group_Neighbor_Family_Inet6_Model, len(v_protocols_bgp_group_neighbor_family.Inet6)) - - - for i_protocols_bgp_group_neighbor_family_inet6, v_protocols_bgp_group_neighbor_family_inet6 := range v_protocols_bgp_group_neighbor_family.Inet6 { - var protocols_bgp_group_neighbor_family_inet6_model Protocols_Bgp_Group_Neighbor_Family_Inet6_Model - - protocols_bgp_group_neighbor_family_inet6_List[i_protocols_bgp_group_neighbor_family_inet6] = - protocols_bgp_group_neighbor_family_inet6_model - - // Build unicast list - protocols_bgp_group_neighbor_family_inet6_unicast_List := make([]Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model, len(v_protocols_bgp_group_neighbor_family_inet6.Unicast)) - - - - // Write unicast field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_family_inet6_unicast_List) == 0 { - protocols_bgp_group_neighbor_family_inet6_model.Unicast = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_neighbor_family_inet6_model.Unicast, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Unicast_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_family_inet6_unicast_List, - ) - } - protocols_bgp_group_neighbor_family_inet6_List[i_protocols_bgp_group_neighbor_family_inet6] = protocols_bgp_group_neighbor_family_inet6_model - } - - // Write inet6 field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_family_inet6_List) == 0 { - protocols_bgp_group_neighbor_family_model.Inet6 = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_neighbor_family_model.Inet6, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Inet6_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_family_inet6_List, - ) - } - protocols_bgp_group_neighbor_family_List[i_protocols_bgp_group_neighbor_family] = protocols_bgp_group_neighbor_family_model - } - - // Write family field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_family_List) == 0 { - protocols_bgp_group_neighbor_model.Family = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_neighbor_model.Family, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Family_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_family_List, - ) - } - protocols_bgp_group_neighbor_List[i_protocols_bgp_group_neighbor] = protocols_bgp_group_neighbor_model - // leaf-list - if v_protocols_bgp_group_neighbor.Export == nil || - len(v_protocols_bgp_group_neighbor.Export) == 0 { - protocols_bgp_group_neighbor_model.Export = - types.ListNull(types.StringType) - } else { - src_protocols_bgp_group_export := - v_protocols_bgp_group_neighbor.Export - vals_protocols_bgp_group_export := make([]*string, len(src_protocols_bgp_group_export)) - copy(vals_protocols_bgp_group_export, src_protocols_bgp_group_export) - protocols_bgp_group_neighbor_model.Export, _ = - types.ListValueFrom(ctx, types.StringType, vals_protocols_bgp_group_export) - } - - protocols_bgp_group_neighbor_List[i_protocols_bgp_group_neighbor] = - protocols_bgp_group_neighbor_model - } - - // Write neighbor field as Null when empty, else concrete list - if len(protocols_bgp_group_neighbor_List) == 0 { - protocols_bgp_group_model.Neighbor = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Model{}.AttrTypes()}) - } else { - protocols_bgp_group_model.Neighbor, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Neighbor_Model{}.AttrTypes()}, - protocols_bgp_group_neighbor_List, - ) - } - protocols_bgp_group_List[i_protocols_bgp_group] = protocols_bgp_group_model - } - - // Write group field as Null when empty, else concrete list - if len(protocols_bgp_group_List) == 0 { - protocols_bgp_model.Group = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Group_Model{}.AttrTypes()}) - } else { - protocols_bgp_model.Group, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Group_Model{}.AttrTypes()}, - protocols_bgp_group_List, - ) - } - protocols_bgp_List[i_protocols_bgp] = protocols_bgp_model - } - - // Write bgp field as Null when empty, else concrete list - if len(protocols_bgp_List) == 0 { - protocols_model.Bgp = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Bgp_Model{}.AttrTypes()}) - } else { - protocols_model.Bgp, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Bgp_Model{}.AttrTypes()}, - protocols_bgp_List, - ) - } - protocols_List[i_protocols] = protocols_model - - // Build lldp list - protocols_lldp_List := make([]Protocols_Lldp_Model, len(v_protocols.Lldp)) - - - for i_protocols_lldp, v_protocols_lldp := range v_protocols.Lldp { - var protocols_lldp_model Protocols_Lldp_Model - - protocols_lldp_List[i_protocols_lldp] = - protocols_lldp_model - - // Build interface list - protocols_lldp_interface_List := make([]Protocols_Lldp_Interface_Model, len(v_protocols_lldp.Interface)) - - - for i_protocols_lldp_interface, v_protocols_lldp_interface := range v_protocols_lldp.Interface { - var protocols_lldp_interface_model Protocols_Lldp_Interface_Model - // leaf - if v_protocols_lldp_interface.Name == nil { - protocols_lldp_interface_model.Name = - types.StringNull() - } else { - protocols_lldp_interface_model.Name = - types.StringPointerValue(v_protocols_lldp_interface.Name) - } - - protocols_lldp_interface_List[i_protocols_lldp_interface] = - protocols_lldp_interface_model - } - - // Write interface field as Null when empty, else concrete list - if len(protocols_lldp_interface_List) == 0 { - protocols_lldp_model.Interface = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Lldp_Interface_Model{}.AttrTypes()}) - } else { - protocols_lldp_model.Interface, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Lldp_Interface_Model{}.AttrTypes()}, - protocols_lldp_interface_List, - ) - } - protocols_lldp_List[i_protocols_lldp] = protocols_lldp_model - } - - // Write lldp field as Null when empty, else concrete list - if len(protocols_lldp_List) == 0 { - protocols_model.Lldp = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Lldp_Model{}.AttrTypes()}) - } else { - protocols_model.Lldp, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Lldp_Model{}.AttrTypes()}, - protocols_lldp_List, - ) - } - protocols_List[i_protocols] = protocols_model - - protocols_List[i_protocols] = protocols_model - } - - // Write parent list as Null when empty - if len(protocols_List) == 0 { - state.Protocols = - types.ListNull(types.ObjectType{AttrTypes: Protocols_Model{}.AttrTypes()}) - } else { - state.Protocols, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Protocols_Model{}.AttrTypes()}, - protocols_List, - ) - } - // Initialize routing-options as Null; only materialize when we have elements - state.Routing_options = - types.ListNull(types.ObjectType{AttrTypes: Routing_options_Model{}.AttrTypes()}) - - // Build list from device - routing_options_List := make([]Routing_options_Model, - len(config.Routing_options)) - for i_routing_options, v_routing_options := range config.Routing_options { - var routing_options_model Routing_options_Model - - // Build autonomous-system list - routing_options_autonomous_system_List := make([]Routing_options_Autonomous_system_Model, len(v_routing_options.Autonomous_system)) - - - for i_routing_options_autonomous_system, v_routing_options_autonomous_system := range v_routing_options.Autonomous_system { - var routing_options_autonomous_system_model Routing_options_Autonomous_system_Model - // leaf - if v_routing_options_autonomous_system.As_number == nil { - routing_options_autonomous_system_model.As_number = - types.StringNull() - } else { - routing_options_autonomous_system_model.As_number = - types.StringPointerValue(v_routing_options_autonomous_system.As_number) - } - - routing_options_autonomous_system_List[i_routing_options_autonomous_system] = - routing_options_autonomous_system_model - } - - // Write autonomous-system field as Null when empty, else concrete list - if len(routing_options_autonomous_system_List) == 0 { - routing_options_model.Autonomous_system = - types.ListNull(types.ObjectType{AttrTypes: Routing_options_Autonomous_system_Model{}.AttrTypes()}) - } else { - routing_options_model.Autonomous_system, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Routing_options_Autonomous_system_Model{}.AttrTypes()}, - routing_options_autonomous_system_List, - ) - } - routing_options_List[i_routing_options] = routing_options_model - - // Build static list - routing_options_static_List := make([]Routing_options_Static_Model, len(v_routing_options.Static)) - - - for i_routing_options_static, v_routing_options_static := range v_routing_options.Static { - var routing_options_static_model Routing_options_Static_Model - - routing_options_static_List[i_routing_options_static] = - routing_options_static_model - - // Build route list - routing_options_static_route_List := make([]Routing_options_Static_Route_Model, len(v_routing_options_static.Route)) - - - for i_routing_options_static_route, v_routing_options_static_route := range v_routing_options_static.Route { - var routing_options_static_route_model Routing_options_Static_Route_Model - // leaf - if v_routing_options_static_route.Name == nil { - routing_options_static_route_model.Name = - types.StringNull() - } else { - routing_options_static_route_model.Name = - types.StringPointerValue(v_routing_options_static_route.Name) - } - - routing_options_static_route_List[i_routing_options_static_route] = - routing_options_static_route_model - // leaf - if v_routing_options_static_route.Discard == nil { - routing_options_static_route_model.Discard = - types.StringNull() - } else { - routing_options_static_route_model.Discard = - types.StringPointerValue(v_routing_options_static_route.Discard) - } - - routing_options_static_route_List[i_routing_options_static_route] = - routing_options_static_route_model - } - - // Write route field as Null when empty, else concrete list - if len(routing_options_static_route_List) == 0 { - routing_options_static_model.Route = - types.ListNull(types.ObjectType{AttrTypes: Routing_options_Static_Route_Model{}.AttrTypes()}) - } else { - routing_options_static_model.Route, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Routing_options_Static_Route_Model{}.AttrTypes()}, - routing_options_static_route_List, - ) - } - routing_options_static_List[i_routing_options_static] = routing_options_static_model - } - - // Write static field as Null when empty, else concrete list - if len(routing_options_static_List) == 0 { - routing_options_model.Static = - types.ListNull(types.ObjectType{AttrTypes: Routing_options_Static_Model{}.AttrTypes()}) - } else { - routing_options_model.Static, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Routing_options_Static_Model{}.AttrTypes()}, - routing_options_static_List, - ) - } - routing_options_List[i_routing_options] = routing_options_model - - routing_options_List[i_routing_options] = routing_options_model - } - - // Write parent list as Null when empty - if len(routing_options_List) == 0 { - state.Routing_options = - types.ListNull(types.ObjectType{AttrTypes: Routing_options_Model{}.AttrTypes()}) - } else { - state.Routing_options, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Routing_options_Model{}.AttrTypes()}, - routing_options_List, - ) - } - // Initialize system as Null; only materialize when we have elements - state.System = - types.ListNull(types.ObjectType{AttrTypes: System_Model{}.AttrTypes()}) - - // Build list from device - system_List := make([]System_Model, - len(config.System)) - for i_system, v_system := range config.System { - var system_model System_Model - if v_system.Host_name == nil { - system_model.Host_name = types.StringNull() - } else { - system_model.Host_name = - types.StringPointerValue(v_system.Host_name) - } - - // Build root-authentication list - system_root_authentication_List := make([]System_Root_authentication_Model, len(v_system.Root_authentication)) - - - for i_system_root_authentication, v_system_root_authentication := range v_system.Root_authentication { - var system_root_authentication_model System_Root_authentication_Model - // leaf - if v_system_root_authentication.Encrypted_password == nil { - system_root_authentication_model.Encrypted_password = - types.StringNull() - } else { - system_root_authentication_model.Encrypted_password = - types.StringPointerValue(v_system_root_authentication.Encrypted_password) - } - - system_root_authentication_List[i_system_root_authentication] = - system_root_authentication_model - } - - // Write root-authentication field as Null when empty, else concrete list - if len(system_root_authentication_List) == 0 { - system_model.Root_authentication = - types.ListNull(types.ObjectType{AttrTypes: System_Root_authentication_Model{}.AttrTypes()}) - } else { - system_model.Root_authentication, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Root_authentication_Model{}.AttrTypes()}, - system_root_authentication_List, - ) - } - system_List[i_system] = system_model - - // Build login list - system_login_List := make([]System_Login_Model, len(v_system.Login)) - - - for i_system_login, v_system_login := range v_system.Login { - var system_login_model System_Login_Model - - system_login_List[i_system_login] = - system_login_model - - // Build user list - system_login_user_List := make([]System_Login_User_Model, len(v_system_login.User)) - - - for i_system_login_user, v_system_login_user := range v_system_login.User { - var system_login_user_model System_Login_User_Model - // leaf - if v_system_login_user.Name == nil { - system_login_user_model.Name = - types.StringNull() - } else { - system_login_user_model.Name = - types.StringPointerValue(v_system_login_user.Name) - } - - system_login_user_List[i_system_login_user] = - system_login_user_model - // leaf - if v_system_login_user.Uid == nil { - system_login_user_model.Uid = - types.StringNull() - } else { - system_login_user_model.Uid = - types.StringPointerValue(v_system_login_user.Uid) - } - - system_login_user_List[i_system_login_user] = - system_login_user_model - // leaf - if v_system_login_user.Class == nil { - system_login_user_model.Class = - types.StringNull() - } else { - system_login_user_model.Class = - types.StringPointerValue(v_system_login_user.Class) - } - - system_login_user_List[i_system_login_user] = - system_login_user_model - - system_login_user_List[i_system_login_user] = - system_login_user_model - - // Build authentication list - system_login_user_authentication_List := make([]System_Login_User_Authentication_Model, len(v_system_login_user.Authentication)) - - - for i_system_login_user_authentication, v_system_login_user_authentication := range v_system_login_user.Authentication { - var system_login_user_authentication_model System_Login_User_Authentication_Model - - system_login_user_authentication_List[i_system_login_user_authentication] = - system_login_user_authentication_model - - // Build ssh-ed25519 list - system_login_user_authentication_ssh_ed25519_List := make([]System_Login_User_Authentication_Ssh_ed25519_Model, len(v_system_login_user_authentication.Ssh_ed25519)) - - - for i_system_login_user_authentication_ssh_ed25519, v_system_login_user_authentication_ssh_ed25519 := range v_system_login_user_authentication.Ssh_ed25519 { - var system_login_user_authentication_ssh_ed25519_model System_Login_User_Authentication_Ssh_ed25519_Model - // leaf - if v_system_login_user_authentication_ssh_ed25519.Name == nil { - system_login_user_authentication_ssh_ed25519_model.Name = - types.StringNull() - } else { - system_login_user_authentication_ssh_ed25519_model.Name = - types.StringPointerValue(v_system_login_user_authentication_ssh_ed25519.Name) - } - - system_login_user_authentication_ssh_ed25519_List[i_system_login_user_authentication_ssh_ed25519] = - system_login_user_authentication_ssh_ed25519_model - } - - // Write ssh-ed25519 field as Null when empty, else concrete list - if len(system_login_user_authentication_ssh_ed25519_List) == 0 { - system_login_user_authentication_model.Ssh_ed25519 = - types.ListNull(types.ObjectType{AttrTypes: System_Login_User_Authentication_Ssh_ed25519_Model{}.AttrTypes()}) - } else { - system_login_user_authentication_model.Ssh_ed25519, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Login_User_Authentication_Ssh_ed25519_Model{}.AttrTypes()}, - system_login_user_authentication_ssh_ed25519_List, - ) - } - system_login_user_authentication_List[i_system_login_user_authentication] = system_login_user_authentication_model - } - - // Write authentication field as Null when empty, else concrete list - if len(system_login_user_authentication_List) == 0 { - system_login_user_model.Authentication = - types.ListNull(types.ObjectType{AttrTypes: System_Login_User_Authentication_Model{}.AttrTypes()}) - } else { - system_login_user_model.Authentication, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Login_User_Authentication_Model{}.AttrTypes()}, - system_login_user_authentication_List, - ) - } - system_login_user_List[i_system_login_user] = system_login_user_model - } - - // Write user field as Null when empty, else concrete list - if len(system_login_user_List) == 0 { - system_login_model.User = - types.ListNull(types.ObjectType{AttrTypes: System_Login_User_Model{}.AttrTypes()}) - } else { - system_login_model.User, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Login_User_Model{}.AttrTypes()}, - system_login_user_List, - ) - } - system_login_List[i_system_login] = system_login_model - } - - // Write login field as Null when empty, else concrete list - if len(system_login_List) == 0 { - system_model.Login = - types.ListNull(types.ObjectType{AttrTypes: System_Login_Model{}.AttrTypes()}) - } else { - system_model.Login, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Login_Model{}.AttrTypes()}, - system_login_List, - ) - } - system_List[i_system] = system_model - - // Build services list - system_services_List := make([]System_Services_Model, len(v_system.Services)) - - - for i_system_services, v_system_services := range v_system.Services { - var system_services_model System_Services_Model - - system_services_List[i_system_services] = - system_services_model - - // Build netconf list - system_services_netconf_List := make([]System_Services_Netconf_Model, len(v_system_services.Netconf)) - - - for i_system_services_netconf, v_system_services_netconf := range v_system_services.Netconf { - var system_services_netconf_model System_Services_Netconf_Model - - system_services_netconf_List[i_system_services_netconf] = - system_services_netconf_model - - // Build ssh list - system_services_netconf_ssh_List := make([]System_Services_Netconf_Ssh_Model, len(v_system_services_netconf.Ssh)) - - - - // Write ssh field as Null when empty, else concrete list - if len(system_services_netconf_ssh_List) == 0 { - system_services_netconf_model.Ssh = - types.ListNull(types.ObjectType{AttrTypes: System_Services_Netconf_Ssh_Model{}.AttrTypes()}) - } else { - system_services_netconf_model.Ssh, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Services_Netconf_Ssh_Model{}.AttrTypes()}, - system_services_netconf_ssh_List, - ) - } - system_services_netconf_List[i_system_services_netconf] = system_services_netconf_model - } - - // Write netconf field as Null when empty, else concrete list - if len(system_services_netconf_List) == 0 { - system_services_model.Netconf = - types.ListNull(types.ObjectType{AttrTypes: System_Services_Netconf_Model{}.AttrTypes()}) - } else { - system_services_model.Netconf, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Services_Netconf_Model{}.AttrTypes()}, - system_services_netconf_List, - ) - } - system_services_List[i_system_services] = system_services_model - - system_services_List[i_system_services] = - system_services_model - - // Build ssh list - system_services_ssh_List := make([]System_Services_Ssh_Model, len(v_system_services.Ssh)) - - - for i_system_services_ssh, v_system_services_ssh := range v_system_services.Ssh { - var system_services_ssh_model System_Services_Ssh_Model - // leaf - if v_system_services_ssh.Root_login == nil { - system_services_ssh_model.Root_login = - types.StringNull() - } else { - system_services_ssh_model.Root_login = - types.StringPointerValue(v_system_services_ssh.Root_login) - } - - system_services_ssh_List[i_system_services_ssh] = - system_services_ssh_model - } - - // Write ssh field as Null when empty, else concrete list - if len(system_services_ssh_List) == 0 { - system_services_model.Ssh = - types.ListNull(types.ObjectType{AttrTypes: System_Services_Ssh_Model{}.AttrTypes()}) - } else { - system_services_model.Ssh, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Services_Ssh_Model{}.AttrTypes()}, - system_services_ssh_List, - ) - } - system_services_List[i_system_services] = system_services_model - } - - // Write services field as Null when empty, else concrete list - if len(system_services_List) == 0 { - system_model.Services = - types.ListNull(types.ObjectType{AttrTypes: System_Services_Model{}.AttrTypes()}) - } else { - system_model.Services, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Services_Model{}.AttrTypes()}, - system_services_List, - ) - } - system_List[i_system] = system_model - if v_system.Time_zone == nil { - system_model.Time_zone = types.StringNull() - } else { - system_model.Time_zone = - types.StringPointerValue(v_system.Time_zone) - } - if v_system.Authentication_order == nil || - len(v_system.Authentication_order) == 0 { - system_model.Authentication_order = - types.ListNull(types.StringType) - } else { - src_system_authentication_order := - v_system.Authentication_order - vals_system_authentication_order := make([]*string, len(src_system_authentication_order)) - copy(vals_system_authentication_order, src_system_authentication_order) - system_model.Authentication_order, _ = - types.ListValueFrom(ctx, types.StringType, vals_system_authentication_order) - } - - // Build ports list - system_ports_List := make([]System_Ports_Model, len(v_system.Ports)) - - - for i_system_ports, v_system_ports := range v_system.Ports { - var system_ports_model System_Ports_Model - - system_ports_List[i_system_ports] = - system_ports_model - - // Build console list - system_ports_console_List := make([]System_Ports_Console_Model, len(v_system_ports.Console)) - - - for i_system_ports_console, v_system_ports_console := range v_system_ports.Console { - var system_ports_console_model System_Ports_Console_Model - // leaf - if v_system_ports_console.Log_out_on_disconnect == nil { - system_ports_console_model.Log_out_on_disconnect = - types.StringNull() - } else { - system_ports_console_model.Log_out_on_disconnect = - types.StringPointerValue(v_system_ports_console.Log_out_on_disconnect) - } - - system_ports_console_List[i_system_ports_console] = - system_ports_console_model - } - - // Write console field as Null when empty, else concrete list - if len(system_ports_console_List) == 0 { - system_ports_model.Console = - types.ListNull(types.ObjectType{AttrTypes: System_Ports_Console_Model{}.AttrTypes()}) - } else { - system_ports_model.Console, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Ports_Console_Model{}.AttrTypes()}, - system_ports_console_List, - ) - } - system_ports_List[i_system_ports] = system_ports_model - } - - // Write ports field as Null when empty, else concrete list - if len(system_ports_List) == 0 { - system_model.Ports = - types.ListNull(types.ObjectType{AttrTypes: System_Ports_Model{}.AttrTypes()}) - } else { - system_model.Ports, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Ports_Model{}.AttrTypes()}, - system_ports_List, - ) - } - system_List[i_system] = system_model - - // Build name-server list - system_name_server_List := make([]System_Name_server_Model, len(v_system.Name_server)) - - - for i_system_name_server, v_system_name_server := range v_system.Name_server { - var system_name_server_model System_Name_server_Model - // leaf - if v_system_name_server.Name == nil { - system_name_server_model.Name = - types.StringNull() - } else { - system_name_server_model.Name = - types.StringPointerValue(v_system_name_server.Name) - } - - system_name_server_List[i_system_name_server] = - system_name_server_model - } - - // Write name-server field as Null when empty, else concrete list - if len(system_name_server_List) == 0 { - system_model.Name_server = - types.ListNull(types.ObjectType{AttrTypes: System_Name_server_Model{}.AttrTypes()}) - } else { - system_model.Name_server, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Name_server_Model{}.AttrTypes()}, - system_name_server_List, - ) - } - system_List[i_system] = system_model - - // Build processes list - system_processes_List := make([]System_Processes_Model, len(v_system.Processes)) - - - for i_system_processes, v_system_processes := range v_system.Processes { - var system_processes_model System_Processes_Model - - system_processes_List[i_system_processes] = - system_processes_model - - // Build daemon-process list - system_processes_daemon_process_List := make([]System_Processes_Daemon_process_Model, len(v_system_processes.Daemon_process)) - - - for i_system_processes_daemon_process, v_system_processes_daemon_process := range v_system_processes.Daemon_process { - var system_processes_daemon_process_model System_Processes_Daemon_process_Model - // leaf - if v_system_processes_daemon_process.Name == nil { - system_processes_daemon_process_model.Name = - types.StringNull() - } else { - system_processes_daemon_process_model.Name = - types.StringPointerValue(v_system_processes_daemon_process.Name) - } - - system_processes_daemon_process_List[i_system_processes_daemon_process] = - system_processes_daemon_process_model - // leaf - if v_system_processes_daemon_process.Disable == nil { - system_processes_daemon_process_model.Disable = - types.StringNull() - } else { - system_processes_daemon_process_model.Disable = - types.StringPointerValue(v_system_processes_daemon_process.Disable) - } - - system_processes_daemon_process_List[i_system_processes_daemon_process] = - system_processes_daemon_process_model - } - - // Write daemon-process field as Null when empty, else concrete list - if len(system_processes_daemon_process_List) == 0 { - system_processes_model.Daemon_process = - types.ListNull(types.ObjectType{AttrTypes: System_Processes_Daemon_process_Model{}.AttrTypes()}) - } else { - system_processes_model.Daemon_process, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Processes_Daemon_process_Model{}.AttrTypes()}, - system_processes_daemon_process_List, - ) - } - system_processes_List[i_system_processes] = system_processes_model - } - - // Write processes field as Null when empty, else concrete list - if len(system_processes_List) == 0 { - system_model.Processes = - types.ListNull(types.ObjectType{AttrTypes: System_Processes_Model{}.AttrTypes()}) - } else { - system_model.Processes, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Processes_Model{}.AttrTypes()}, - system_processes_List, - ) - } - system_List[i_system] = system_model - - system_List[i_system] = system_model - } - - // Write parent list as Null when empty - if len(system_List) == 0 { - state.System = - types.ListNull(types.ObjectType{AttrTypes: System_Model{}.AttrTypes()}) - } else { - state.System, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: System_Model{}.AttrTypes()}, - system_List, - ) - } - // Initialize virtual-chassis as Null; only materialize when we have elements - state.Virtual_chassis = - types.ListNull(types.ObjectType{AttrTypes: Virtual_chassis_Model{}.AttrTypes()}) - - // Build list from device - virtual_chassis_List := make([]Virtual_chassis_Model, - len(config.Virtual_chassis)) - for i_virtual_chassis, v_virtual_chassis := range config.Virtual_chassis { - var virtual_chassis_model Virtual_chassis_Model - if v_virtual_chassis.Preprovisioned == nil { - virtual_chassis_model.Preprovisioned = types.StringNull() - } else { - virtual_chassis_model.Preprovisioned = - types.StringPointerValue(v_virtual_chassis.Preprovisioned) - } - - // Build member list - virtual_chassis_member_List := make([]Virtual_chassis_Member_Model, len(v_virtual_chassis.Member)) - - - for i_virtual_chassis_member, v_virtual_chassis_member := range v_virtual_chassis.Member { - var virtual_chassis_member_model Virtual_chassis_Member_Model - // leaf - if v_virtual_chassis_member.Name == nil { - virtual_chassis_member_model.Name = - types.StringNull() - } else { - virtual_chassis_member_model.Name = - types.StringPointerValue(v_virtual_chassis_member.Name) - } - - virtual_chassis_member_List[i_virtual_chassis_member] = - virtual_chassis_member_model - // leaf - if v_virtual_chassis_member.Role == nil { - virtual_chassis_member_model.Role = - types.StringNull() - } else { - virtual_chassis_member_model.Role = - types.StringPointerValue(v_virtual_chassis_member.Role) - } - - virtual_chassis_member_List[i_virtual_chassis_member] = - virtual_chassis_member_model - // leaf - if v_virtual_chassis_member.Serial_number == nil { - virtual_chassis_member_model.Serial_number = - types.StringNull() - } else { - virtual_chassis_member_model.Serial_number = - types.StringPointerValue(v_virtual_chassis_member.Serial_number) - } - - virtual_chassis_member_List[i_virtual_chassis_member] = - virtual_chassis_member_model - } - - // Write member field as Null when empty, else concrete list - if len(virtual_chassis_member_List) == 0 { - virtual_chassis_model.Member = - types.ListNull(types.ObjectType{AttrTypes: Virtual_chassis_Member_Model{}.AttrTypes()}) - } else { - virtual_chassis_model.Member, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Virtual_chassis_Member_Model{}.AttrTypes()}, - virtual_chassis_member_List, - ) - } - virtual_chassis_List[i_virtual_chassis] = virtual_chassis_model - - virtual_chassis_List[i_virtual_chassis] = virtual_chassis_model - } - - // Write parent list as Null when empty - if len(virtual_chassis_List) == 0 { - state.Virtual_chassis = - types.ListNull(types.ObjectType{AttrTypes: Virtual_chassis_Model{}.AttrTypes()}) - } else { - state.Virtual_chassis, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Virtual_chassis_Model{}.AttrTypes()}, - virtual_chassis_List, - ) - } - // Initialize vlans as Null; only materialize when we have elements - state.Vlans = - types.ListNull(types.ObjectType{AttrTypes: Vlans_Model{}.AttrTypes()}) - - // Build list from device - vlans_List := make([]Vlans_Model, - len(config.Vlans)) - for i_vlans, v_vlans := range config.Vlans { - var vlans_model Vlans_Model - - // Build vlan list - vlans_vlan_List := make([]Vlans_Vlan_Model, len(v_vlans.Vlan)) - - - for i_vlans_vlan, v_vlans_vlan := range v_vlans.Vlan { - var vlans_vlan_model Vlans_Vlan_Model - // leaf - if v_vlans_vlan.Name == nil { - vlans_vlan_model.Name = - types.StringNull() - } else { - vlans_vlan_model.Name = - types.StringPointerValue(v_vlans_vlan.Name) - } - - vlans_vlan_List[i_vlans_vlan] = - vlans_vlan_model - // leaf - if v_vlans_vlan.Vlan_id == nil { - vlans_vlan_model.Vlan_id = - types.StringNull() - } else { - vlans_vlan_model.Vlan_id = - types.StringPointerValue(v_vlans_vlan.Vlan_id) - } - - vlans_vlan_List[i_vlans_vlan] = - vlans_vlan_model - // leaf - if v_vlans_vlan.L3_interface == nil { - vlans_vlan_model.L3_interface = - types.StringNull() - } else { - vlans_vlan_model.L3_interface = - types.StringPointerValue(v_vlans_vlan.L3_interface) - } - - vlans_vlan_List[i_vlans_vlan] = - vlans_vlan_model - } - - // Write vlan field as Null when empty, else concrete list - if len(vlans_vlan_List) == 0 { - vlans_model.Vlan = - types.ListNull(types.ObjectType{AttrTypes: Vlans_Vlan_Model{}.AttrTypes()}) - } else { - vlans_model.Vlan, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Vlans_Vlan_Model{}.AttrTypes()}, - vlans_vlan_List, - ) - } - vlans_List[i_vlans] = vlans_model - - vlans_List[i_vlans] = vlans_model - } - - // Write parent list as Null when empty - if len(vlans_List) == 0 { - state.Vlans = - types.ListNull(types.ObjectType{AttrTypes: Vlans_Model{}.AttrTypes()}) - } else { - state.Vlans, _ = - types.ListValueFrom(ctx, - types.ObjectType{AttrTypes: Vlans_Model{}.AttrTypes()}, - vlans_List, - ) - } - - return state -} - -func (r *configResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var priorState ConfigResourceModel - resp.Diagnostics.Append(req.State.Get(ctx, &priorState)...) - if resp.Diagnostics.HasError() { - return - } - - state, ok := r.readStateFromDevice(ctx, priorState, &resp.Diagnostics) - if !ok { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - - - - - -// Update implements resource.Resource. -func (r *configResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ConfigResourceModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - planConfig := modelToConfig(ctx, plan, &resp.Diagnostics) - if resp.Diagnostics.HasError() { - return - } - - var stateConfig xml_Configuration - err := r.client.MarshalConfig(&stateConfig) - if err != nil { - resp.Diagnostics.AddError("Failed while reading current configuration", err.Error()) - return - } - - planXML, err := marshalConfigXML(planConfig) - if err != nil { - resp.Diagnostics.AddError("Failed to marshal plan config", err.Error()) - return - } - stateXML, err := marshalConfigXML(stateConfig) - if err != nil { - resp.Diagnostics.AddError("Failed to marshal state config", err.Error()) - return - } - - idx, err := patch.UnmarshalTrimmedSchemaIndex(TrimmedSchemaJSON) - if err != nil { - resp.Diagnostics.AddError("Failed while parsing trimmed schema", err.Error()) - return - } - - planTree, err := patch.BuildTree(planXML) - if err != nil { - resp.Diagnostics.AddError("Failed to parse plan XML", err.Error()) - return - } - stateTree, err := patch.BuildTree(stateXML) - if err != nil { - resp.Diagnostics.AddError("Failed to parse state XML", err.Error()) - return - } - - planMap := patch.LeafMapWithSchema(planTree, idx) - stateMap := patch.LeafMapWithSchema(stateTree, idx) - diffMap := patch.ComputeDiff(stateMap, planMap) - - if len(diffMap) == 0 { - state, ok := r.readStateFromDevice(ctx, plan, &resp.Diagnostics) - if !ok { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) - return - } - - patchXML, err := patch.CreateDiffPatch(diffMap, plan.ResourceName.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Failed to build NETCONF patch", err.Error()) - return - } - - patchPayload := string(patchXML) - debugPatchUpdate(plan.ResourceName.ValueString(), planXML, stateXML, diffMap, patchPayload) - err = r.client.SendUpdate("", patchPayload, false) - if err != nil { - resp.Diagnostics.AddError("Failed while sending diff patch", err.Error()) - return - } - commit_err := r.client.SendCommit() - if commit_err != nil { - resp.Diagnostics.AddError("Failed while committing configuration", commit_err.Error()) - return - } - - var verifiedConfig xml_Configuration - err = r.client.MarshalConfig(&verifiedConfig) - if err != nil { - resp.Diagnostics.AddError("Failed while reading patched configuration", err.Error()) - return - } - - verifiedXML, err := marshalConfigXML(verifiedConfig) - if err != nil { - resp.Diagnostics.AddError("Failed to marshal verified config", err.Error()) - return - } - verifiedTree, err := patch.BuildTree(verifiedXML) - if err != nil { - resp.Diagnostics.AddError("Failed to parse verified XML", err.Error()) - return - } - - verifiedMap := patch.LeafMapWithSchema(verifiedTree, idx) - remainingDiff := patch.ComputeDiff(verifiedMap, planMap) - if len(remainingDiff) > 0 { - err = r.client.SendDirectTransaction(planConfig, false) - if err != nil { - resp.Diagnostics.AddError("Patch had no effect and fallback update failed", err.Error()) - return - } - commit_err = r.client.SendCommit() - if commit_err != nil { - resp.Diagnostics.AddError("Fallback update commit failed", commit_err.Error()) - return - } - } - state, ok := r.readStateFromDevice(ctx, plan, &resp.Diagnostics) - if !ok { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - - - -// Delete implements resource.Resource. -func (r *configResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ConfigResourceModel - d := req.State.Get(ctx, &state) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { - return - } - - stateConfig := modelToConfig(ctx, state, &resp.Diagnostics) - if resp.Diagnostics.HasError() { - return - } - - stateXML, err := marshalConfigXML(stateConfig) - if err != nil { - resp.Diagnostics.AddError("Failed to marshal current configuration", err.Error()) - return - } - - emptyXML, err := marshalConfigXML(xml_Configuration{}) - if err != nil { - resp.Diagnostics.AddError("Failed to marshal empty configuration", err.Error()) - return - } - - idx, err := patch.UnmarshalTrimmedSchemaIndex(TrimmedSchemaJSON) - if err != nil { - resp.Diagnostics.AddError("Failed while parsing trimmed schema", err.Error()) - return - } - - stateTree, err := patch.BuildTree(stateXML) - if err != nil { - resp.Diagnostics.AddError("Failed to parse current XML", err.Error()) - return - } - emptyTree, err := patch.BuildTree(emptyXML) - if err != nil { - resp.Diagnostics.AddError("Failed to parse empty XML", err.Error()) - return - } - - stateMap := patch.LeafMapWithSchema(stateTree, idx) - emptyMap := patch.LeafMapWithSchema(emptyTree, idx) - diffMap := patch.ComputeDiff(stateMap, emptyMap) - if len(diffMap) == 0 { - return - } - - patchXML, err := patch.CreateDiffPatch(diffMap, state.ResourceName.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Failed to build delete patch", err.Error()) - return - } - - err = r.client.SendUpdate("", string(patchXML), false) - if err != nil { - resp.Diagnostics.AddError("Failed while deleting configuration", err.Error()) - return - } - commit_err := r.client.SendCommit() - if commit_err != nil { - resp.Diagnostics.AddError("Failed while committing configuration", commit_err.Error()) - return - } -} - -func debugPatchUpdate(resourceName string, planXML []byte, stateXML []byte, diffMap map[string]patch.Change, patchPayload string) { - if os.Getenv("JUNOS_TF_DEBUG_PATCH") == "" { - return - } - - fmt.Printf("\n=== terraform diff patch debug: %s ===\n", resourceName) - fmt.Printf("--- state xml ---\n%s\n", string(stateXML)) - fmt.Printf("--- plan xml ---\n%s\n", string(planXML)) - fmt.Printf("--- diff map ---\n") - for _, entry := range patch.DebugSortedChanges(diffMap) { - fmt.Printf("%v | %s | old=%q | new=%q\n", entry.Op, entry.Path, entry.OldVal, entry.NewVal) - } - fmt.Printf("--- patch payload ---\n%s\n", patchPayload) -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_config_provider_test.go b/tf/providers/terraform-provider-junos-qfx/resource_config_provider_test.go deleted file mode 100644 index f9b7485d..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_config_provider_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "context" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/resource" -) - -func isStubConfigResource(t *testing.T, r *configResource) bool { - t.Helper() - schemaResp := &resource.SchemaResponse{} - r.Schema(context.Background(), resource.SchemaRequest{}, schemaResp) - return len(schemaResp.Schema.Attributes) == 0 -} - -// TestConfigResourceStubMethods verifies stub CRUD methods remain no-op. -func TestConfigResourceStubMethods(t *testing.T) { - r := &configResource{} - if !isStubConfigResource(t, r) { - t.Skip("generated config resource requires framework-populated requests") - } - - ctx := context.Background() - - createResp := &resource.CreateResponse{} - r.Create(ctx, resource.CreateRequest{}, createResp) - if createResp.Diagnostics.HasError() { - t.Fatalf("Create() should not add diagnostics") - } - - readResp := &resource.ReadResponse{} - r.Read(ctx, resource.ReadRequest{}, readResp) - if readResp.Diagnostics.HasError() { - t.Fatalf("Read() should not add diagnostics") - } - - updateResp := &resource.UpdateResponse{} - r.Update(ctx, resource.UpdateRequest{}, updateResp) - if updateResp.Diagnostics.HasError() { - t.Fatalf("Update() should not add diagnostics") - } - - deleteResp := &resource.DeleteResponse{} - r.Delete(ctx, resource.DeleteRequest{}, deleteResp) - if deleteResp.Diagnostics.HasError() { - t.Fatalf("Delete() should not add diagnostics") - } -} - -// TestConfigResourceMetadataAndSchema verifies current stub metadata and schema. -func TestConfigResourceMetadataAndSchema(t *testing.T) { - r := &configResource{} - ctx := context.Background() - - metadataResp := &resource.MetadataResponse{} - r.Metadata(ctx, resource.MetadataRequest{}, metadataResp) - if metadataResp.TypeName == "" { - t.Fatalf("expected non-empty metadata type name") - } - - schemaResp := &resource.SchemaResponse{} - r.Schema(ctx, resource.SchemaRequest{}, schemaResp) - if isStubConfigResource(t, r) && len(schemaResp.Schema.Attributes) != 0 { - t.Fatalf("expected empty schema attributes for stub resource") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_file.go b/tf/providers/terraform-provider-junos-qfx/resource_file.go deleted file mode 100644 index 3352383f..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_file.go +++ /dev/null @@ -1,142 +0,0 @@ -package main - -import ( - "context" - "os" - "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/types" -) - -var _ resource.ResourceWithConfigure = new(resourceFile) - -type resourceFile struct { - dir string -} - -// Configure implements resource.ResourceWithConfigure. -func (r *resourceFile) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - r.dir = req.ProviderData.(string) -} - -type fileModel struct { - Name types.String `tfsdk:"name"` - Contents types.String `tfsdk:"contents"` -} - -// writeManagedFile writes the planned resource contents to disk. -func writeManagedFile(dir string, plan fileModel) error { - return os.WriteFile(path.Join(dir, plan.Name.ValueString()), []byte(plan.Contents.ValueString()), 0644) -} - -// readManagedFile loads the managed file contents into Terraform state. -func readManagedFile(dir string, state *fileModel) error { - data, err := os.ReadFile(path.Join(dir, state.Name.ValueString())) - if err != nil { - return err - } - state.Contents = types.StringValue(string(data)) - return nil -} - -// deleteManagedFile removes the managed file and ignores missing-file cases. -func deleteManagedFile(dir string, state fileModel) error { - err := os.Remove(path.Join(dir, state.Name.ValueString())) - if err != nil && os.IsNotExist(err) { - return nil - } - return err -} - -// Metadata implements resource.Resource. -func (r *resourceFile) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_file" -} - -// Schema implements resource.Resource. -func (r *resourceFile) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "contents": schema.StringAttribute{ - Required: true, - }, - }, - } -} - -// Create implements resource.Resource. -func (r *resourceFile) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan fileModel - d := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { - return - } - err := writeManagedFile(r.dir, plan) - if err != nil { - resp.Diagnostics.AddError("failed writing file", err.Error()) - return - } - d = resp.State.Set(ctx, &plan) - resp.Diagnostics.Append(d...) -} - -// Read implements resource.Resource. -func (r *resourceFile) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state fileModel - d := req.State.Get(ctx, &state) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { - return - } - err := readManagedFile(r.dir, &state) - if err != nil { - resp.Diagnostics.AddError("failed reading file", err.Error()) - return - } - d = resp.State.Set(ctx, &state) - resp.Diagnostics.Append(d...) -} - -// Update implements resource.Resource. -func (r *resourceFile) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan fileModel - d := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { - return - } - err := writeManagedFile(r.dir, plan) - if err != nil { - resp.Diagnostics.AddError("failed writing file", err.Error()) - return - } - d = resp.State.Set(ctx, &plan) - resp.Diagnostics.Append(d...) -} - -// Delete implements resource.Resource. -func (r *resourceFile) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state fileModel - d := req.State.Get(ctx, &state) - resp.Diagnostics.Append(d...) - if resp.Diagnostics.HasError() { - return - } - err := deleteManagedFile(r.dir, state) - if err != nil { - resp.Diagnostics.AddError("failed deleting file", err.Error()) - return - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_file_crud_test.go b/tf/providers/terraform-provider-junos-qfx/resource_file_crud_test.go deleted file mode 100644 index 7d1cfc1d..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_file_crud_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package main - -import ( - "context" - "errors" - "os" - "path/filepath" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// fileResourceSchema builds the resource schema used to initialize typed plan/state values. -func fileResourceSchema(t *testing.T, rf *resourceFile) resource.SchemaResponse { - t.Helper() - resp := resource.SchemaResponse{} - rf.Schema(context.Background(), resource.SchemaRequest{}, &resp) - return resp -} - -// TestResourceFileCreateReadUpdateDeleteEndToEnd verifies full CRUD behavior. -func TestResourceFileCreateReadUpdateDeleteEndToEnd(t *testing.T) { - ctx := context.Background() - dir := t.TempDir() - rf := &resourceFile{dir: dir} - schemaResp := fileResourceSchema(t, rf) - - createPlan := fileModel{ - Name: types.StringValue("managed.txt"), - Contents: types.StringValue("initial"), - } - createReqPlan := tfsdk.Plan{Schema: schemaResp.Schema} - if diags := createReqPlan.Set(ctx, createPlan); diags.HasError() { - t.Fatalf("failed to build create plan: %v", diags) - } - createReq := resource.CreateRequest{Plan: createReqPlan} - createResp := &resource.CreateResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Create(ctx, createReq, createResp) - if createResp.Diagnostics.HasError() { - t.Fatalf("Create() diagnostics: %v", createResp.Diagnostics) - } - - readState := tfsdk.State{Schema: schemaResp.Schema} - if diags := readState.Set(ctx, fileModel{Name: types.StringValue("managed.txt")}); diags.HasError() { - t.Fatalf("failed to build read state: %v", diags) - } - readReq := resource.ReadRequest{State: readState} - readResp := &resource.ReadResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Read(ctx, readReq, readResp) - if readResp.Diagnostics.HasError() { - t.Fatalf("Read() diagnostics: %v", readResp.Diagnostics) - } - - updatePlan := tfsdk.Plan{Schema: schemaResp.Schema} - if diags := updatePlan.Set(ctx, fileModel{Name: types.StringValue("managed.txt"), Contents: types.StringValue("updated")}); diags.HasError() { - t.Fatalf("failed to build update plan: %v", diags) - } - updateReq := resource.UpdateRequest{Plan: updatePlan} - updateResp := &resource.UpdateResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Update(ctx, updateReq, updateResp) - if updateResp.Diagnostics.HasError() { - t.Fatalf("Update() diagnostics: %v", updateResp.Diagnostics) - } - - deleteState := tfsdk.State{Schema: schemaResp.Schema} - if diags := deleteState.Set(ctx, fileModel{Name: types.StringValue("managed.txt")}); diags.HasError() { - t.Fatalf("failed to build delete state: %v", diags) - } - deleteReq := resource.DeleteRequest{State: deleteState} - deleteResp := &resource.DeleteResponse{} - rf.Delete(ctx, deleteReq, deleteResp) - if deleteResp.Diagnostics.HasError() { - t.Fatalf("Delete() diagnostics: %v", deleteResp.Diagnostics) - } - - if _, err := os.Stat(filepath.Join(dir, "managed.txt")); !errors.Is(err, os.ErrNotExist) { - t.Fatalf("expected managed file to be deleted, stat err: %v", err) - } -} - -// TestResourceFileCrudDiagPaths verifies unset request payloads panic in framework decoding. -func TestResourceFileCrudDiagPaths(t *testing.T) { - ctx := context.Background() - rf := &resourceFile{dir: t.TempDir()} - - tests := []struct { - name string - fn func() - }{ - { - name: "create", - fn: func() { - rf.Create(ctx, resource.CreateRequest{}, &resource.CreateResponse{}) - }, - }, - { - name: "read", - fn: func() { - rf.Read(ctx, resource.ReadRequest{}, &resource.ReadResponse{}) - }, - }, - { - name: "update", - fn: func() { - rf.Update(ctx, resource.UpdateRequest{}, &resource.UpdateResponse{}) - }, - }, - { - name: "delete", - fn: func() { - rf.Delete(ctx, resource.DeleteRequest{}, &resource.DeleteResponse{}) - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - defer func() { - if recover() == nil { - t.Fatalf("expected panic when request payload is unset") - } - }() - tc.fn() - }) - } -} - -// TestResourceFileCrudFilesystemErrors verifies diagnostics for filesystem failures. -func TestResourceFileCrudFilesystemErrors(t *testing.T) { - ctx := context.Background() - rf := &resourceFile{dir: filepath.Join(t.TempDir(), "does-not-exist")} - schemaResp := fileResourceSchema(t, rf) - - plan := tfsdk.Plan{Schema: schemaResp.Schema} - if diags := plan.Set(ctx, fileModel{Name: types.StringValue("f.txt"), Contents: types.StringValue("c")}); diags.HasError() { - t.Fatalf("failed to build plan: %v", diags) - } - - createResp := &resource.CreateResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Create(ctx, resource.CreateRequest{Plan: plan}, createResp) - if !createResp.Diagnostics.HasError() { - t.Fatalf("expected Create() filesystem diagnostic") - } - - readState := tfsdk.State{Schema: schemaResp.Schema} - if diags := readState.Set(ctx, fileModel{Name: types.StringValue("f.txt")}); diags.HasError() { - t.Fatalf("failed to build state: %v", diags) - } - readResp := &resource.ReadResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Read(ctx, resource.ReadRequest{State: readState}, readResp) - if !readResp.Diagnostics.HasError() { - t.Fatalf("expected Read() filesystem diagnostic") - } - - updateResp := &resource.UpdateResponse{State: tfsdk.State{Schema: schemaResp.Schema}} - rf.Update(ctx, resource.UpdateRequest{Plan: plan}, updateResp) - if !updateResp.Diagnostics.HasError() { - t.Fatalf("expected Update() filesystem diagnostic") - } - - deleteResp := &resource.DeleteResponse{} - rf.Delete(ctx, resource.DeleteRequest{State: readState}, deleteResp) - if deleteResp.Diagnostics.HasError() { - t.Fatalf("expected Delete() missing file path to be treated as no-op") - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_file_helpers_test.go b/tf/providers/terraform-provider-junos-qfx/resource_file_helpers_test.go deleted file mode 100644 index dce2d6cc..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_file_helpers_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "errors" - "os" - "path" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// TestWriteManagedFileAndReadManagedFile verifies round-trip write/read helper behavior. -func TestWriteManagedFileAndReadManagedFile(t *testing.T) { - dir := t.TempDir() - model := fileModel{ - Name: types.StringValue("sample.txt"), - Contents: types.StringValue("hello"), - } - - if err := writeManagedFile(dir, model); err != nil { - t.Fatalf("writeManagedFile() error: %v", err) - } - - state := fileModel{Name: types.StringValue("sample.txt")} - if err := readManagedFile(dir, &state); err != nil { - t.Fatalf("readManagedFile() error: %v", err) - } - if state.Contents.ValueString() != "hello" { - t.Fatalf("unexpected contents: %q", state.Contents.ValueString()) - } -} - -// TestReadManagedFileMissing verifies read helper errors for missing files. -func TestReadManagedFileMissing(t *testing.T) { - dir := t.TempDir() - state := fileModel{Name: types.StringValue("missing.txt")} - - err := readManagedFile(dir, &state) - if err == nil { - t.Fatalf("expected readManagedFile() error for missing file") - } -} - -// TestDeleteManagedFile verifies delete helper removes an existing file. -func TestDeleteManagedFile(t *testing.T) { - dir := t.TempDir() - filePath := path.Join(dir, "delete-me.txt") - if err := os.WriteFile(filePath, []byte("x"), 0644); err != nil { - t.Fatalf("setup write failed: %v", err) - } - - state := fileModel{Name: types.StringValue("delete-me.txt")} - if err := deleteManagedFile(dir, state); err != nil { - t.Fatalf("deleteManagedFile() error: %v", err) - } - if _, err := os.Stat(filePath); !errors.Is(err, os.ErrNotExist) { - t.Fatalf("expected file to be deleted, got stat err: %v", err) - } -} - -// TestDeleteManagedFileMissingIsNoop verifies delete helper ignores missing files. -func TestDeleteManagedFileMissingIsNoop(t *testing.T) { - dir := t.TempDir() - state := fileModel{Name: types.StringValue("missing.txt")} - - if err := deleteManagedFile(dir, state); err != nil { - t.Fatalf("expected no error for missing file delete, got: %v", err) - } -} diff --git a/tf/providers/terraform-provider-junos-qfx/resource_file_test.go b/tf/providers/terraform-provider-junos-qfx/resource_file_test.go deleted file mode 100644 index b7584e1f..00000000 --- a/tf/providers/terraform-provider-junos-qfx/resource_file_test.go +++ /dev/null @@ -1,356 +0,0 @@ -package main - -import ( - "context" - "os" - "path" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// TestResourceFileMetadata tests the Metadata method -func TestResourceFileMetadata(t *testing.T) { - rf := &resourceFile{} - ctx := context.Background() - - req := resource.MetadataRequest{ - ProviderTypeName: "junos", - } - resp := &resource.MetadataResponse{} - - rf.Metadata(ctx, req, resp) - - expectedTypeName := "junos_file" - if resp.TypeName != expectedTypeName { - t.Errorf("type name mismatch: expected %s, got %s", expectedTypeName, resp.TypeName) - } -} - -// TestResourceFileSchema tests the Schema method -func TestResourceFileSchema(t *testing.T) { - rf := &resourceFile{} - ctx := context.Background() - - req := resource.SchemaRequest{} - resp := &resource.SchemaResponse{} - - rf.Schema(ctx, req, resp) - - if len(resp.Schema.Attributes) == 0 { - t.Fatal("expected schema to have attributes") - } - - // Verify required attributes - requiredAttrs := []string{"name", "contents"} - for _, attr := range requiredAttrs { - if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("expected attribute %q in schema", attr) - } - } - - // Verify name is required - nameAttr := resp.Schema.Attributes["name"] - if nameAttr == nil { - t.Fatal("name attribute not found") - } - if !nameAttr.IsRequired() { - t.Error("name attribute should be required") - } - - // Verify contents is required - contentsAttr := resp.Schema.Attributes["contents"] - if contentsAttr == nil { - t.Fatal("contents attribute not found") - } - if !contentsAttr.IsRequired() { - t.Error("contents attribute should be required") - } -} - -// TestResourceFileConfigure tests the Configure method -func TestResourceFileConfigure(t *testing.T) { - rf := &resourceFile{} - ctx := context.Background() - testDir := "/tmp/test-terraform-files" - - req := resource.ConfigureRequest{ - ProviderData: testDir, - } - resp := &resource.ConfigureResponse{} - - rf.Configure(ctx, req, resp) - - if rf.dir != testDir { - t.Errorf("dir mismatch: expected %s, got %s", testDir, rf.dir) - } -} - -// TestResourceFileConfigureWithNilData tests Configure with nil provider data -func TestResourceFileConfigureWithNilData(t *testing.T) { - rf := &resourceFile{} - ctx := context.Background() - - req := resource.ConfigureRequest{ - ProviderData: nil, - } - resp := &resource.ConfigureResponse{} - - rf.Configure(ctx, req, resp) - - if rf.dir != "" { - t.Errorf("expected empty dir, got %s", rf.dir) - } -} - -// TestResourceFileCreate tests the Create method -func TestResourceFileCreate(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - testFileName := "test.txt" - testContent := "Hello, World!" - - plan := fileModel{ - Name: types.StringValue(testFileName), - Contents: types.StringValue(testContent), - } - _ = plan - - // Create a mock request - // Use the provided APIs to set plan/state via helper structs - // Build a fake plan/state using the framework types - req := resource.CreateRequest{} - _ = req - // Calling Create directly requires using the framework runtime; instead, exercise the underlying logic by writing the file directly - if err := os.WriteFile(path.Join(tmpDir, testFileName), []byte(testContent), 0644); err != nil { - t.Fatalf("failed to write test file: %v", err) - } - - // Simulate the Create call by invoking the file write logic used by Create - // (We already wrote the file above; this ensures the file exists for assertions) - - // Verify file was created - filePath := path.Join(tmpDir, testFileName) - if _, err := os.Stat(filePath); err != nil { - t.Fatalf("expected file to exist at %s, got error: %v", filePath, err) - } - - // Verify file contents - contents, err := os.ReadFile(filePath) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - if string(contents) != testContent { - t.Errorf("content mismatch: expected %s, got %s", testContent, string(contents)) - } -} - -// TestResourceFileRead tests the Read method -func TestResourceFileRead(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - // Create a test file - testFileName := "test.txt" - testContent := "Test content" - filePath := path.Join(tmpDir, testFileName) - - if err := os.WriteFile(filePath, []byte(testContent), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - // Validate by reading the file directly instead of using framework plumbing - data, err := os.ReadFile(filePath) - if err != nil { - t.Fatalf("failed to read file directly: %v", err) - } - if string(data) != testContent { - t.Errorf("content mismatch: expected %s, got %s", testContent, string(data)) - } -} - -// TestResourceFileReadNonExistentFile tests Read with a non-existent file -func TestResourceFileReadNonExistentFile(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - // Reading a non-existent file should return an error when using os.ReadFile - _, err := os.ReadFile(path.Join(tmpDir, "nonexistent.txt")) - if err == nil { - t.Error("expected error reading non-existent file") - } -} - -// TestResourceFileUpdate tests the Update method -func TestResourceFileUpdate(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - testFileName := "test.txt" - newContent := "Updated content" - - plan := fileModel{ - Name: types.StringValue(testFileName), - Contents: types.StringValue(newContent), - } - _ = plan - - // Simulate an update by writing the new content directly and validating it - if err := os.WriteFile(path.Join(tmpDir, testFileName), []byte(newContent), 0644); err != nil { - t.Fatalf("failed to write updated file: %v", err) - } - - filePath := path.Join(tmpDir, testFileName) - contents, err := os.ReadFile(filePath) - if err != nil { - t.Fatalf("failed to read file: %v", err) - } - - if string(contents) != newContent { - t.Errorf("content mismatch: expected %s, got %s", newContent, string(contents)) - } -} - -// TestResourceFileDelete tests the Delete method -func TestResourceFileDelete(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - // Create a test file - testFileName := "test.txt" - filePath := path.Join(tmpDir, testFileName) - if err := os.WriteFile(filePath, []byte("content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - state := fileModel{ - Name: types.StringValue(testFileName), - Contents: types.StringValue("content"), - } - _ = state - - // Simulate deletion by removing the file and validating it no longer exists - if err := os.Remove(filePath); err != nil { - t.Fatalf("failed to remove file: %v", err) - } - - if _, err := os.Stat(filePath); err == nil { - t.Error("expected file to be deleted") - } -} - -// TestResourceFileDeleteNonExistentFile tests Delete with a non-existent file -func TestResourceFileDeleteNonExistentFile(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - state := fileModel{ - Name: types.StringValue("nonexistent.txt"), - Contents: types.StringValue(""), - } - _ = state - - // Deleting a non-existent file should return an error from os.Remove - err := os.Remove(path.Join(tmpDir, "nonexistent.txt")) - if err == nil { - t.Error("expected error when removing non-existent file") - } -} - -// TestFileModel tests the fileModel structure -func TestFileModel(t *testing.T) { - model := fileModel{ - Name: types.StringValue("test.txt"), - Contents: types.StringValue("test content"), - } - - if model.Name.ValueString() != "test.txt" { - t.Error("name value mismatch") - } - - if model.Contents.ValueString() != "test content" { - t.Error("contents value mismatch") - } -} - -// TestResourceFileWithSubdirectories tests file creation in subdirectories -func TestResourceFileWithSubdirectories(t *testing.T) { - tmpDir := t.TempDir() - subdir := "subdir" - if err := os.MkdirAll(path.Join(tmpDir, subdir), 0755); err != nil { - t.Fatalf("failed to create subdirectory: %v", err) - } - - rf := &resourceFile{dir: tmpDir} - ctx := context.Background() - _ = rf - _ = ctx - - testFileName := path.Join(subdir, "test.txt") - testContent := "Nested file content" - - // Simulate creating nested file directly - filePath := path.Join(tmpDir, testFileName) - if err := os.WriteFile(filePath, []byte(testContent), 0644); err != nil { - t.Fatalf("failed to create nested file: %v", err) - } - - // Verify file was created in subdirectory - if _, err := os.Stat(filePath); err != nil { - t.Fatalf("expected file to exist at %s, got error: %v", filePath, err) - } -} - -// TestResourceFilePermissions tests file creation with correct permissions -func TestResourceFilePermissions(t *testing.T) { - tmpDir := t.TempDir() - rf := &resourceFile{dir: tmpDir} - _ = rf - - testFileName := "test.txt" - filePath := path.Join(tmpDir, testFileName) - - if err := os.WriteFile(filePath, []byte("content"), 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - fileInfo, err := os.Stat(filePath) - if err != nil { - t.Fatalf("failed to stat file: %v", err) - } - - // Check file permissions (0644 = rw-r--r--) - expectedPerm := os.FileMode(0644) - if fileInfo.Mode().Perm() != expectedPerm { - t.Errorf("permission mismatch: expected %o, got %o", expectedPerm, fileInfo.Mode().Perm()) - } -} - -// TestResourceFileInterfaceImplementation verifies interface implementation -func TestResourceFileInterfaceImplementation(t *testing.T) { - rf := &resourceFile{} - _ = rf - var _ resource.ResourceWithConfigure = rf - // If this compiles, the interface is properly implemented -} diff --git a/tf/providers/terraform-provider-junos-qfx/trimmed_schema.json b/tf/providers/terraform-provider-junos-qfx/trimmed_schema.json deleted file mode 100644 index 958f19a7..00000000 --- a/tf/providers/terraform-provider-junos-qfx/trimmed_schema.json +++ /dev/null @@ -1,1391 +0,0 @@ -{ - "root": { - "name": "root", - "type": "container", - "children": [ - { - "name": "configuration", - "type": "container", - "children": [ - { - "name": "chassis", - "type": "container", - "children": [ - { - "name": "aggregated-devices", - "type": "container", - "children": [ - { - "name": "ethernet", - "type": "container", - "children": [ - { - "name": "device-count", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "chassis/aggregated-devices/ethernet/device-count" - }, - { - "type": "uint32", - "path": "chassis/aggregated-devices/ethernet/device-count" - } - ], - "path": "chassis/aggregated-devices/ethernet" - } - ], - "path": "chassis/aggregated-devices" - } - ], - "path": "chassis" - }, - { - "name": "fpc", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "chassis/fpc/name" - }, - { - "type": "uint32", - "path": "chassis/fpc/name" - } - ], - "path": "chassis/fpc" - }, - { - "name": "pic", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "chassis/fpc/pic/name" - }, - { - "type": "uint32", - "path": "chassis/fpc/pic/name" - } - ], - "path": "chassis/fpc/pic" - }, - { - "name": "port", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "chassis/fpc/pic/port/name" - }, - { - "type": "uint32", - "path": "chassis/fpc/pic/port/name" - } - ], - "path": "chassis/fpc/pic/port" - }, - { - "name": "speed", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "oc3-stm1", - "value": 0, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "oc12-stm4", - "value": 1, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "oc48-stm16", - "value": 2, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "1G", - "value": 3, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "10g", - "value": 4, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "25g", - "value": 5, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "40g", - "value": 6, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "100g", - "value": 7, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "50g", - "value": 8, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "200g", - "value": 9, - "path": "chassis/fpc/pic/port/speed" - }, - { - "id": "400g", - "value": 10, - "path": "chassis/fpc/pic/port/speed" - } - ], - "path": "chassis/fpc/pic/port" - }, - { - "name": "channel-speed", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "10g", - "value": 0, - "path": "chassis/fpc/pic/port/channel-speed" - }, - { - "id": "25g", - "value": 1, - "path": "chassis/fpc/pic/port/channel-speed" - }, - { - "id": "50g", - "value": 2, - "path": "chassis/fpc/pic/port/channel-speed" - }, - { - "id": "disable-auto-speed-detection", - "value": 3, - "path": "chassis/fpc/pic/port/channel-speed" - } - ], - "path": "chassis/fpc/pic/port" - } - ], - "path": "chassis/fpc/pic" - } - ], - "path": "chassis/fpc" - } - ], - "path": "chassis" - } - ], - "path": "" - }, - { - "name": "firewall", - "type": "container", - "children": [ - { - "name": "family", - "type": "container", - "children": [ - { - "name": "inet", - "type": "container", - "children": [ - { - "name": "filter", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "firewall/family/inet/filter" - }, - { - "name": "term", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "firewall/family/inet/filter/term" - }, - { - "name": "from", - "type": "container", - "children": [ - { - "name": "source-address", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipv4prefix", - "base-type": "string", - "path": "firewall/family/inet/filter/term/from/source-address" - } - ], - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from" - }, - { - "name": "destination-address", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipv4prefix", - "base-type": "string", - "path": "firewall/family/inet/filter/term/from/destination-address" - } - ], - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from" - }, - { - "name": "protocol", - "type": "leaf-list", - "leaf-type": "string", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from" - }, - { - "name": "destination-port", - "type": "leaf-list", - "leaf-type": "string", - "ordered-by": "user", - "path": "firewall/family/inet/filter/term/from" - } - ], - "path": "firewall/family/inet/filter/term" - }, - { - "name": "then", - "type": "container", - "children": [ - { - "name": "accept", - "type": "leaf", - "leaf-type": "empty", - "path": "firewall/family/inet/filter/term/then" - }, - { - "name": "discard", - "type": "container", - "path": "firewall/family/inet/filter/term/then" - } - ], - "path": "firewall/family/inet/filter/term" - } - ], - "ordered-by": "user", - "path": "firewall/family/inet/filter" - } - ], - "path": "firewall/family/inet" - } - ], - "path": "firewall/family" - }, - { - "name": "inet6", - "type": "container", - "children": [ - { - "name": "filter", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "firewall/family/inet6/filter" - }, - { - "name": "term", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "firewall/family/inet6/filter/term" - }, - { - "name": "from", - "type": "container", - "children": [ - { - "name": "next-header", - "type": "leaf-list", - "leaf-type": "string", - "ordered-by": "user", - "path": "firewall/family/inet6/filter/term/from" - }, - { - "name": "destination-port", - "type": "leaf-list", - "leaf-type": "string", - "ordered-by": "user", - "path": "firewall/family/inet6/filter/term/from" - } - ], - "path": "firewall/family/inet6/filter/term" - }, - { - "name": "then", - "type": "container", - "children": [ - { - "name": "accept", - "type": "leaf", - "leaf-type": "empty", - "path": "firewall/family/inet6/filter/term/then" - }, - { - "name": "discard", - "type": "leaf", - "leaf-type": "empty", - "path": "firewall/family/inet6/filter/term/then" - } - ], - "path": "firewall/family/inet6/filter/term" - } - ], - "ordered-by": "user", - "path": "firewall/family/inet6/filter" - } - ], - "path": "firewall/family/inet6" - } - ], - "path": "firewall/family" - } - ], - "path": "firewall" - } - ], - "path": "" - }, - { - "name": "forwarding-options", - "type": "container", - "children": [ - { - "name": "storm-control-profiles", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "lengths": [ - { - "min": 1, - "max": 127, - "path": "forwarding-options/storm-control-profiles/name" - } - ], - "path": "forwarding-options/storm-control-profiles" - }, - { - "name": "all", - "type": "container", - "children": [ - { - "name": "bandwidth-level", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "forwarding-options/storm-control-profiles/all/bandwidth-level" - }, - { - "type": "uint32", - "path": "forwarding-options/storm-control-profiles/all/bandwidth-level" - } - ], - "units": "kbps", - "path": "forwarding-options/storm-control-profiles/all" - } - ], - "path": "forwarding-options/storm-control-profiles" - } - ], - "path": "forwarding-options" - } - ], - "path": "" - }, - { - "name": "interfaces", - "type": "container", - "children": [ - { - "name": "interface", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "interfaces/interface" - }, - { - "name": "mtu", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "interfaces/interface/mtu" - }, - { - "type": "uint32", - "path": "interfaces/interface/mtu" - } - ], - "path": "interfaces/interface" - }, - { - "name": "ether-options", - "type": "container", - "children": [ - { - "name": "ieee-802.3ad", - "type": "container", - "children": [ - { - "name": "bundle", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle" - }, - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "interfaces/interface/ether-options/ieee-802.3ad/bundle" - } - ], - "path": "interfaces/interface/ether-options/ieee-802.3ad" - } - ], - "path": "interfaces/interface/ether-options" - } - ], - "path": "interfaces/interface" - }, - { - "name": "aggregated-ether-options", - "type": "container", - "children": [ - { - "name": "lacp", - "type": "container", - "children": [ - { - "name": "active", - "type": "leaf", - "leaf-type": "empty", - "path": "interfaces/interface/aggregated-ether-options/lacp" - } - ], - "path": "interfaces/interface/aggregated-ether-options" - } - ], - "path": "interfaces/interface" - }, - { - "name": "unit", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "interfaces/interface/unit" - }, - { - "name": "family", - "type": "container", - "children": [ - { - "name": "inet", - "type": "container", - "children": [ - { - "name": "filter", - "type": "container", - "children": [ - { - "name": "input", - "type": "container", - "children": [ - { - "name": "filter-name", - "type": "leaf", - "leaf-type": "string", - "path": "interfaces/interface/unit/family/inet/filter/input" - } - ], - "path": "interfaces/interface/unit/family/inet/filter" - } - ], - "path": "interfaces/interface/unit/family/inet" - }, - { - "name": "address", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipv4prefix", - "base-type": "string", - "path": "interfaces/interface/unit/family/inet/address" - } - ], - "ordered-by": "user", - "path": "interfaces/interface/unit/family/inet" - } - ], - "path": "interfaces/interface/unit/family" - }, - { - "name": "inet6", - "type": "container", - "children": [ - { - "name": "filter", - "type": "container", - "children": [ - { - "name": "input", - "type": "container", - "children": [ - { - "name": "filter-name", - "type": "leaf", - "leaf-type": "string", - "path": "interfaces/interface/unit/family/inet6/filter/input" - } - ], - "path": "interfaces/interface/unit/family/inet6/filter" - } - ], - "path": "interfaces/interface/unit/family/inet6" - }, - { - "name": "address", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipv6prefix", - "base-type": "string", - "path": "interfaces/interface/unit/family/inet6/address" - } - ], - "ordered-by": "user", - "path": "interfaces/interface/unit/family/inet6" - } - ], - "path": "interfaces/interface/unit/family" - }, - { - "name": "ethernet-switching", - "type": "container", - "children": [ - { - "name": "interface-mode", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "access", - "value": 0, - "path": "interfaces/interface/unit/family/ethernet-switching/interface-mode" - }, - { - "id": "trunk", - "value": 1, - "path": "interfaces/interface/unit/family/ethernet-switching/interface-mode" - } - ], - "default": "access", - "path": "interfaces/interface/unit/family/ethernet-switching" - }, - { - "name": "vlan", - "type": "container", - "children": [ - { - "name": "members", - "type": "leaf-list", - "leaf-type": "string", - "ordered-by": "user", - "path": "interfaces/interface/unit/family/ethernet-switching/vlan" - } - ], - "path": "interfaces/interface/unit/family/ethernet-switching" - }, - { - "name": "storm-control", - "type": "container", - "children": [ - { - "name": "profile-name", - "type": "leaf", - "leaf-type": "string", - "path": "interfaces/interface/unit/family/ethernet-switching/storm-control" - } - ], - "path": "interfaces/interface/unit/family/ethernet-switching" - } - ], - "path": "interfaces/interface/unit/family" - } - ], - "path": "interfaces/interface/unit" - } - ], - "path": "interfaces/interface" - } - ], - "path": "interfaces" - } - ], - "path": "" - }, - { - "name": "policy-options", - "type": "container", - "children": [ - { - "name": "policy-statement", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "policy-options/policy-statement" - }, - { - "name": "term", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "policy-options/policy-statement/term" - }, - { - "name": "from", - "type": "container", - "children": [ - { - "name": "route-filter", - "type": "list", - "key": "address choice-ident choice-value", - "children": [ - { - "name": "address", - "type": "leaf", - "leaf-type": "jt:ipprefix", - "base-type": "string", - "path": "policy-options/policy-statement/term/from/route-filter" - }, - { - "name": "exact", - "type": "leaf", - "leaf-type": "string", - "path": "policy-options/policy-statement/term/from/route-filter" - } - ], - "ordered-by": "user", - "path": "policy-options/policy-statement/term/from" - } - ], - "path": "policy-options/policy-statement/term" - }, - { - "name": "then", - "type": "container", - "children": [ - { - "name": "accept", - "type": "leaf", - "leaf-type": "empty", - "path": "policy-options/policy-statement/term/then" - }, - { - "name": "reject", - "type": "leaf", - "leaf-type": "empty", - "path": "policy-options/policy-statement/term/then" - } - ], - "path": "policy-options/policy-statement/term" - } - ], - "ordered-by": "user", - "path": "policy-options/policy-statement" - } - ], - "path": "policy-options" - } - ], - "path": "" - }, - { - "name": "protocols", - "type": "container", - "children": [ - { - "name": "bgp", - "type": "container", - "children": [ - { - "name": "group", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "protocols/bgp/group" - }, - { - "name": "type", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "internal", - "value": 0, - "path": "protocols/bgp/group/type" - }, - { - "id": "external", - "value": 1, - "path": "protocols/bgp/group/type" - } - ], - "path": "protocols/bgp/group" - }, - { - "name": "peer-as", - "type": "leaf", - "leaf-type": "string", - "path": "protocols/bgp/group" - }, - { - "name": "neighbor", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipaddr-scoped", - "base-type": "string", - "path": "protocols/bgp/group/neighbor" - }, - { - "name": "import", - "type": "leaf-list", - "leaf-type": "jt:policy-algebra", - "base-type": "string", - "ordered-by": "user", - "path": "protocols/bgp/group/neighbor" - }, - { - "name": "family", - "type": "container", - "children": [ - { - "name": "inet", - "type": "container", - "children": [ - { - "name": "unicast", - "type": "container", - "path": "protocols/bgp/group/neighbor/family/inet" - } - ], - "path": "protocols/bgp/group/neighbor/family" - }, - { - "name": "inet6", - "type": "container", - "children": [ - { - "name": "unicast", - "type": "container", - "path": "protocols/bgp/group/neighbor/family/inet6" - } - ], - "path": "protocols/bgp/group/neighbor/family" - } - ], - "path": "protocols/bgp/group/neighbor" - }, - { - "name": "export", - "type": "leaf-list", - "leaf-type": "jt:policy-algebra", - "base-type": "string", - "ordered-by": "user", - "path": "protocols/bgp/group/neighbor" - } - ], - "ordered-by": "user", - "path": "protocols/bgp/group" - } - ], - "ordered-by": "user", - "path": "protocols/bgp" - } - ], - "path": "protocols" - }, - { - "name": "lldp", - "type": "container", - "children": [ - { - "name": "interface", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "protocols/lldp/interface" - } - ], - "ordered-by": "user", - "path": "protocols/lldp" - } - ], - "path": "protocols" - } - ], - "path": "" - }, - { - "name": "routing-options", - "type": "container", - "children": [ - { - "name": "autonomous-system", - "type": "container", - "children": [ - { - "name": "as-number", - "type": "leaf", - "leaf-type": "string", - "path": "routing-options/autonomous-system" - } - ], - "path": "routing-options" - }, - { - "name": "static", - "type": "container", - "children": [ - { - "name": "route", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipprefix", - "base-type": "string", - "path": "routing-options/static/route" - }, - { - "name": "discard", - "type": "leaf", - "leaf-type": "empty", - "path": "routing-options/static/route" - } - ], - "path": "routing-options/static" - } - ], - "path": "routing-options" - } - ], - "path": "" - }, - { - "name": "system", - "type": "container", - "children": [ - { - "name": "host-name", - "type": "leaf", - "leaf-type": "string", - "lengths": [ - { - "min": 1, - "max": 255, - "path": "system/host-name" - } - ], - "path": "system" - }, - { - "name": "root-authentication", - "type": "container", - "children": [ - { - "name": "encrypted-password", - "type": "leaf", - "leaf-type": "string", - "lengths": [ - { - "min": 1, - "max": 128, - "path": "system/root-authentication/encrypted-password" - } - ], - "path": "system/root-authentication" - } - ], - "path": "system" - }, - { - "name": "login", - "type": "container", - "children": [ - { - "name": "user", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "system/login/user" - }, - { - "name": "uid", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "system/login/user/uid" - }, - { - "type": "uint32", - "ranges": [ - { - "min": 100, - "max": 64000, - "path": "system/login/user/uid" - } - ], - "path": "system/login/user/uid" - } - ], - "path": "system/login/user" - }, - { - "name": "class", - "type": "leaf", - "leaf-type": "string", - "path": "system/login/user" - }, - { - "name": "authentication", - "type": "container", - "children": [ - { - "name": "ssh-ed25519", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "system/login/user/authentication/ssh-ed25519" - } - ], - "ordered-by": "user", - "path": "system/login/user/authentication" - } - ], - "path": "system/login/user" - } - ], - "path": "system/login" - } - ], - "path": "system" - }, - { - "name": "services", - "type": "container", - "children": [ - { - "name": "netconf", - "type": "container", - "children": [ - { - "name": "ssh", - "type": "container", - "path": "system/services/netconf" - } - ], - "path": "system/services" - }, - { - "name": "ssh", - "type": "container", - "children": [ - { - "name": "root-login", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "allow", - "value": 0, - "path": "system/services/ssh/root-login" - }, - { - "id": "deny", - "value": 1, - "path": "system/services/ssh/root-login" - }, - { - "id": "deny-password", - "value": 2, - "path": "system/services/ssh/root-login" - } - ], - "path": "system/services/ssh" - } - ], - "path": "system/services" - } - ], - "path": "system" - }, - { - "name": "time-zone", - "type": "leaf", - "leaf-type": "string", - "default": "UTC", - "units": "/ or ", - "path": "system" - }, - { - "name": "authentication-order", - "type": "leaf-list", - "leaf-type": "enumeration", - "enums": [ - { - "id": "radius", - "value": 0, - "path": "system/authentication-order" - }, - { - "id": "tacplus", - "value": 1, - "path": "system/authentication-order" - }, - { - "id": "password", - "value": 2, - "path": "system/authentication-order" - } - ], - "ordered-by": "user", - "path": "system" - }, - { - "name": "ports", - "type": "container", - "children": [ - { - "name": "console", - "type": "container", - "children": [ - { - "name": "log-out-on-disconnect", - "type": "leaf", - "leaf-type": "empty", - "path": "system/ports/console" - } - ], - "path": "system/ports" - } - ], - "path": "system" - }, - { - "name": "name-server", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "jt:ipaddr", - "base-type": "string", - "path": "system/name-server" - } - ], - "ordered-by": "user", - "path": "system" - }, - { - "name": "processes", - "type": "container", - "children": [ - { - "name": "daemon-process", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "path": "system/processes/daemon-process" - }, - { - "name": "disable", - "type": "leaf", - "leaf-type": "empty", - "path": "system/processes/daemon-process" - } - ], - "ordered-by": "user", - "path": "system/processes" - } - ], - "path": "system" - } - ], - "path": "" - }, - { - "name": "virtual-chassis", - "type": "container", - "children": [ - { - "name": "preprovisioned", - "type": "leaf", - "leaf-type": "empty", - "path": "virtual-chassis" - }, - { - "name": "member", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "virtual-chassis/member/name" - }, - { - "type": "int32", - "path": "virtual-chassis/member/name" - } - ], - "path": "virtual-chassis/member" - }, - { - "name": "role", - "type": "leaf", - "leaf-type": "enumeration", - "enums": [ - { - "id": "routing-engine", - "value": 0, - "path": "virtual-chassis/member/role" - }, - { - "id": "line-card", - "value": 1, - "path": "virtual-chassis/member/role" - } - ], - "default": "line-card", - "path": "virtual-chassis/member" - }, - { - "name": "serial-number", - "type": "leaf", - "leaf-type": "string", - "lengths": [ - { - "min": 1, - "max": 12, - "path": "virtual-chassis/member/serial-number" - } - ], - "path": "virtual-chassis/member" - } - ], - "ordered-by": "user", - "path": "virtual-chassis" - } - ], - "path": "" - }, - { - "name": "vlans", - "type": "container", - "children": [ - { - "name": "vlan", - "type": "list", - "key": "name", - "children": [ - { - "name": "name", - "type": "leaf", - "leaf-type": "string", - "lengths": [ - { - "min": 2, - "max": 64, - "path": "vlans/vlan/name" - } - ], - "path": "vlans/vlan" - }, - { - "name": "vlan-id", - "type": "leaf", - "leaf-type": "string", - "path": "vlans/vlan" - }, - { - "name": "l3-interface", - "type": "leaf", - "leaf-type": "union", - "types": [ - { - "type": "string", - "path": "vlans/vlan/l3-interface" - }, - { - "type": "string", - "patterns": [ - "<.*>|$.*" - ], - "path": "vlans/vlan/l3-interface" - } - ], - "path": "vlans/vlan" - } - ], - "path": "vlans" - } - ], - "path": "" - } - ], - "config": "true", - "path": "" - } - ], - "path": "" - }, - "path": "" -} \ No newline at end of file diff --git a/tf/shared/modules/cluster-fabric/.terraform.lock.hcl b/tf/shared/modules/cluster-fabric/.terraform.lock.hcl new file mode 100644 index 00000000..f9b28706 --- /dev/null +++ b/tf/shared/modules/cluster-fabric/.terraform.lock.hcl @@ -0,0 +1,18 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/jeremmfr/junos" { + version = "2.19.0" + constraints = "~> 2.19" + hashes = [ + "h1:s5wxKF9Zqrgra04dUQfvHIdNiUIyzC1YoUi983h/Omc=", + "zh:0133026a8e4187f54e6101c9faab426896328be804638daeaf1a22376d1d8805", + "zh:0532ba48ee37c814e18a76c714c0d0810fe688fe791989639b3bf5e43f7ceaff", + "zh:0fa82a384b25a58b65523e0ea4768fa1212b1f5cfc0c9379d31162454fedcc9d", + "zh:4e325ace584dac143bd770884eb1bbeba96aa59ac88c84cdf641bf4bdcab9857", + "zh:5e4d22b3792d20a6a38f27d8c712343b4c60a060967b18911148ba8de5a9be2c", + "zh:7a8cdb6452db79ae49e74a5a54bdc6298364720c3cbbdb674a1870468a04a5f1", + "zh:97b5a8fe55923c3b6c1d231e1c7f8e7df88ab653c0d234aacdfcc58b4bd3902a", + "zh:9cca035a71f1469113ec294ebd2634d9196ad7f4071c3ca1010016fbd547ba8f", + ] +} diff --git a/tf/shared/modules/cluster-fabric/chassis.tf b/tf/shared/modules/cluster-fabric/chassis.tf index 5ae7313c..e654ca87 100644 --- a/tf/shared/modules/cluster-fabric/chassis.tf +++ b/tf/shared/modules/cluster-fabric/chassis.tf @@ -1,13 +1,25 @@ -locals { - # Server ports run at 25G. Speed is set per quad (the quad leader at 0,4,8,...), - # on both VC members. - chassis_block = [{ - aggregated_devices = [{ ethernet = [{ device_count = var.aggregated_device_count }] }] - fpc = [ - for fpc in [0, 1] : { - name = fpc - pic = [{ name = 0, port = [for p in range(0, var.server_lag_count, 4) : { name = p, speed = "25g" }] }] - } +# Preprovisioned leaf VC + per-quad 25G port speed (set on the quad leader +# 0,4,8,... on both members). Speed has no typed resource, so it's raw set-config. +# device-count is auto-managed by junos_interface_physical. +resource "junos_virtual_chassis" "leaf" { + preprovisioned = true + + dynamic "member" { + for_each = var.vc_member_serials + content { + id = member.key + role = "routing-engine" + serial_number = member.value + } + } +} + +resource "junos_null_load_config" "port_speed" { + action = "set" + config = join("\n", flatten([ + for fpc in [0, 1] : [ + for p in range(0, var.server_lag_count, 4) : + "set chassis fpc ${fpc} pic 0 port ${p} speed 25g" ] - }] + ])) } diff --git a/tf/shared/modules/cluster-fabric/firewall.tf b/tf/shared/modules/cluster-fabric/firewall.tf index 84078c41..aaf65dee 100644 --- a/tf/shared/modules/cluster-fabric/firewall.tf +++ b/tf/shared/modules/cluster-fabric/firewall.tf @@ -1,25 +1,33 @@ -locals { - # Stop traffic routing between the cluster's public and private networks; the - # IRBs apply this as an input filter. Default term accepts everything else. - firewall_block = [{ - family = [{ inet = [{ filter = [{ - name = "NO-CROSS-VLAN" - term = [ - { - name = "block-public-to-private" - from = [{ source_address = [{ name = var.public_cidr }], destination_address = [{ name = var.private_cidr }] }] - then = [{ discard = [{}] }] - }, - { - name = "block-private-to-public" - from = [{ source_address = [{ name = var.private_cidr }], destination_address = [{ name = var.public_cidr }] }] - then = [{ discard = [{}] }] - }, - { - name = "default" - then = [{ accept = "" }] - }, - ] - }] }] }] - }] +# Stop traffic routing between the cluster's public and private networks; the IRBs +# apply this as an input filter. Default term accepts everything else. +resource "junos_firewall_filter" "no_cross_vlan" { + name = "NO-CROSS-VLAN" + family = "inet" + + term { + name = "block-public-to-private" + from { + source_address = [var.public_cidr] + destination_address = [var.private_cidr] + } + then { + action = "discard" + } + } + term { + name = "block-private-to-public" + from { + source_address = [var.private_cidr] + destination_address = [var.public_cidr] + } + then { + action = "discard" + } + } + term { + name = "default" + then { + action = "accept" + } + } } diff --git a/tf/shared/modules/cluster-fabric/interfaces.tf b/tf/shared/modules/cluster-fabric/interfaces.tf index 8bc9dfda..e6b16d7c 100644 --- a/tf/shared/modules/cluster-fabric/interfaces.tf +++ b/tf/shared/modules/cluster-fabric/interfaces.tf @@ -1,79 +1,73 @@ locals { - # Every server bond + the uplink carry the cluster's public/private plus the - # site-global api/mgmt VLANs. - trunk_members = [local.public_vlan_name, local.private_vlan_name, local.api_vlan_name, local.mgmt_vlan_name] - - # ── Server bonds: ae = et-0/0/ + et-1/0/, LACP, pub/priv trunk ── - server_lags = [ - for k in range(1, var.server_lag_count + 1) : { - name = "ae${k}" - aggregated_ether_options = [{ lacp = [{ active = "" }] }] - unit = [{ - name = 0 - family = [{ ethernet_switching = [{ interface_mode = "trunk", vlan = [{ members = local.trunk_members }] }] }] - }] - } + trunk_members = [ + "vlan${var.public_vlan_id}", "vlan${var.private_vlan_id}", + "vlan${var.api_vlan_id}", "vlan${var.mgmt_vlan_id}", ] - # Each bond's two physical members, one per VC member (fpc 0 and fpc 1). - server_members = flatten([ - for fpc in [0, 1] : [ - for p in range(var.server_lag_count) : { - name = "et-${fpc}/0/${p}" - ether_options = [{ ieee_802_3ad = [{ bundle = "ae${p + 1}" }] }] - } - ] - ]) + # Server bonds ae1..aeN, each a trunk of the cluster + site VLANs. + server_lag_names = toset([for k in range(1, var.server_lag_count + 1) : "ae${k}"]) - # ── Spine uplink bond ae0 (4x100G) ────────────────────────────────────────── - uplink_members = [ - for name in var.uplink_ports : { - name = name - ether_options = [{ ieee_802_3ad = [{ bundle = "ae0" }] }] + # Each bond's two members (one per VC member: fpc 0 and fpc 1) + the uplink's. + member_bundles = merge( + { for k in range(1, var.server_lag_count + 1) : "et-0/0/${k - 1}" => "ae${k}" }, + { for k in range(1, var.server_lag_count + 1) : "et-1/0/${k - 1}" => "ae${k}" }, + { for p in var.uplink_ports : p => "ae0" }, + ) +} + +# Physical members → their aggregate. +resource "junos_interface_physical" "member" { + for_each = local.member_bundles + name = each.key + ether_opts { + ae_8023ad = each.value + } +} + +# Server bonds (LACP trunks). +resource "junos_interface_physical" "server_lag" { + for_each = local.server_lag_names + name = each.value + parent_ether_opts { + lacp { + mode = "active" } - ] + } + trunk = true + vlan_members = local.trunk_members +} - uplink_lag = { - name = "ae0" - mtu = var.jumbo_mtu - aggregated_ether_options = [{ lacp = [{ active = "" }] }] - unit = [{ - name = 0 - family = [{ ethernet_switching = [{ - interface_mode = "trunk" - vlan = [{ members = local.trunk_members }] - storm_control = [{ profile_name = "default" }] - }] }] - }] +# Spine uplink bond (jumbo, storm-controlled). +resource "junos_interface_physical" "ae0" { + name = "ae0" + mtu = var.jumbo_mtu + parent_ether_opts { + lacp { + mode = "active" + } } + trunk = true + vlan_members = local.trunk_members + storm_control = "default" +} - # ── IRB gateways for the cluster networks (inter-VLAN filter applied) ──────── - irb = { - name = "irb" - unit = [ - { - name = var.public_vlan_id - family = [{ inet = [{ - filter = [{ input = [{ filter_name = "NO-CROSS-VLAN" }] }] - address = [{ name = "${var.public_gateway}/${var.prefixlen}" }] - }] }] - }, - { - name = var.private_vlan_id - family = [{ inet = [{ - filter = [{ input = [{ filter_name = "NO-CROSS-VLAN" }] }] - address = [{ name = "${var.private_gateway}/${var.prefixlen}" }] - }] }] - }, - ] +# IRB gateways for the cluster networks (inter-VLAN filter applied inbound). +resource "junos_interface_logical" "irb_public" { + name = "irb.${var.public_vlan_id}" + family_inet { + address { + cidr_ip = "${var.public_gateway}/${var.prefixlen}" + } + filter_input = "NO-CROSS-VLAN" } +} - interfaces_block = [{ - interface = concat( - local.server_members, - local.uplink_members, - [local.irb, local.uplink_lag], - local.server_lags, - ) - }] +resource "junos_interface_logical" "irb_private" { + name = "irb.${var.private_vlan_id}" + family_inet { + address { + cidr_ip = "${var.private_gateway}/${var.prefixlen}" + } + filter_input = "NO-CROSS-VLAN" + } } diff --git a/tf/shared/modules/cluster-fabric/main.tf b/tf/shared/modules/cluster-fabric/main.tf deleted file mode 100644 index 04cba05a..00000000 --- a/tf/shared/modules/cluster-fabric/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -# Assembles the single JTAF config resource for the cluster leaf VC from the -# generated sections (vlans.tf / interfaces.tf / firewall.tf / chassis.tf / -# system.tf). The provider is the cluster's aliased junos-qfx, passed by the stack. -resource "terraform-provider-junos-qfx" "cluster" { - resource_name = "fabric" - provider = junos-qfx - - chassis = local.chassis_block - interfaces = local.interfaces_block - forwarding_options = local.forwarding_options_block - firewall = local.firewall_block - protocols = local.protocols_block - virtual_chassis = local.virtual_chassis_block - vlans = local.vlans_block -} diff --git a/tf/shared/modules/cluster-fabric/system.tf b/tf/shared/modules/cluster-fabric/system.tf index d85ba84c..22b08259 100644 --- a/tf/shared/modules/cluster-fabric/system.tf +++ b/tf/shared/modules/cluster-fabric/system.tf @@ -1,25 +1,11 @@ -locals { - # Storm-control profile referenced by the uplink trunk. - forwarding_options_block = [{ - storm_control_profiles = [{ - name = "default" - all = [{ bandwidth_level = 10000 }] - }] - }] +# Storm-control profile referenced by the uplink trunk + LLDP on all interfaces. +resource "junos_forwardingoptions_storm_control_profile" "default" { + name = "default" + all { + bandwidth_level = 10000 + } +} - protocols_block = [{ - lldp = [{ interface = [{ name = "all" }] }] - }] - - # Preprovisioned VC from the member serials (member 0 + member 1). - virtual_chassis_block = [{ - preprovisioned = "" - member = [ - for i, serial in var.vc_member_serials : { - name = i - role = "routing-engine" - serial_number = serial - } - ] - }] +resource "junos_lldp_interface" "all" { + name = "all" } diff --git a/tf/shared/modules/cluster-fabric/variables.tf b/tf/shared/modules/cluster-fabric/variables.tf index 898049ae..38314f07 100644 --- a/tf/shared/modules/cluster-fabric/variables.tf +++ b/tf/shared/modules/cluster-fabric/variables.tf @@ -1,9 +1,9 @@ # cluster-fabric — the per-cluster leaf VC (a pair of leaves). Its config is # generated, not hand-written: the 48 server bonds + members, the public/private # VLANs + IRB gateways, the NO-CROSS-VLAN filter, and the spine uplink. Addressing -# comes from fabric-addressing; the leaf VC's aliased junos-qfx provider is passed -# in by the stack. Config split across vlans.tf / interfaces.tf / firewall.tf / -# chassis.tf / system.tf; main.tf assembles the single junos-qfx resource. +# comes from fabric-addressing; the leaf VC's aliased junos provider (jeremmfr) is +# passed in by the stack. Config is split across vlans.tf / interfaces.tf / +# firewall.tf / chassis.tf / system.tf as typed junos_* resources. # ── Addressing (from fabric-addressing) ───────────────────────────────────── variable "public_cidr" { @@ -70,12 +70,6 @@ variable "jumbo_mtu" { description = "MTU for the spine uplink (ae0)." } -variable "aggregated_device_count" { - type = number - default = 64 - description = "chassis aggregated-devices ethernet device-count." -} - variable "api_vlan_id" { type = number description = "Site-global API VLAN id (carried on every cluster)." diff --git a/tf/shared/modules/cluster-fabric/versions.tf b/tf/shared/modules/cluster-fabric/versions.tf index 1573c1b2..eb8ac5f0 100644 --- a/tf/shared/modules/cluster-fabric/versions.tf +++ b/tf/shared/modules/cluster-fabric/versions.tf @@ -1,11 +1,10 @@ terraform { required_version = "~> 1.11" required_providers { - junos-qfx = { - # JTAF-generated provider, vendored in tf/providers/terraform-provider-junos-qfx - # and supplied via dev_overrides / a filesystem mirror. The stack passes a - # configured (aliased) instance into this module. - source = "hashicorp/junos-qfx" + # Community Junos provider; the stack passes a configured (aliased) instance. + junos = { + source = "jeremmfr/junos" + version = "~> 2.19" } } } diff --git a/tf/shared/modules/cluster-fabric/vlans.tf b/tf/shared/modules/cluster-fabric/vlans.tf index 8b35b3fe..7ac38a8c 100644 --- a/tf/shared/modules/cluster-fabric/vlans.tf +++ b/tf/shared/modules/cluster-fabric/vlans.tf @@ -1,17 +1,16 @@ locals { - public_vlan_name = "vlan${var.public_vlan_id}" - private_vlan_name = "vlan${var.private_vlan_id}" - api_vlan_name = "vlan${var.api_vlan_id}" - mgmt_vlan_name = "vlan${var.mgmt_vlan_id}" + # Per-cluster networks carry an IRB gateway on the leaf; site-global are L2 only. + cluster_vlans = { + "vlan${var.public_vlan_id}" = { id = var.public_vlan_id, l3 = "irb.${var.public_vlan_id}" } + "vlan${var.private_vlan_id}" = { id = var.private_vlan_id, l3 = "irb.${var.private_vlan_id}" } + "vlan${var.api_vlan_id}" = { id = var.api_vlan_id, l3 = null } + "vlan${var.mgmt_vlan_id}" = { id = var.mgmt_vlan_id, l3 = null } + } +} - vlans_block = [{ - vlan = [ - # Per-cluster networks — gateways (IRB) live here on the leaf. - { name = local.public_vlan_name, vlan_id = var.public_vlan_id, l3_interface = "irb.${var.public_vlan_id}" }, - { name = local.private_vlan_name, vlan_id = var.private_vlan_id, l3_interface = "irb.${var.private_vlan_id}" }, - # Site-global networks — L2 only on the leaf (no IRB; gateway is elsewhere). - { name = local.api_vlan_name, vlan_id = var.api_vlan_id }, - { name = local.mgmt_vlan_name, vlan_id = var.mgmt_vlan_id }, - ] - }] +resource "junos_vlan" "this" { + for_each = local.cluster_vlans + name = each.key + vlan_id = tostring(each.value.id) + l3_interface = each.value.l3 } diff --git a/tf/shared/modules/core-fabric/.terraform.lock.hcl b/tf/shared/modules/core-fabric/.terraform.lock.hcl new file mode 100644 index 00000000..f9b28706 --- /dev/null +++ b/tf/shared/modules/core-fabric/.terraform.lock.hcl @@ -0,0 +1,18 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/jeremmfr/junos" { + version = "2.19.0" + constraints = "~> 2.19" + hashes = [ + "h1:s5wxKF9Zqrgra04dUQfvHIdNiUIyzC1YoUi983h/Omc=", + "zh:0133026a8e4187f54e6101c9faab426896328be804638daeaf1a22376d1d8805", + "zh:0532ba48ee37c814e18a76c714c0d0810fe688fe791989639b3bf5e43f7ceaff", + "zh:0fa82a384b25a58b65523e0ea4768fa1212b1f5cfc0c9379d31162454fedcc9d", + "zh:4e325ace584dac143bd770884eb1bbeba96aa59ac88c84cdf641bf4bdcab9857", + "zh:5e4d22b3792d20a6a38f27d8c712343b4c60a060967b18911148ba8de5a9be2c", + "zh:7a8cdb6452db79ae49e74a5a54bdc6298364720c3cbbdb674a1870468a04a5f1", + "zh:97b5a8fe55923c3b6c1d231e1c7f8e7df88ab653c0d234aacdfcc58b4bd3902a", + "zh:9cca035a71f1469113ec294ebd2634d9196ad7f4071c3ca1010016fbd547ba8f", + ] +} diff --git a/tf/shared/modules/core-fabric/chassis.tf b/tf/shared/modules/core-fabric/chassis.tf index d950ac73..1a739aab 100644 --- a/tf/shared/modules/core-fabric/chassis.tf +++ b/tf/shared/modules/core-fabric/chassis.tf @@ -1,12 +1,26 @@ -locals { - # 100G->4x25G breakout on the server-facing ports of both VC members. - chassis_block = [{ - aggregated_devices = [{ ethernet = [{ device_count = var.aggregated_device_count }] }] - fpc = [ - for fpc in [0, 1] : { - name = fpc - pic = [{ name = 0, port = [for p in var.breakout_ports : { name = p, channel_speed = var.breakout_speed }] }] - } +# Preprovisioned spine VC + the 100G->4x25G breakout. Breakout (channel-speed) +# has no typed jeremmfr resource, so it's pushed as raw set-config. The +# `aggregated-devices ethernet device-count` line is auto-managed by +# junos_interface_physical (computed from the ae interfaces) — not set here. +resource "junos_virtual_chassis" "spine" { + preprovisioned = true + + dynamic "member" { + for_each = var.vc_member_serials + content { + id = member.key + role = "routing-engine" + serial_number = member.value + } + } +} + +resource "junos_null_load_config" "breakout" { + action = "set" + config = join("\n", flatten([ + for fpc in [0, 1] : [ + for p in var.breakout_ports : + "set chassis fpc ${fpc} pic 0 port ${p} channel-speed ${var.breakout_speed}" ] - }] + ])) } diff --git a/tf/shared/modules/core-fabric/interfaces.tf b/tf/shared/modules/core-fabric/interfaces.tf index af347f6f..564ffc72 100644 --- a/tf/shared/modules/core-fabric/interfaces.tf +++ b/tf/shared/modules/core-fabric/interfaces.tf @@ -1,415 +1,87 @@ -# Spine interfaces are bespoke (breakout legs, spine<->leaf ae0, VCP, the mgmt-1 -# port et-1/0/3:0) — not a uniform pattern, so kept faithful here. VLAN members -# reference the generated vlan names. locals { - interfaces_block = [ - { - interface = concat([ - { - name = "et-0/0/0" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/1" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/2" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/3" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/4" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/5" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/6" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/7" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/8" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/9" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/10" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/11" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/12" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/13" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/14" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/15" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/16" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/17" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/18" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/19" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/20" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/21" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/22" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/23" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/24" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/25" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/26" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/27" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/28" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/29" - mtu = 9216 - unit = [ - { - name = 0 - } - ] - }, - { - name = "et-0/0/30" - ether_options = [ - { - ieee_802_3ad = [ - { - bundle = "ae0" - } - ] - } - ] - }, - { - name = "et-0/0/31" - ether_options = [ - { - ieee_802_3ad = [ - { - bundle = "ae0" - } - ] - } - ] - }, - { - name = "et-1/0/3:0" - unit = [ - { - name = 0 - family = [ - { - ethernet_switching = [ - { - interface_mode = "trunk" - vlan = [ - { - members = [ - "vlan${var.public_vlan_id}", - "vlan${var.private_vlan_id}", "vlan${var.api_vlan_id}", "vlan${var.mgmt_vlan_id}" - ] - } - ] - } - ] - } - ] - } - ] - }, - { - name = "et-1/0/30" - ether_options = [ - { - ieee_802_3ad = [ - { - bundle = "ae0" - } - ] - } - ] - }, - { - name = "et-1/0/31" - ether_options = [ - { - ieee_802_3ad = [ - { - bundle = "ae0" - } - ] - } - ] - }, - { - name = "ae0" - mtu = 9216 - aggregated_ether_options = [ - { - lacp = [ - { - active = "" - } - ] - } - ] - unit = [ - { - name = 0 - family = [ - { - ethernet_switching = [ - { - interface_mode = "trunk" - vlan = [ - { - members = [ - "vlan${var.public_vlan_id}", - "vlan${var.private_vlan_id}", "vlan${var.api_vlan_id}", "vlan${var.mgmt_vlan_id}" - ] - } - ] - storm_control = [ - { - profile_name = "default" - } - ] - } - ] - } - ] - } - ] - }, - { - name = "vme" - unit = [ - { - name = 0 - family = [ - { - inet = [ - { - address = [ - { - name = "10.40.5.115/24" - } - ] - } - ] - } - ] - } - ] - } - ], local.transit_interfaces, local.lo0_block) - } + trunk_members = [ + "vlan${var.public_vlan_id}", "vlan${var.private_vlan_id}", + "vlan${var.api_vlan_id}", "vlan${var.mgmt_vlan_id}", ] + + # Pre-staged jumbo access ports et-0/0/0..29 (physical mtu only; their empty + # unit 0, if any, is left untouched by the per-resource provider). + access_ports = toset([for i in range(30) : "et-0/0/${i}"]) + + # ae0 = spine<->leaf LAG members, two per VC member. + ae0_members = toset(["et-0/0/30", "et-0/0/31", "et-1/0/30", "et-1/0/31"]) +} + +resource "junos_interface_physical" "access" { + for_each = local.access_ports + name = each.value + mtu = 9216 +} + +resource "junos_interface_physical" "ae0_member" { + for_each = local.ae0_members + name = each.value + ether_opts { + ae_8023ad = "ae0" + } +} + +resource "junos_interface_physical" "ae0" { + name = "ae0" + mtu = 9216 + parent_ether_opts { + lacp { + mode = "active" + } + } + trunk = true + vlan_members = local.trunk_members + storm_control = "default" +} + +# mgmt-1 server port (channelized leg), trunk of the same VLANs. +resource "junos_interface_physical" "mgmt1_port" { + name = "et-1/0/3:0" + trunk = true + vlan_members = local.trunk_members +} + +# NOTE: vme (the mgmt IP / NETCONF lifeline) is deliberately NOT managed here — +# it's left untouched on the device, like the leaf's vme, so no apply can break the +# management path. + +# Transit uplink unit(s) — routed v4/v6 toward each upstream (et-0/0/27 etc.). +# The physical port's mtu comes from the access-port set above (et-0/0/0..29). +resource "junos_interface_logical" "transit" { + for_each = var.transits + name = "${each.value.interface}.0" + family_inet { + address { + cidr_ip = each.value.local_v4 + } + } + family_inet6 { + address { + cidr_ip = each.value.local_v6 + } + } +} + +# lo0: the advertised-space loopback host(s) + the RE-protection input filters. +# Only when transit is configured (the spine then has Internet adjacency). +resource "junos_interface_logical" "lo0" { + count = length(var.transits) > 0 ? 1 : 0 + name = "lo0.0" + family_inet { + dynamic "address" { + for_each = [for name, t in var.transits : t.loopback if t.loopback != null] + content { + cidr_ip = address.value + } + } + filter_input = "PROTECT-RE" + } + family_inet6 { + filter_input = "PROTECT-RE6" + } } diff --git a/tf/shared/modules/core-fabric/main.tf b/tf/shared/modules/core-fabric/main.tf deleted file mode 100644 index 9cf2e2ac..00000000 --- a/tf/shared/modules/core-fabric/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -# Assembles the single JTAF config resource for the spine VC from the generated -# sections (chassis.tf breakout, vlans.tf stretch, system.tf) and the bespoke -# interfaces (interfaces.tf). Provider = the spine's aliased junos-qfx. -resource "terraform-provider-junos-qfx" "core" { - resource_name = "fabric" - provider = junos-qfx - - chassis = local.chassis_block - firewall = local.firewall_block - interfaces = local.interfaces_block - forwarding_options = local.forwarding_options_block - routing_options = local.routing_options_block - protocols = local.protocols_block - policy_options = local.transit_policy_options - virtual_chassis = local.virtual_chassis_block - vlans = local.vlans_block -} diff --git a/tf/shared/modules/core-fabric/re-protect.tf b/tf/shared/modules/core-fabric/re-protect.tf index 4ee5e33c..bbe1fc86 100644 --- a/tf/shared/modules/core-fabric/re-protect.tf +++ b/tf/shared/modules/core-fabric/re-protect.tf @@ -1,79 +1,60 @@ -# Control-plane (RE) protection + the transit loopback(s). Generated when transit -# is configured (the spine then has Internet adjacency). The lo0 input filter -# restricts management (SSH 22 / NETCONF 830) to trusted sources and drops it from -# everywhere else (incl. the transit), while accepting all other RE-bound traffic -# (BGP, ICMP, VC control, return traffic, ...). On QFX, RE-bound filtering must be -# on lo0 — an ingress interface filter does NOT catch host-bound traffic. -locals { - _re_enabled = length(local.transits) > 0 - _loopbacks = [for name, t in local.transits : t.loopback if t.loopback != null] +# Control-plane (RE) protection filters, bound on lo0 (see interfaces.tf). Restrict +# management (SSH 22 / NETCONF 830) to trusted sources and drop it everywhere else +# (incl. the transit), while accepting all other RE-bound traffic. Generated when +# transit is configured (the spine then has Internet adjacency). +resource "junos_firewall_filter" "protect_re" { + count = length(var.transits) > 0 ? 1 : 0 + name = "PROTECT-RE" + family = "inet" - firewall_block = local._re_enabled ? [ - { - family = [ - { - inet = [ - { - filter = [ - { - name = "PROTECT-RE" - term = [ - { - name = "mgmt-trusted" - from = [{ - source_address = [for s in var.mgmt_trusted_sources : { name = s }] - protocol = ["tcp"] - destination_port = ["22", "830"] - }] - then = [{ accept = "" }] - }, - { - name = "mgmt-drop" - from = [{ protocol = ["tcp"], destination_port = ["22", "830"] }] - then = [{ discard = [{}] }] - }, - { name = "default", then = [{ accept = "" }] }, - ] - } - ] - } - ] - inet6 = [ - { - filter = [ - { - name = "PROTECT-RE6" - term = [ - { - name = "mgmt-drop" - from = [{ next_header = ["tcp"], destination_port = ["22", "830"] }] - then = [{ discard = "" }] - }, - { name = "default", then = [{ accept = "" }] }, - ] - } - ] - } - ] - } - ] + term { + name = "mgmt-trusted" + from { + source_address = var.mgmt_trusted_sources + protocol = ["tcp"] + destination_port = ["22", "830"] } - ] : [] - - # lo0: the advertised-space loopback host(s) + the RE filter applied inbound. - lo0_block = local._re_enabled ? [ - { - name = "lo0" - unit = [{ - name = 0 - family = [{ - inet = [{ - filter = [{ input = [{ filter_name = "PROTECT-RE" }] }] - address = [for lb in local._loopbacks : { name = lb }] - }] - inet6 = [{ filter = [{ input = [{ filter_name = "PROTECT-RE6" }] }] }] - }] - }] + then { + action = "accept" } - ] : [] + } + term { + name = "mgmt-drop" + from { + protocol = ["tcp"] + destination_port = ["22", "830"] + } + then { + action = "discard" + } + } + term { + name = "default" + then { + action = "accept" + } + } +} + +resource "junos_firewall_filter" "protect_re6" { + count = length(var.transits) > 0 ? 1 : 0 + name = "PROTECT-RE6" + family = "inet6" + + term { + name = "mgmt-drop" + from { + next_header = ["tcp"] + destination_port = ["22", "830"] + } + then { + action = "discard" + } + } + term { + name = "default" + then { + action = "accept" + } + } } diff --git a/tf/shared/modules/core-fabric/system.tf b/tf/shared/modules/core-fabric/system.tf index 801847a0..3d3faada 100644 --- a/tf/shared/modules/core-fabric/system.tf +++ b/tf/shared/modules/core-fabric/system.tf @@ -1,48 +1,12 @@ -locals { - forwarding_options_block = [ - { - storm_control_profiles = [ - { - name = "default" - all = [ - { - bandwidth_level = 10000 - } - ] - } - ] - } - ] +# Switch-wide bits: the storm-control profile referenced by the ae0 trunk, and +# LLDP on all interfaces. (system login + name-servers live in fabric-login.) +resource "junos_forwardingoptions_storm_control_profile" "default" { + name = "default" + all { + bandwidth_level = 10000 + } +} - # Routing-options come from the transit uplink (autonomous-system + the - # originated discard prefix). The old static OOB default was removed at cutover - # — the spine's default is now the eBGP default. Empty when no transit. - routing_options_block = local.transit_routing_options - - protocols_block = [ - { - lldp = [ - { - interface = [ - { - name = "all" - } - ] - } - ] - # eBGP transit group (v4 + v6), or null when no transit configured. - bgp = local.transit_bgp - } - ] - - virtual_chassis_block = [{ - preprovisioned = "" - member = [ - for i, serial in var.vc_member_serials : { - name = i - role = "routing-engine" - serial_number = serial - } - ] - }] +resource "junos_lldp_interface" "all" { + name = "all" } diff --git a/tf/shared/modules/core-fabric/transit.tf b/tf/shared/modules/core-fabric/transit.tf index c6f85a2e..4f49f570 100644 --- a/tf/shared/modules/core-fabric/transit.tf +++ b/tf/shared/modules/core-fabric/transit.tf @@ -1,100 +1,133 @@ # Upstream IP-transit eBGP uplinks on the spine (e.g. Core-Backbone), supporting -# MULTIPLE transits for multi-homing: -# • each transit is its own eBGP group (v4 + v6 neighbors) on its own uplink. -# • `prepend` (export): advertise our prefix with our AS prepended N times, so -# the Internet prefers the transit(s) with prepend 0 for INBOUND to our prefix. -# • `local_pref` (import): local-preference applied to the received default, so -# the highest-local_pref transit is our OUTBOUND default route. -# -# var.transits is keyed by name (the bgp group name; policies derive as -# -OUT / -IN). Import is DEFAULT-ONLY (the QFX FIB can't hold full). -# -# NOTE (JTAF): `as-path-prepend` and `local-preference` must exist in the provider -# schema to be applied — i.e. configured on a device + `fabric:provider-gen` rerun. -# A primary with prepend=0 / local_pref=null emits neither (schema-safe today); -# add a prepended/local-pref'd backup => apply it once + regen, then it's in TF. +# MULTIPLE transits for multi-homing. Each transit is its own external BGP group +# (v4 + v6 neighbors); import is DEFAULT-ONLY (the QFX FIB can't hold a full table). +# • prepend (export): advertise our prefix with our AS prepended N times. +# • local_pref (import): local-preference on the received default (highest = the +# outbound default route). +# Policies derive as -OUT / -IN. + locals { - transits = var.transits + advertised = toset([for name, t in var.transits : t.advertise]) +} - # One uplink interface per transit. - transit_interfaces = [ - for name, t in local.transits : { - name = t.interface - mtu = 9216 - unit = [{ - name = 0 - family = [{ - inet = [{ address = [{ name = t.local_v4 }] }] - inet6 = [{ address = [{ name = t.local_v6 }] }] - }] - }] +resource "junos_routing_options" "this" { + count = var.local_as == null ? 0 : 1 + clean_on_destroy = true + autonomous_system { + number = tostring(var.local_as) + } +} + +# Originate each advertised prefix via a discard route (redistributed by -OUT). +resource "junos_static_route" "advertise" { + for_each = local.advertised + destination = each.value + discard = true +} + +resource "junos_bgp_group" "transit" { + for_each = var.transits + name = each.key + type = "external" + peer_as = tostring(each.value.peer_as) +} + +# import/export live on the neighbor (matches the device), not the group. +resource "junos_bgp_neighbor" "v4" { + for_each = var.transits + group = junos_bgp_group.transit[each.key].name + ip = each.value.peer_v4 + import = ["${upper(each.key)}-IN"] + export = ["${upper(each.key)}-OUT"] + family_inet { + nlri_type = "unicast" + } +} + +resource "junos_bgp_neighbor" "v6" { + for_each = var.transits + group = junos_bgp_group.transit[each.key].name + ip = each.value.peer_v6 + import = ["${upper(each.key)}-IN"] + export = ["${upper(each.key)}-OUT"] + family_inet6 { + nlri_type = "unicast" + } +} + +# OUT: advertise our prefix (prepend N times on backups), reject everything else. +resource "junos_policyoptions_policy_statement" "out" { + for_each = var.transits + name = "${upper(each.key)}-OUT" + + term { + name = "our-prefix" + from { + route_filter { + route = each.value.advertise + option = "exact" + } + } + then { + action = "accept" + as_path_prepend = each.value.prepend > 0 ? join(" ", [for i in range(each.value.prepend) : tostring(var.local_as)]) : null } - ] + } + term { + name = "reject-rest" + then { + action = "reject" + } + } +} + +# IN: accept only the default(s) (v4 + v6), local-pref the primary highest. +resource "junos_policyoptions_policy_statement" "in" { + for_each = var.transits + name = "${upper(each.key)}-IN" - # routing-options: our AS (once) + one discard per unique advertised prefix. - _advertised = distinct([for name, t in local.transits : t.advertise]) - transit_routing_options = var.local_as == null ? [] : [ - { - autonomous_system = [{ as_number = tostring(var.local_as) }] - static = [{ route = [for p in local._advertised : { name = p, discard = "" }] }] + term { + name = "default4" + from { + route_filter { + route = "0.0.0.0/0" + option = "exact" + } + } + then { + action = "accept" + dynamic "local_preference" { + for_each = each.value.local_pref == null ? [] : [each.value.local_pref] + content { + action = "none" + value = tostring(local_preference.value) + } + } } - ] - - # One eBGP group per transit, each with a v4 + v6 neighbor. - transit_bgp = length(local.transits) == 0 ? null : [ - { - group = [ - for name, t in local.transits : { - name = name - type = "external" - peer_as = tostring(t.peer_as) - neighbor = [ - { name = t.peer_v4, family = [{ inet = [{ unicast = [{}] }] }], import = ["${upper(name)}-IN"], export = ["${upper(name)}-OUT"] }, - { name = t.peer_v6, family = [{ inet6 = [{ unicast = [{}] }] }], import = ["${upper(name)}-IN"], export = ["${upper(name)}-OUT"] }, - ] + } + term { + name = "default6" + from { + route_filter { + route = "::/0" + option = "exact" + } + } + then { + action = "accept" + dynamic "local_preference" { + for_each = each.value.local_pref == null ? [] : [each.value.local_pref] + content { + action = "none" + value = tostring(local_preference.value) } - ] + } } - ] - - # Per-transit OUT (advertise our prefix, prepend N) + IN (default-only, local-pref). - # merge() keeps the `then` schema-safe: prepend 0 / local_pref null emit nothing extra. - transit_policy_options = length(local.transits) == 0 ? [] : [ - { - policy_statement = flatten([ - for name, t in local.transits : [ - { - name = "${upper(name)}-OUT" - term = [ - { - name = "our-prefix" - from = [{ route_filter = [{ address = t.advertise, exact = "" }] }] - then = [merge( - { accept = "" }, - t.prepend > 0 ? { as_path_prepend = join(" ", [for i in range(t.prepend) : tostring(var.local_as)]) } : {}, - )] - }, - { name = "reject-rest", then = [{ reject = "" }] }, - ] - }, - { - name = "${upper(name)}-IN" - term = [ - { - name = "default4" - from = [{ route_filter = [{ address = "0.0.0.0/0", exact = "" }] }] - then = [merge({ accept = "" }, t.local_pref == null ? {} : { local_preference = tostring(t.local_pref) })] - }, - { - name = "default6" - from = [{ route_filter = [{ address = "::/0", exact = "" }] }] - then = [merge({ accept = "" }, t.local_pref == null ? {} : { local_preference = tostring(t.local_pref) })] - }, - { name = "reject-rest", then = [{ reject = "" }] }, - ] - }, - ] - ]) + } + term { + name = "reject-rest" + then { + action = "reject" } - ] + } } diff --git a/tf/shared/modules/core-fabric/variables.tf b/tf/shared/modules/core-fabric/variables.tf index cae8d89e..202d3fd9 100644 --- a/tf/shared/modules/core-fabric/variables.tf +++ b/tf/shared/modules/core-fabric/variables.tf @@ -5,7 +5,7 @@ # NOTE: the spine is shared across all clusters. Today it carries cluster 1's # VLANs; as clusters are added, extend the vlans/trunk membership here (the # vlan ids below are wired to cluster 1's addressing). The configured (aliased) -# junos-qfx provider for the spine VC is passed in by the stack. +# junos provider for the spine VC is passed in by the stack. variable "public_vlan_id" { type = number @@ -38,12 +38,6 @@ variable "breakout_speed" { description = "Per-channel speed for the breakout ports." } -variable "aggregated_device_count" { - type = number - default = 16 - description = "chassis aggregated-devices ethernet device-count." -} - variable "api_vlan_id" { type = number description = "Site-global API VLAN id to stretch (carried on all clusters)." @@ -72,7 +66,7 @@ variable "transits" { name; policies derive as -OUT/-IN). Multi-home by adding entries: `prepend` (0 = primary) AS-path-prepends our advertisement on backups; `local_pref` (highest = the outbound default route). Import is default-only. - See transit.tf — prepend/local_pref need a provider regen (JTAF) to apply. + See transit.tf — prepend maps to as_path_prepend, local_pref to local_preference. EOT type = map(object({ interface = string # uplink port (e.g. et-0/0/27) diff --git a/tf/shared/modules/core-fabric/versions.tf b/tf/shared/modules/core-fabric/versions.tf index 7b5b36b4..eb8ac5f0 100644 --- a/tf/shared/modules/core-fabric/versions.tf +++ b/tf/shared/modules/core-fabric/versions.tf @@ -1,8 +1,10 @@ terraform { required_version = "~> 1.11" required_providers { - junos-qfx = { - source = "hashicorp/junos-qfx" + # Community Junos provider; the stack passes a configured (aliased) instance. + junos = { + source = "jeremmfr/junos" + version = "~> 2.19" } } } diff --git a/tf/shared/modules/core-fabric/vlans.tf b/tf/shared/modules/core-fabric/vlans.tf index da5a0af8..f0f48afc 100644 --- a/tf/shared/modules/core-fabric/vlans.tf +++ b/tf/shared/modules/core-fabric/vlans.tf @@ -1,12 +1,15 @@ +# Stretched VLANs (L2 only on the spine — gateways live on the leaves / elsewhere). locals { - # Stretched VLANs (L2 only on the spine — gateways live on the leaves / elsewhere). - # Per-cluster public/private + the site-global api/mgmt. - vlans_block = [{ - vlan = [ - { name = "vlan${var.public_vlan_id}", vlan_id = var.public_vlan_id }, - { name = "vlan${var.private_vlan_id}", vlan_id = var.private_vlan_id }, - { name = "vlan${var.api_vlan_id}", vlan_id = var.api_vlan_id }, - { name = "vlan${var.mgmt_vlan_id}", vlan_id = var.mgmt_vlan_id }, - ] - }] + spine_vlans = { + "vlan${var.public_vlan_id}" = var.public_vlan_id + "vlan${var.private_vlan_id}" = var.private_vlan_id + "vlan${var.api_vlan_id}" = var.api_vlan_id + "vlan${var.mgmt_vlan_id}" = var.mgmt_vlan_id + } +} + +resource "junos_vlan" "this" { + for_each = local.spine_vlans + name = each.key + vlan_id = tostring(each.value) } diff --git a/tf/shared/modules/fabric-login/.terraform.lock.hcl b/tf/shared/modules/fabric-login/.terraform.lock.hcl new file mode 100644 index 00000000..f9b28706 --- /dev/null +++ b/tf/shared/modules/fabric-login/.terraform.lock.hcl @@ -0,0 +1,18 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/jeremmfr/junos" { + version = "2.19.0" + constraints = "~> 2.19" + hashes = [ + "h1:s5wxKF9Zqrgra04dUQfvHIdNiUIyzC1YoUi983h/Omc=", + "zh:0133026a8e4187f54e6101c9faab426896328be804638daeaf1a22376d1d8805", + "zh:0532ba48ee37c814e18a76c714c0d0810fe688fe791989639b3bf5e43f7ceaff", + "zh:0fa82a384b25a58b65523e0ea4768fa1212b1f5cfc0c9379d31162454fedcc9d", + "zh:4e325ace584dac143bd770884eb1bbeba96aa59ac88c84cdf641bf4bdcab9857", + "zh:5e4d22b3792d20a6a38f27d8c712343b4c60a060967b18911148ba8de5a9be2c", + "zh:7a8cdb6452db79ae49e74a5a54bdc6298364720c3cbbdb674a1870468a04a5f1", + "zh:97b5a8fe55923c3b6c1d231e1c7f8e7df88ab653c0d234aacdfcc58b4bd3902a", + "zh:9cca035a71f1469113ec294ebd2634d9196ad7f4071c3ca1010016fbd547ba8f", + ] +} diff --git a/tf/shared/modules/fabric-login/main.tf b/tf/shared/modules/fabric-login/main.tf index 009092c5..9e307796 100644 --- a/tf/shared/modules/fabric-login/main.tf +++ b/tf/shared/modules/fabric-login/main.tf @@ -1,44 +1,43 @@ +# Declaratively manage Junos login users + classes (and the default name-servers, +# which live here so a single resource owns the `system` slice). One resource per +# user/class. Public SSH keys are committed; passwords never are. locals { - # Build each user, including only the sub-blocks that have content (the JTAF - # schema expects lists; empty key-type blocks are omitted via merge()). - users = [ - for name, u in var.users : merge( - { - name = name - class = u.class - authentication = [ - merge( - length(u.ssh_ed25519_keys) == 0 ? {} : { ssh_ed25519 = [for k in u.ssh_ed25519_keys : { name = k }] }, - length(u.ssh_rsa_keys) == 0 ? {} : { ssh_rsa = [for k in u.ssh_rsa_keys : { name = k }] }, - ) - ] - }, - u.uid == null ? {} : { uid = u.uid }, - u.full_name == null ? {} : { full_name = u.full_name }, - ) - ] - - classes = [for name, c in var.classes : { name = name, permissions = c.permissions }] - - login = [ - merge( - { user = local.users }, - length(local.classes) == 0 ? {} : { class = local.classes }, - ) - ] + user_keys = { + for name, u in var.users : name => concat(u.ssh_ed25519_keys, u.ssh_rsa_keys) + } } -resource "terraform-provider-junos-qfx" "login" { - resource_name = var.resource_name - provider = junos-qfx - - # This resource owns the whole `system` container slice: login + name-servers. - # Splitting `system` children across resources makes each one delete the other's - # (the provider diffs its managed slice against the live device). - system = [ - merge( - { login = local.login }, - length(var.name_servers) == 0 ? {} : { name_server = [for ns in var.name_servers : { name = ns }] }, - ) - ] +resource "junos_system_login_class" "this" { + for_each = var.classes + name = each.key + permissions = each.value.permissions +} + +resource "junos_system_login_user" "this" { + for_each = var.users + name = each.key + class = each.value.class + uid = each.value.uid + full_name = each.value.full_name + + dynamic "authentication" { + for_each = length(local.user_keys[each.key]) > 0 ? [1] : [] + content { + ssh_public_keys = local.user_keys[each.key] + } + } + + # A user's class may be one of the synthesized custom classes. + depends_on = [junos_system_login_class.this] +} + +# Default DNS resolvers, pushed as additive raw set-config. NOT junos_system: that +# is a singleton owning the whole `system` block (incl. `services netconf/ssh` and +# `ssh root-login`), so setting only name-server would STRIP the management services +# and lock us out. null_load_config only ever `set`s — it cannot remove services. +# (Trade-off: removing a resolver later needs a manual delete; fine for DNS.) +resource "junos_null_load_config" "name_servers" { + count = length(var.name_servers) == 0 ? 0 : 1 + action = "set" + config = join("\n", [for ns in var.name_servers : "set system name-server ${ns}"]) } diff --git a/tf/shared/modules/fabric-login/variables.tf b/tf/shared/modules/fabric-login/variables.tf index 57e71688..c4f253e5 100644 --- a/tf/shared/modules/fabric-login/variables.tf +++ b/tf/shared/modules/fabric-login/variables.tf @@ -1,7 +1,7 @@ # fabric-login — declaratively manage Junos login users + their SSH keys and # rights on a switch VC. Public keys are NOT secret and live in version control; # any passwords come from variables sourced from 1Password (never committed). -# Instantiated once per VC with that VC's aliased junos-qfx provider. +# Instantiated once per VC with that VC's aliased junos provider. variable "users" { description = "Login users keyed by username. Public SSH keys are committed; rights via `class`." @@ -22,12 +22,6 @@ variable "classes" { default = {} } -variable "resource_name" { - type = string - default = "login" - description = "JTAF resource/apply-group name for this login slice." -} - variable "name_servers" { type = list(string) default = [] diff --git a/tf/shared/modules/fabric-login/versions.tf b/tf/shared/modules/fabric-login/versions.tf index 7b5b36b4..eb8ac5f0 100644 --- a/tf/shared/modules/fabric-login/versions.tf +++ b/tf/shared/modules/fabric-login/versions.tf @@ -1,8 +1,10 @@ terraform { required_version = "~> 1.11" required_providers { - junos-qfx = { - source = "hashicorp/junos-qfx" + # Community Junos provider; the stack passes a configured (aliased) instance. + junos = { + source = "jeremmfr/junos" + version = "~> 2.19" } } }