Terraform で ACI 上に L3out (OSPF) を含む Tenant を作成する

Terraform を使って Cisco ACI に「OSPF 設定の L3out を作成する」サンプルをメモしておきます。

構成

Terraform で以下の構成を設定します。

file

Terraform の設定ファイル

Terraform の設定ファイルは以下の通りです。

  1. variables.tf
  2. main.tf
  3. aci.tf

variables.tf

variable "aci_url" { default = "https://10.0.0.1" }
variable "aci_username" { default = "admin" }
variable "aci_password" { default = "password" }
variable "tenant" { default = "Tenant1" }
variable "vrf" { default = "VRF" }
variable "contract1" { default = "CT" }

variable "l3dom" { default = "ExtRoutedDom" }
variable "l3out1" { default = "L3out" }
variable "l3out1_ospf_area" { default = "0.0.0.0" }
variable "l3out1_lnode1_tdn" { default = "topology/pod-1/node-201" }
variable "l3out1_lnode1_rtr_id" { default = "10.0.254.201" }
variable "l3out1_lnode1_rtr_id_loop_back" { default = "no" }
variable "l3out1_att_addr" { default = "10.0.100.254/24" }
variable "l3out1_att_autostate" { default = "enabled" }
variable "l3out1_att_encap" { default = "vlan-100" }
variable "l3out1_att_ifInstT" { default = "ext-svi" }
variable "l3out1_att_mtu" { default = "1500" }
variable "l3out1_att_tDn" { default = "topology/pod-1/paths-201/pathep-[eth1/1]" }

variable "bd1" { default = "VL101_BD" }
variable "bd1_subnet" { default = "10.0.101.254/24" }
variable "ap1" { default = "AP" }
variable "physdom" { default = "PhysDom" }
variable "epg1" { default = "VL101_EPG" }
variable "egp1_port1_tdn" { default = "topology/pod-1/paths-201/pathep-[eth1/2]" }
variable "egp1_port1_vlan" { default = "vlan-101" }
variable "epg1_contract1_type" { default = "provider" }

main.tf

terraform {
  required_providers {
    aci = {
      source  = "CiscoDevNet/aci"
      version = "0.5.4"
    }
  }
}

provider "aci" {
  username = var.aci_username
  password = var.aci_password
  url      = var.aci_url
  insecure = true
}

aci.tf

現時点の Terraform には Logical Interface を定義する Resource が有りませんが、同様に L3out に関する OSPF 設定も Resource が存在しません (※ OSPF Interface Policy は存在します)。 その為、L3out へ OSPF 設定を関連付ける部分は aci_rest を用いて設定します。

# Tenant
resource "aci_tenant" "tenant" {
  name = var.tenant
}

# VRF
resource "aci_vrf" "vrf" {
  tenant_dn = aci_tenant.tenant.id
  name      = var.vrf
}

# Contract / Subject / Filter
resource "aci_filter" "any" {
    tenant_dn = aci_tenant.tenant.id
    name      = "any_Filt"
}

resource "aci_filter_entry" "entry1" {
    name        = "0010"
    filter_dn   = aci_filter.any.id
    ether_t     = "unspecified"
}

resource "aci_contract" "contract1" {
    tenant_dn = aci_tenant.tenant.id
    name      = var.contract1
}

resource "aci_contract_subject" "subject1" {
    contract_dn                  = aci_contract.contract1.id
    name                         = "${var.contract1}_Subj"
    relation_vz_rs_subj_filt_att = [aci_filter.any.id]
}

# OSPF Interface Policy
resource "aci_ospf_interface_policy" "ospf_if" {
  tenant_dn    = aci_tenant.tenant.id
  name         = "Point-to-Point"
  cost         = "unspecified"
  nw_t         = "p2p"
  prio         = "1"
  #ctrl         = ""
  pfx_suppress = "inherit"
  hello_intvl  = "10"
  dead_intvl   = "40"
  rexmit_intvl = "5"
  xmit_delay   = "1"
}

# Domain
data "aci_l3_domain_profile" "l3dom" {
  name = var.l3dom
}

# L3Out
resource "aci_l3_outside" "l3out1" {
  tenant_dn                    = aci_tenant.tenant.id
  name                         = var.l3out1
  relation_l3ext_rs_ectx       = aci_vrf.vrf.id
  relation_l3ext_rs_l3_dom_att = data.aci_l3_domain_profile.l3dom.id
}

resource "aci_rest" "l3out1_ospf" {
  path       = "/api/mo/uni/tn-${var.tenant}/out-${aci_l3_outside.l3out1.name}.json"
  class_name = "ospfExtP"
  content = {
    "areaType" = "regular"
    "areaCost" = "1"
    "areaId"   = var.l3out1_ospf_area
    "areaCtrl" = "redistribute,summary"
  }
  depends_on = [aci_l3_outside.l3out1]
}

resource "aci_logical_node_profile" "l3out1_lnprof1" {
  l3_outside_dn = aci_l3_outside.l3out1.id
  name          = "${var.l3out1}_NodeProf"
}

resource "aci_logical_node_to_fabric_node" "l3out1_lnode1" {
  logical_node_profile_dn  = aci_logical_node_profile.l3out1_lnprof1.id
  tdn                      = var.l3out1_lnode1_tdn
  rtr_id                   = var.l3out1_lnode1_rtr_id
  rtr_id_loop_back         = var.l3out1_lnode1_rtr_id_loop_back
}

resource "aci_logical_interface_profile" "l3out1_lifprof1" {
  logical_node_profile_dn           = aci_logical_node_profile.l3out1_lnprof1.id
  name                              = "${var.l3out1}_IntProf"
  relation_l3ext_rs_path_l3_out_att = toset([var.l3out1_att_tDn])
}

resource "aci_rest" "l3out1_att" {
  path       = "/api/mo/uni/tn-${var.tenant}/out-${aci_l3_outside.l3out1.name}/lnodep-${aci_logical_node_profile.l3out1_lnprof1.name}/lifp-${aci_logical_interface_profile.l3out1_lifprof1.name}.json"
  class_name = "l3extRsPathL3OutAtt"
  content = {
    "addr"      = var.l3out1_att_addr
    "autostate" = var.l3out1_att_autostate
    "encap"     = var.l3out1_att_encap
    "ifInstT"   = var.l3out1_att_ifInstT
    "mtu"       = var.l3out1_att_mtu
    "tDn"       = var.l3out1_att_tDn
  }
  depends_on = [aci_logical_interface_profile.l3out1_lifprof1]
}

resource "aci_rest" "l3out1_att_ospf" {
  path       = "/api/mo/uni/tn-${var.tenant}/out-${aci_l3_outside.l3out1.name}/lnodep-${aci_logical_node_profile.l3out1_lnprof1.name}/lifp-${aci_logical_interface_profile.l3out1_lifprof1.name}.json"
  class_name = "ospfIfP"
  content = {
    "authKeyId" = "1"
    "authType"  = "none"
  }
  depends_on = [aci_rest.l3out1_att]
}

resource "aci_rest" "l3out1_att_ospfpol" {
  path       = "/api/mo/uni/tn-${var.tenant}/out-${aci_l3_outside.l3out1.name}/lnodep-${aci_logical_node_profile.l3out1_lnprof1.name}/lifp-${aci_logical_interface_profile.l3out1_lifprof1.name}/ospfIfP.json"
  class_name = "ospfRsIfPol"
  content = {
    "tnOspfIfPolName" = aci_ospf_interface_policy.ospf_if.name
  }
  depends_on = [aci_rest.l3out1_att_ospf]
}

# L3Out1 External EPG
resource "aci_external_network_instance_profile" "l3out1_epg1" {
  l3_outside_dn       = aci_l3_outside.l3out1.id
  name                = "${var.l3out1}_L3PEG"
  relation_fv_rs_cons = [aci_contract.contract1.id]
}

resource "aci_l3_ext_subnet" "l3out1_subnet1" {
  external_network_instance_profile_dn = aci_external_network_instance_profile.l3out1_epg1.id
  ip                                   = "0.0.0.0/0"
  scope                                = ["import-security"]
}

# BD1
resource "aci_bridge_domain" "bd1" {
  tenant_dn                = aci_tenant.tenant.id
  name                     = var.bd1
  relation_fv_rs_ctx       = aci_vrf.vrf.id
  relation_fv_rs_bd_to_out = [aci_l3_outside.l3out1.id]
}

resource "aci_subnet" "bd1_subnet" {
  parent_dn = aci_bridge_domain.bd1.id
  ip        = var.bd1_subnet
  scope     = ["public"]
}

# Application Profile
resource "aci_application_profile" "ap1" {
  tenant_dn = aci_tenant.tenant.id
  name      = var.ap1
}

# Domain
data "aci_physical_domain" "physdom" {
  name = var.physdom
}

# EPG1
resource "aci_application_epg" "epg1" {
  application_profile_dn = aci_application_profile.ap1.id
  name                   = var.epg1
  relation_fv_rs_bd      = aci_bridge_domain.bd1.id
}

resource "aci_epg_to_domain" "epg1_physdom" {
  application_epg_dn = aci_application_epg.epg1.id
  tdn                = data.aci_physical_domain.physdom.id
}

resource "aci_epg_to_static_path" "egp1_port1" {
  application_epg_dn = aci_application_epg.epg1.id
  tdn                = var.egp1_port1_tdn
  encap              = var.egp1_port1_vlan
}

resource "aci_epg_to_contract" "epg1_contract1" {
    application_epg_dn = aci_application_epg.epg1.id
    contract_dn        = aci_contract.contract1.id
    contract_type      = var.epg1_contract1_type
}

コメント

タイトルとURLをコピーしました