Octet

Automation and SDN

0 of 39 nailed

Core16

  1. Why would you automate a network, and what would you automate first?

    Say this first

    Automation gives me consistency, speed and a record of every change. The same template produces the same config on 500 switches, a change that took a week of SSH sessions takes minutes, and every change is reviewed in Git before it runs. I start with read-only work (config backups, inventory, compliance checks), then move to small, repetitive, low-risk changes such as VLANs, NTP, SNMP and interface descriptions.

    Go deeper

    What it fixes

    • Human error. Typing the same 40 lines on 200 devices guarantees a typo somewhere. A template does not get tired.
    • Drift. Hand-made changes slowly make every device a little different. Automation lets you compare every device against one intended state.
    • Speed and scale. The team stops being the bottleneck for routine requests.
    • Audit and compliance. Changes are in Git with an author, a reviewer and a ticket number.
    • Faster troubleshooting. A script collects show output from 300 devices in a minute during an incident.

    A sensible order

    1. Read-only. Nightly backups, inventory, "which devices still run this old image", compliance reports. Nothing can break, and the team learns the tools.
    2. Standard config. NTP, syslog, SNMP, AAA, banners. Low risk, high repetition.
    3. Service requests. VLANs, port configuration, ACL entries, driven from a source of truth.
    4. Full provisioning and closed loop. Day 0 builds from templates, then continuous checks that the network still matches intent.

    What I would say about risk

    Automation also multiplies mistakes. A bad template reaches every device as fast as a good one. So the process matters as much as the tool: peer review, a dry run with a diff, a canary device first, and a rollback plan.

    Traps

    • Saying automation means fewer engineers. Interviewers want to hear that it moves engineers to higher-value work.
    • Starting with large config pushes. Read-only wins come first and build trust.
    How did you do?Book 1.2 RequirementsOwn page
  2. Explain the management, control and data planes with examples.

    Say this first

    The data plane forwards transit traffic using tables that are already built: FIB, adjacency, ACLs, QoS, usually in hardware. The control plane builds those tables: OSPF, BGP, STP, ARP, LACP running on the CPU. The management plane is how people and tools operate the box: SSH, SNMP, NETCONF, syslog, the API.

    Go deeper

    Side by side

    PlaneJobExamplesRuns on
    Data (forwarding)Move packets that pass through the deviceFIB lookup, MAC table lookup, ACL match, NAT, QoS markingASICs or the fast software path
    ControlDecide where packets should go and build the tablesOSPF, BGP, IS-IS, STP, ARP, LACP, LLDPRoute processor CPU
    ManagementConfigure and monitor the deviceSSH, SNMP, NETCONF, RESTCONF, syslog, NTPRoute processor CPU

    Why the split matters

    • Protection. The CPU is small compared to the ASICs. A flood of traffic aimed at the router itself (punted traffic) can starve OSPF and BGP. Control plane policing (CoPP) rate limits what reaches the CPU.
    • SDN. Classic SDN moves the control plane off the box into a central controller. Most modern controllers keep a distributed control plane on the devices and centralize policy and management instead.
    • Resilience. With NSF/SSO, the data plane keeps forwarding on the old FIB while the control plane restarts.

    Quick test

    A ping through R1 is data plane. A ping to R1 is punted to the CPU and answered there. An OSPF hello is control plane. An SSH login is management plane.

    Traps

    • Calling ARP or STP data plane. They build forwarding state, so they are control plane.
    • Forgetting that management traffic and control traffic both hit the same CPU, which is why CoPP exists.
    How did you do?Book 3.5 ImplementationOwn page
  3. What is SDN, and how is what vendors sell today different from the original idea?

    Say this first

    SDN separates the decision making about traffic from the devices that forward it and puts that logic in software with open APIs. The original idea (OpenFlow, around 2008) moved the whole control plane to a central controller that programmed dumb switches flow by flow. Today's products, such as Cisco ACI, SD-Access and SD-WAN, keep routing protocols on the devices and centralize policy, automation and visibility in a controller you drive through a REST API.

    Go deeper

    Three flavors you will hear about

    • Open SDN. A controller programs flow tables in switches with OpenFlow. Logic is fully central.
    • SDN through APIs. Devices keep their own control plane. A controller or script configures them through NETCONF, RESTCONF, gNMI or vendor APIs.
    • Overlay SDN. A virtual network runs on top of an IP underlay with VXLAN or similar tunnels. Hypervisor switches or leaf VTEPs build the tunnels, and the physical network just routes IP.

    What a modern controller still centralizes

    • One place to declare policy (who may talk to whom).
    • Automation of device config from that policy.
    • A network-wide view for assurance and troubleshooting.
    • A northbound REST API so other systems can drive the network.

    What stays distributed

    In ACI the fabric still runs IS-IS in the underlay, COOP on the spines for endpoint mapping and MP-BGP for external routes. If every APIC goes down, the fabric keeps forwarding. You just cannot change policy.

    Traps

    • Saying SDN means OpenFlow. OpenFlow is one southbound protocol; most SDN products today do not use it.
    • Claiming the controller forwards traffic. In nearly all products it never sits in the data path.
  4. What is the difference between northbound and southbound APIs?

    Say this first

    Northbound APIs face up, from the controller to applications, orchestrators and scripts. They are usually REST with JSON and speak in business terms like sites, policies and devices. Southbound APIs face down, from the controller to the network devices: OpenFlow, NETCONF, RESTCONF, gNMI, SNMP, or plain SSH and CLI.

    Go deeper

    Picture it as a stack

    Output
    ITSM, orchestrator, your Python script
            |  northbound: REST API, JSON, webhooks
        Controller (Catalyst Center, APIC, SD-WAN Manager)
            |  southbound: NETCONF, RESTCONF, gNMI, SNMP, SSH, OpenFlow, OpFlex
        Switches, routers, firewalls, APs

    Examples

    ProductNorthboundSouthbound
    Cisco Catalyst CenterIntent API (REST), webhooks for eventsSSH and CLI, SNMP, NETCONF
    Cisco ACI APICREST API over the object model (JSON or XML)Policy pushed to the Nexus 9000 fabric; Cisco describes OpFlex as the policy protocol
    OpenDaylight, ONOSREST APIsOpenFlow, NETCONF, BGP-LS, PCEP

    East-west

    Some people also name an east-west interface: controllers talking to each other in a cluster or between domains, for example to sync state or federate policy.

    Traps

    • Mixing up the directions. North is toward apps and people, south is toward devices.
    How did you do?Book 3.5 ImplementationOwn page
  5. What is a REST API? What makes an API RESTful?

    Say this first

    REST is an architectural style, not a protocol. A RESTful API exposes resources at URIs and works on them with standard HTTP methods, sends representations such as JSON, and is stateless: every request carries everything the server needs, including authentication. Responses use HTTP status codes and can be cached when marked cacheable.

    Go deeper

    The constraints (Roy Fielding, 2000)

    • Client-server. The client and server evolve separately.
    • Stateless. No session context is kept between requests. Each call carries its token or credentials.
    • Cacheable. Responses say whether they can be cached.
    • Uniform interface. Resources named by URIs, changed through representations, self-descriptive messages.
    • Layered system. Proxies and load balancers can sit in between without the client knowing.
    • Code on demand is optional and rare in network APIs.

    Anatomy of a request

    Output
    GET /dna/intent/api/v1/network-device?hostname=SW1 HTTP/1.1
    Host: catc.example.com
    Accept: application/json
    X-Auth-Token: eyJ0eXAiOiJKV1Qi...

    Method, path (resource), query string (filter), headers, and for POST, PUT and PATCH a body.

    Anatomy of a response

    Status line (HTTP/1.1 200 OK), headers such as Content-Type: application/json, and a body.

    Traps

    • Saying REST means JSON. REST works with any representation; JSON is just the common one.
    • Thinking stateless means the server stores nothing. It stores data; it does not keep per-client session state between calls.
    How did you do?Book 9.1 Traditional ApplicationsOwn page
  6. Walk me through the HTTP methods. Which ones are idempotent, and why does that matter for automation?

    Say this first

    GET reads, POST creates or triggers an action, PUT creates or replaces the whole resource at a URI, PATCH changes part of it, DELETE removes it. GET, PUT and DELETE are idempotent: sending the same request twice leaves the server in the same state as sending it once. POST is not, and PATCH is not guaranteed to be. That matters because a script that times out can safely retry an idempotent call.

    Go deeper

    The table

    MethodCRUDSafeIdempotentTypical success
    GETReadYesYes200
    POSTCreate, or run an actionNoNo201 (created) or 200
    PUTCreate or replaceNoYes201 (created) or 200/204 (replaced)
    PATCHPartial updateNoNot guaranteed200 or 204
    DELETEDeleteNoYes204 or 200
    HEADRead headers onlyYesYes200

    Safe means it does not change state. Idempotent means repeating it has the same effect on the server as doing it once. The response can differ: a second DELETE may return 404, but the resource is still gone, so DELETE is idempotent.

    The network trap with PUT

    PUT replaces the target. If you PUT an interface with only a new description, a RESTCONF server replaces that whole interface entry with what you sent, and leaves you without the IP address you left out. Use PATCH (merge) to change one field.

    Why automation cares

    Networks are unreliable. If a POST times out you do not know whether the object was created, and a retry may create a duplicate. With PUT you can retry until it succeeds. The same idea is why Ansible modules are written to be idempotent.

    Traps

    • Saying DELETE is not idempotent because the second call returns 404. Idempotency is about server state.
    • Using PUT for a one-field change and silently wiping the rest of the object.
    How did you do?Book 9.1 Traditional ApplicationsOwn page
  7. What do the common HTTP status codes mean when you call a network API?

    Say this first

    2xx is success: 200 OK with a body, 201 Created, 204 No Content (success, no body). 4xx is the client's fault: 400 bad request body, 401 not authenticated, 403 authenticated but not allowed, 404 no such resource, 409 conflict such as the object already exists. 5xx is the server's fault: 500 internal error, 503 unavailable. My scripts retry 429 and 503 with backoff, re-authenticate on 401, and never blindly retry other 4xx.

    Go deeper

    The codes to know cold

    CodeMeaningTypical network API cause
    200 OKSuccess with a bodyGET returned data
    201 CreatedNew resource madePOST or PUT created an object; Location header points to it
    204 No ContentSuccess, nothing to returnRESTCONF PUT, PATCH or DELETE that worked
    400 Bad RequestMalformed or invalid requestBad JSON, wrong field name, missing module prefix, value out of range
    401 UnauthorizedNot authenticatedMissing, wrong or expired token or password
    403 ForbiddenAuthenticated, not allowedRead-only account tries a change; RBAC denies it
    404 Not FoundNo such resourceWrong URI, typo in the model path, object not configured
    405 Method Not AllowedMethod not supported herePUT on a read-only resource
    409 ConflictClashes with current statePOST of an object that already exists; datastore locked
    415 Unsupported Media TypeServer cannot read the body formatWrong Content-Type
    429 Too Many RequestsRate limitedToo many calls; honor Retry-After
    500 Internal Server ErrorServer failedBug or crash on the API side
    503 Service UnavailableTemporarily downController starting, overloaded or in maintenance

    The classes

    1xx informational, 2xx success, 3xx redirect (301, 302, 304), 4xx client error, 5xx server error.

    RESTCONF specifics (RFC 8040)

    Errors come back with a body that holds error-type, error-tag and error-message. For example data-exists and lock-denied both map to 409.

    Traps

    • Swapping 401 and 403.
    • Treating every non-200 as failure. 201 and 204 are success.
    How did you do?Book 9.1 Traditional ApplicationsOwn page
  8. Compare JSON, YAML and XML. Where do you meet each one in network automation?

    Say this first

    All three describe the same kinds of structured data: objects or mappings, lists and values. JSON is compact and strict, with no comments; it is the default for REST APIs, RESTCONF and gNMI. YAML is the human-friendly one, with indentation and comments; Ansible playbooks, inventories, CI pipelines and data files use it. XML is verbose but has namespaces and schemas; NETCONF uses it, and Junos can show any config or command output as XML.

    Go deeper

    The same data three ways

    JSON
    {
      "vlans": [
        {"id": 10, "name": "USERS"},
        {"id": 20, "name": "VOICE"}
      ]
    }
    YAML
    # VLANs for building A
    vlans:
      - id: 10
        name: USERS
      - id: 20
        name: VOICE
    Output
    <vlans xmlns="urn:example:vlans">
      <vlan><id>10</id><name>USERS</name></vlan>
      <vlan><id>20</id><name>VOICE</name></vlan>
    </vlans>

    Differences that come up

    JSONYAMLXML
    CommentsNoYes (#)Yes (<!-- -->)
    Structure byBraces and bracketsIndentation (spaces only)Opening and closing tags
    Typesstring, number, true/false, null, object, arraySame plus dates and more, inferredEverything is text unless a schema says otherwise
    NamespacesNo (RESTCONF uses module: prefixes)NoYes
    Pythonjson (standard library)yaml.safe_load (PyYAML)xml.etree.ElementTree, lxml, xmltodict

    YAML 1.2 is a superset of JSON, so a JSON document is almost always valid YAML.

    Traps

    • Calling YAML a programming language. It is a data format; logic lives in Ansible, Python or Jinja2.
    • Using yaml.load without a safe loader on untrusted input. Use yaml.safe_load.
    How did you do?Book 7.1 Presentation FormattingOwn page
  9. What is YANG, and what are containers, lists and leafs?

    Say this first

    YANG is a data modeling language (RFC 6020, version 1.1 in RFC 7950). It defines the structure, types and rules of configuration and state data, so a client knows exactly what a device accepts. It is not a protocol and not an encoding: NETCONF, RESTCONF and gNMI carry YANG-modeled data as XML, JSON or protobuf. A container groups nodes, a list holds entries identified by a key, a leaf holds one typed value and a leaf-list holds several values.

    Go deeper

    A small model

    Output
    module example-vlans {
      namespace "urn:example:vlans";
      prefix exv;
    
      container vlans {
        list vlan {
          key "id";
          leaf id {
            type uint16 {
              range "1..4094";
            }
          }
          leaf name {
            type string;
          }
          leaf-list tagged-ports {
            type string;
          }
        }
      }
    }

    The building blocks

    StatementWhat it is
    moduleThe file-level unit, with a namespace and prefix
    containerA grouping node with no value of its own (interfaces)
    listRepeating entries, each identified by key (interface keyed by name)
    leafOne value with a type (description, mtu)
    leaf-listMany values of one type (DNS servers)
    typedef, grouping, usesReusable types and blocks
    augmentAdds nodes into another module's tree (how vendors extend IETF models)
    config falseMarks operational state (counters, oper-status), read-only
    rpc, action, notificationOperations and events

    Why engineers care

    The model tells you the path and type before you send anything. The device validates input against it, so id: 5000 fails cleanly instead of half applying. And the same model works over NETCONF, RESTCONF and gNMI.

    Traps

    • Saying YANG is XML. YANG has its own syntax; XML and JSON are encodings of data that follows the model.
    • Confusing YANG with SNMP MIBs. Both are models, but MIBs (SMIv2) are mostly read-only and tied to SNMP.
    How did you do?Book 9.3 Infrastructure ApplicationsOwn page
  10. What is NETCONF and how does it work?

    Say this first

    NETCONF (RFC 6241) is a protocol for installing, changing and reading device configuration. It runs as an SSH subsystem on TCP port 830 (RFC 6242), encodes everything in XML, and uses RPCs such as get, get-config, edit-config, lock, commit and close-session. Both sides open with a hello that lists capabilities. Its big advantages over CLI are structured data, validation against YANG and transactions with a candidate datastore.

    Go deeper

    The layers

    LayerExamples
    ContentYANG-modeled config and state data
    Operationsget, get-config, edit-config, copy-config, delete-config, lock, unlock, commit, discard-changes, validate, close-session, kill-session
    Messages<rpc>, <rpc-reply>, <notification>
    TransportSSH (mandatory to implement), TLS (RFC 7589)

    get vs get-config

    get-config returns config only, from the datastore you name (running, candidate, startup). get returns config plus state data from the running system. Both accept a subtree or XPath filter.

    A get-config with a subtree filter

    Output
    <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
      <get-config>
        <source><running/></source>
        <filter type="subtree">
          <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface><name>GigabitEthernet1</name></interface>
          </interfaces>
        </filter>
      </get-config>
    </rpc>

    The reply carries the same message-id and the data inside <data>.

    Framing

    With base:1.0, each message ends with ]]>]]>. When both sides announce base:1.1, they switch to chunked framing, which cannot be broken by that string appearing inside data.

    Turning it on

    Cisco IOS
    R1(config)# netconf-yang
    Junos
    set system services netconf ssh

    Quick test from a shell: ssh -p 830 netops@10.0.0.1 -s netconf prints the server hello.

    Traps

    • Saying NETCONF runs on port 22. The standard port is 830; some platforms also allow the SSH subsystem on 22.
    • Saying NETCONF is the data model. YANG is the model; NETCONF is the protocol.
    How did you do?Book 9.3 Infrastructure ApplicationsOwn page
  11. How does Ansible work against network devices?

    Say this first

    Ansible is agentless. On servers it copies Python modules over SSH and runs them on the host, but network devices cannot run Python, so network modules run on the control node and talk to the device through a connection plugin: network_cli for SSH CLI, netconf, or httpapi for REST-style APIs. An inventory lists the devices and their variables, a playbook holds plays, and each play runs tasks that call modules from collections such as cisco.ios or arista.eos.

    Go deeper

    The pieces

    • Inventory: hosts and groups, in INI or YAML, static or dynamic (for example from NetBox).
    • Variables: group_vars/ and host_vars/, plus secrets in Ansible Vault.
    • Playbook: a YAML list of plays. A play targets hosts and runs tasks in order.
    • Modules: do the work, ideally idempotently. Network modules live in collections.
    • Roles: reusable bundles of tasks, templates and defaults.

    Inventory

    YAML
    all:
      children:
        ios:
          hosts:
            SW1:
              ansible_host: 10.0.0.11
            SW2:
              ansible_host: 10.0.0.12
          vars:
            ansible_connection: ansible.netcommon.network_cli
            ansible_network_os: cisco.ios.ios
            ansible_user: netops
            ansible_password: "{{ vault_netops_password }}"
            ansible_become: true
            ansible_become_method: enable

    A first playbook

    YAML
    - name: Collect versions
      hosts: ios
      gather_facts: false
      tasks:
        - name: Run show version
          cisco.ios.ios_command:
            commands:
              - show version
          register: ver
    
        - name: Print the first line
          ansible.builtin.debug:
            msg: "{{ ver.stdout_lines[0][0] }}"

    gather_facts: false is common in network plays; call cisco.ios.ios_facts when you need facts.

    Traps

    • Saying Ansible installs an agent on the switch. Network modules run on the control node.
    • Using ios_command to push configuration. It runs commands; it does not manage config idempotently.
    How did you do?Own page
  12. What does idempotent mean in Ansible, and how do check mode and diff help on network devices?

    Say this first

    Idempotent means running the playbook again changes nothing if the device is already right: the first run reports changed, the second reports ok with changed=0. Modules get there by reading the current state, comparing it with the desired state and sending only the difference. --check runs without changing anything and reports what would change; --diff shows the before and after. Together they are my dry run before every change.

    Go deeper

    How you see it

    Output
    $ ansible-playbook ntp.yml
    TASK [Set NTP server] **********************************************************
    changed: [SW1]
    
    PLAY RECAP *********************************************************************
    SW1                        : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    
    $ ansible-playbook ntp.yml
    TASK [Set NTP server] **********************************************************
    ok: [SW1]
    
    PLAY RECAP *********************************************************************
    SW1                        : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

    How modules achieve it

    • ios_config reads the running config and compares your lines (under their parents) with it. It sends only lines that are missing.
    • Resource modules (ios_vlans, ios_interfaces) gather the current state as structured data and compute the commands needed.
    • ios_command runs commands. It is for reading, not for managing config.

    What breaks idempotency with ios_config

    • Lines the device never shows: no shutdown is the default and does not appear in the running config, so it is "missing" on every run.
    • Abbreviations: int gi1/0/1 or desc never match the full words in the running config.
    • Lines the device rewrites: secrets are stored hashed, so the plain text never matches.

    Check mode and diff

    Shell
    ansible-playbook vlans.yml --check --diff --limit SW1

    Run it in the change ticket and in CI. A playbook that shows changed in check mode on an unchanged device has an idempotency bug.

    Traps

    • Writing no shutdown with ios_config and wondering why every run reports changed.
    • Treating changed=0 as proof of correct config. It only shows Ansible saw no difference in the lines it manages.
    How did you do?Own page
  13. What is Netmiko, and how does a basic Netmiko script work?

    Say this first

    Netmiko is a Python library built on Paramiko that makes SSH to network devices easy. It knows each platform's prompts, disables paging, enters enable and config mode, and waits for output. You build a device dictionary with a device_type such as cisco_ios, open ConnectHandler, then call send_command for show commands and send_config_set for changes. It is screen scraping: reliable and universal, but you get text back unless you parse it.

    Go deeper

    A minimal script

    Python
    from netmiko import ConnectHandler
    
    device = {
        "device_type": "cisco_ios",
        "host": "10.0.0.11",
        "username": "netops",
        "password": "secret",
        "secret": "enablepass",
    }
    
    with ConnectHandler(**device) as conn:
        conn.enable()
        print(conn.send_command("show ip interface brief"))
        output = conn.send_config_set([
            "interface Loopback100",
            "description mgmt-test",
        ])
        print(output)
        conn.save_config()

    The methods you use most

    MethodWhat it does
    send_command(cmd)Send one command, wait for the prompt, return the output
    send_command(cmd, use_textfsm=True)Parse the output with a TextFSM template into a list of dictionaries
    send_config_set(list)Enter config mode, send the lines, exit
    send_config_from_file(path)Same, from a file
    save_config()Platform-appropriate save (write memory on IOS)
    enable()Enter privileged mode using secret

    Common device types

    cisco_ios, cisco_xe, cisco_nxos, cisco_xr, arista_eos, juniper_junos, linux.

    Timeouts

    In Netmiko 4, send_command waits up to read_timeout (10 seconds by default) for the prompt. A big show running-config on a core switch can need read_timeout=60.

    Traps

    • Calling Netmiko an API. It types commands into an SSH session like a person would.
    • Parsing output with hand-written string splits that break on the next software release.
    How did you do?Own page
  14. How do you use Jinja2 templates to generate network configs?

    Say this first

    Jinja2 separates data from config. The template holds the config text with placeholders; YAML or the source of truth holds the values per device. {{ var }} prints a value, {% for %} and {% if %} add loops and conditions, and filters such as upper, default or join transform values. The same template renders the same structure for every device, which is where consistency comes from.

    Go deeper

    Template

    Output
    hostname {{ hostname }}
    {% for server in ntp_servers %}
    ntp server {{ server }}
    {% endfor %}
    {% if snmp_location is defined %}
    snmp-server location {{ snmp_location }}
    {% endif %}

    Data

    YAML
    hostname: ASW1
    ntp_servers:
      - 10.0.0.5
      - 10.0.0.6
    snmp_location: Building A floor 2

    Render in Python

    Python
    import yaml
    from jinja2 import Environment, FileSystemLoader, StrictUndefined
    
    env = Environment(
        loader=FileSystemLoader("templates"),
        trim_blocks=True,
        lstrip_blocks=True,
        undefined=StrictUndefined,
    )
    template = env.get_template("base.j2")
    with open("data/asw1.yml") as f:
        data = yaml.safe_load(f)
    print(template.render(**data))
    Output
    hostname ASW1
    ntp server 10.0.0.5
    ntp server 10.0.0.6
    snmp-server location Building A floor 2

    Things that trip people

    • Whitespace. Without trim_blocks and lstrip_blocks, every {% %} line leaves a blank line or indentation. You can also trim per tag with {%- and -%}.
    • Undefined variables. The default prints an empty string, which can produce ntp server with no address. StrictUndefined makes it fail instead. Ansible fails on undefined variables by default.
    • Logic creep. Keep templates simple and push decisions into the data.

    In Ansible, the ansible.builtin.template module renders a .j2 file, and modules such as ios_config accept rendered config through src.

    Traps

    • Putting device-specific values inside the template instead of the data.
    • Forgetting whitespace control and shipping configs full of blank lines and stray spaces.
    How did you do?Own page
  15. What is a source of truth, and how does NetBox fit into automation?

    Say this first

    A source of truth is the one authoritative place that says what the network should look like: devices, interfaces, IP addresses, VLANs, circuits. Automation reads intent from it, renders config and pushes it, so the network follows the data instead of the data trying to describe the network. NetBox is the common open source choice: DCIM plus IPAM with a REST and GraphQL API, a Python client (pynetbox) and an Ansible collection that includes a dynamic inventory.

    Go deeper

    What NetBox models

    • DCIM: regions, sites, locations, racks, device types, devices, interfaces, cables, power.
    • IPAM: VRFs, prefixes, IP addresses, VLANs and VLAN groups, ASNs, route targets.
    • Circuits and providers, virtualization, tenancy.
    • Extensibility: custom fields, tags, config contexts (JSON data merged by site, role or platform), config templates (Jinja2), webhooks and event rules, plugins.

    Reading it from Python

    Python
    import os
    import pynetbox
    
    nb = pynetbox.api("https://netbox.example.com", token=os.environ["NETBOX_TOKEN"])
    for dev in nb.dcim.devices.filter(site="dc1", status="active"):
        print(dev.name, dev.primary_ip4)

    As Ansible inventory

    YAML
    # nb_inventory.yml
    plugin: netbox.netbox.nb_inventory
    api_endpoint: https://netbox.example.com
    validate_certs: true
    group_by:
      - sites
      - platforms
    query_filters:
      - status: active

    The token comes from the NETBOX_TOKEN environment variable.

    Intended vs actual

    NetBox holds intended state. The devices hold actual state. Automation turns one into the other, and drift checks compare them. NetBox is not a monitoring system and does not discover the network by itself.

    Traps

    • Calling a config backup repository a source of truth. Backups record what is, not what should be.
    How did you do?Own page
  16. How would you use Git to manage network changes?

    Say this first

    Everything that defines the network (templates, variables, playbooks, intended configs) lives in a Git repo. Each change is a short-lived branch, opened as a pull or merge request that runs automated checks and gets a peer review. Merging to main means "approved", and the pipeline deploys main. Git gives history, a diff of every change, who made it and why, and an easy way to revert.

    Go deeper

    The everyday flow

    Shell
    git clone git@git.example.com:netops/network.git
    cd network
    git switch -c CHG1234-add-vlan-30
    # edit data/sites/dc1/vlans.yml
    git add data/sites/dc1/vlans.yml
    git commit -m "CHG1234: add VLAN 30 GUESTS to dc1 leaves"
    git push -u origin CHG1234-add-vlan-30

    Then open a merge request. CI lints, renders and dry-runs the change, a reviewer approves, and it merges.

    Commands worth knowing

    CommandUse
    git status, git diffWhat changed and is not committed yet
    git log --onelineShort history
    git blame fileWho last changed each line
    git revert <commit>New commit that undoes an old one, safe on shared branches
    git pullFetch and merge the latest from the remote

    What goes in, what stays out

    In: templates, data, playbooks, scripts, tests, pipeline files. Out: secrets (use Vault), generated files you can rebuild, and huge binary images. Config backups usually live in a separate repo.

    Traps

    • Long-lived branches per engineer that drift away from main and conflict at merge time.
    • Committing passwords. Git history keeps them even after deletion.
    How did you do?Own page

Working16

  1. How does OpenFlow actually work, and why did it not take over the network?

    Say this first

    An OpenFlow switch holds flow tables. Each entry has match fields, a priority, counters, instructions or actions and timeouts. The controller connects to the switch over TCP 6653 (optionally TLS), installs entries with Flow-Mod messages, and gets packets that miss every entry through Packet-In. It stayed niche because switch TCAM is small, reactive flow setup does not scale, and operators preferred proven distributed protocols with central policy on top.

    Go deeper

    The pieces

    • Flow entry. Match on ingress port, MACs, EtherType, VLAN, IP addresses, protocol, L4 ports (OpenFlow 1.0 had 12 match fields; later versions added many more). Highest priority match wins.
    • Actions. Output to a port, drop, rewrite a header field, push or pop a VLAN tag, send to the controller.
    • Table-miss. A priority 0 entry that drops or sends to the controller.
    • Messages. Hello, Features Request and Reply, Flow-Mod, Packet-In, Packet-Out, Port-Status, Echo.

    Reactive vs proactive

    • Reactive. The first packet of a new flow goes to the controller, which installs an entry. Flexible, but every new flow costs a round trip, and the controller becomes the bottleneck.
    • Proactive. The controller installs entries in advance from known topology and policy. Scales far better and survives a controller outage.

    Versions

    OpenFlow 1.1 and later added multiple tables in a pipeline and group tables. 1.3 added meters and became the most widely implemented version. The old controller port was 6633; IANA later assigned 6653.

    Where it lived on

    Open vSwitch in hypervisors and OpenStack, Google's B4 inter-datacenter WAN, and research. Its ideas moved into P4, programmable pipelines and controller-based fabrics.

    Output
    $ ovs-ofctl dump-flows br0
     cookie=0x0, duration=120.345s, table=0, n_packets=42, n_bytes=3528, priority=100,ip,nw_dst=10.0.0.2 actions=output:2
     cookie=0x0, duration=300.012s, table=0, n_packets=7, n_bytes=420, priority=0 actions=CONTROLLER:65535

    Traps

    • Saying OpenFlow is a routing protocol. It programs forwarding state; it does not compute paths.
    • Quoting only 6633. Know that 6653 is the assigned port.
    How did you do?Book 3.5 ImplementationOwn page
  2. Compare Cisco Catalyst Center and ACI APIC. What does each controller manage?

    Say this first

    Catalyst Center (formerly DNA Center) manages campus and branch: design, provisioning, software images, plug-and-play, SD-Access fabrics and assurance, exposed through the Intent API. APIC manages an ACI data center fabric of Nexus 9000 switches with a policy object model (tenants, VRFs, bridge domains, EPGs, contracts). Everything in the APIC GUI is a REST call against that model.

    Go deeper

    Catalyst Center

    • Design: sites, buildings, floors, IP pools, global settings (DNS, NTP, AAA).
    • Policy: group-based policy with ISE and SGTs for SD-Access.
    • Provision: templates, PnP zero-touch onboarding, SWIM for image upgrades.
    • Assurance: health scores, client and device telemetry, issue detection.
    • SD-Access fabric: LISP control plane, VXLAN data plane, TrustSec (SGT) policy plane.
    • API: get a token from POST /dna/system/api/v1/auth/token with basic auth, then send it in the X-Auth-Token header, for example GET /dna/intent/api/v1/network-device.

    ACI APIC

    • Fabric: spine-leaf Nexus 9000 in ACI mode, APIC cluster (three or more in production).
    • Object model: tenant contains VRFs, bridge domains and application profiles with EPGs. Contracts allow traffic between EPGs. With no contract, EPGs cannot talk (allow-list model).
    • Internals: IS-IS underlay, VXLAN overlay, COOP on spines for endpoint lookup, MP-BGP to distribute external routes.
    • API: POST /api/aaaLogin.json returns a token (also set as the APIC-cookie cookie). Query a class with GET /api/class/fvTenant.json or one object with GET /api/mo/uni/tn-Prod.json. The GUI's API Inspector shows the exact call behind every click.

    Others worth naming

    Cisco SD-WAN Manager and Controller, Meraki Dashboard (cloud managed), Arista CloudVision, Juniper Apstra.

    Traps

    • Calling ACI "just a GUI". The point is the object model and API; the GUI is one client of it.
    • Saying Catalyst Center forwards traffic or is in the data path. It never is.
    How did you do?Own page
  3. What is intent-based networking, and how is it different from plain automation?

    Say this first

    In intent-based networking you state the outcome you want ("contractors can reach only the internet"), and the system translates it into device config, pushes it, and then keeps checking with telemetry that the outcome is still true. Plain automation runs the steps you wrote. IBN adds the translation from business terms and the closed loop of continuous assurance.

    Go deeper

    The loop

    1. Translation. Business or service intent becomes a network policy and then device config.
    2. Activation. The controller pushes config to every device involved.
    3. Assurance. Telemetry and tests confirm the network behaves as intended, all the time.
    4. Remediation. When reality drifts from intent, the system alerts or corrects.

    Products that use the term

    • Cisco Catalyst Center with SD-Access: group-based policies between user and device groups, assurance on top.
    • Juniper Apstra: intent for data center fabrics, with continuous validation of the built fabric against the blueprint.
    • Cisco ACI: the policy model (EPGs and contracts) is a form of declared intent.

    Honest view

    The term is marketing-heavy. The useful part is real: declare the desired state, generate config from it, and verify continuously instead of trusting that a push worked.

    Traps

    • Describing IBN as "a GUI that pushes templates". Without assurance there is no closed loop and no intent check.
    How did you do?Own page
  4. How do network APIs authenticate you, and how do you handle tokens in a script?

    Say this first

    Three patterns cover most of it. Basic auth sends a base64 user and password on every call, so it is only acceptable over TLS. Token APIs (Catalyst Center, APIC) take a login call once and return a short-lived token you send in a header. API keys and OAuth 2.0 bearer tokens (Meraki, cloud platforms) go in the Authorization header. In scripts I keep credentials out of the code, verify TLS, and handle a 401 by getting a fresh token and retrying once.

    Go deeper

    Examples

    PlatformHow you log inHow you send it after
    IOS-XE RESTCONFBasic auth on every requestAuthorization: Basic ...
    Catalyst CenterPOST /dna/system/api/v1/auth/token with basic auth, returns {"Token": "..."}X-Auth-Token header
    ACI APICPOST /api/aaaLogin.json with user and password in JSONAPIC-cookie cookie; refresh with /api/aaaRefresh.json
    Meraki DashboardAPI key created in the dashboardAuthorization: Bearer <key>

    A clean Python pattern

    Python
    import os
    import requests
    
    BASE = "https://catc.example.com"
    CA = "/etc/ssl/certs/corp-ca.pem"
    
    resp = requests.post(
        f"{BASE}/dna/system/api/v1/auth/token",
        auth=(os.environ["CATC_USER"], os.environ["CATC_PASS"]),
        verify=CA,
        timeout=10,
    )
    resp.raise_for_status()
    token = resp.json()["Token"]
    
    devices = requests.get(
        f"{BASE}/dna/intent/api/v1/network-device",
        headers={"X-Auth-Token": token, "Accept": "application/json"},
        verify=CA,
        timeout=30,
    )
    devices.raise_for_status()
    for dev in devices.json()["response"]:
        print(dev["hostname"], dev["managementIpAddress"])

    Habits that matter

    • Credentials come from environment variables, Ansible Vault or a secrets manager, never from the repo.
    • verify=False is a lab shortcut. In production point verify at the company CA bundle.
    • Tokens expire. A long job must refresh or re-authenticate on 401 instead of failing halfway.
    • Use a dedicated service account with only the rights the job needs, so the AAA logs show what the automation did.

    Traps

    • Hard-coding a password in a script and committing it to Git. History keeps it even after you delete it.
    • Disabling certificate checks in production code.
    How did you do?Own page
  5. How do you read a YANG model and find the path you need for a change?

    Say this first

    I turn the module into a tree with pyang -f tree, find the node, and read its flags: rw is config, ro is state, * marks a list or leaf-list, ? an optional node, and the list key is in brackets. To see real data I pull the device's config through NETCONF or RESTCONF and match it to the tree. Cisco YANG Suite or similar tools do the same in a GUI.

    Go deeper

    Tree output for ietf-interfaces (trimmed)

    Output
    $ pyang -f tree ietf-interfaces.yang
    module: ietf-interfaces
      +--rw interfaces
      |  +--rw interface* [name]
      |     +--rw name                        string
      |     +--rw description?                string
      |     +--rw type                        identityref
      |     +--rw enabled?                    boolean
      |     +--rw link-up-down-trap-enable?   enumeration {if-mib}?
      |     +--ro admin-status                enumeration {if-mib}?
      |     +--ro oper-status                 enumeration
      |     +--ro last-change?                yang:date-and-time
      |     +--ro if-index                    int32 {if-mib}?
      |     +--ro phys-address?               yang:phys-address
      |     +--ro speed?                      yang:gauge64
      |     +--ro statistics
      |        +--ro discontinuity-time    yang:date-and-time
      |        +--ro in-octets?            yang:counter64
      ...
      x--ro interfaces-state
      ...

    How to read it

    • interface* [name] is a list keyed by name.
    • description? is optional config. type has no ?, so it is mandatory when you create an interface.
    • {if-mib}? means the node exists only if the server supports the if-mib feature.
    • x marks a deprecated node: interfaces-state was replaced by the ro nodes inside interfaces.

    Turning it into paths

    UsePath
    RESTCONF/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1/description
    XPath (NETCONF filter, telemetry)/interfaces/interface[name='GigabitEthernet1']/description
    gNMI/interfaces/interface[name=GigabitEthernet1]/description

    Where models come from

    The device itself (NETCONF get-schema, RFC 6022), the vendor's published model set for that release, and the public YangModels collection that holds IETF, OpenConfig and vendor models by release.

    Traps

    • Writing to a ro node. State data cannot be configured, and the device will reject it.
    • Forgetting to URL-encode a key with a slash. GigabitEthernet1/0/1 must be GigabitEthernet1%2F0%2F1 in a RESTCONF URI.
    How did you do?Own page
  6. What is the difference between native, OpenConfig and IETF YANG models? Which do you use?

    Say this first

    IETF models are standards (ietf-interfaces, ietf-ip, ietf-routing) and are vendor neutral but cover a limited set of features. OpenConfig models are written by operators such as Google and Microsoft, are vendor neutral, and pair config with operational state for streaming telemetry. Native models come from the vendor (Cisco-IOS-XE-native, the Junos configuration schema) and cover every feature, but only on that platform. I use OpenConfig or IETF where coverage is good and fall back to native for the gaps.

    Go deeper

    Side by side

    IETFOpenConfigNative
    Written byIETF working groupsOperator consortiumVendor
    CoverageBasic building blocksBroad for common features, grows each yearEverything the OS supports
    PortabilityHighHigh, within each vendor's supportNone
    StyleConfig and state in separate or NMDA treesconfig and state containers under each nodeMirrors the CLI
    Typical transportNETCONF, RESTCONFgNMI, also NETCONFAll

    What the paths look like

    Output
    IETF:        /interfaces/interface[name=GigabitEthernet1]/description
    OpenConfig:  /interfaces/interface[name=Ethernet1]/config/description
                 /interfaces/interface[name=Ethernet1]/state/counters/in-octets
    IOS-XE:      /native/interface/GigabitEthernet[name=1]/description

    Cisco operational models

    IOS-XE also ships -oper models for state, for example Cisco-IOS-XE-interfaces-oper and Cisco-IOS-XE-process-cpu-oper. IOS XR splits native models into -cfg and -oper.

    The practical rule

    A multi-vendor shop gets the most from OpenConfig, especially for telemetry. But vendors implement OpenConfig at different depths and publish deviations, so test each model on each platform and release before you build on it.

    Traps

    • Assuming "OpenConfig supported" means every leaf works. Check the vendor's deviations for your release.
    How did you do?Own page
  7. Explain the running and candidate datastores, commit, confirmed commit and the edit-config operations.

    Say this first

    Running is the live config. Candidate (the :candidate capability) is a scratch copy: you edit it, check it, then commit copies it to running in one step, or discard-changes throws it away. A confirmed commit applies the change but rolls it back automatically unless a second commit arrives within the timeout (600 seconds by default). Inside edit-config, the operation attribute decides merge, replace, create, delete or remove for each node.

    Go deeper

    Datastores

    DatastoreCapabilityNotes
    runningalwaysLive config. Writable directly only with :writable-running
    candidate:candidateEdit, validate, then commit or discard-changes
    startup:startupConfig loaded at boot, where the platform separates it

    edit-config operations

    OperationBehavior
    merge (default)Merge the sent data into the target
    replaceReplace the target node with the sent data
    createCreate; error data-exists if it is already there
    deleteDelete; error data-missing if it is not there
    removeDelete if present, no error if absent

    default-operation (merge, replace or none) sets the behavior for nodes without an attribute. error-option is stop-on-error (default), continue-on-error or rollback-on-error (needs the :rollback-on-error capability).

    Deleting one interface description

    Output
    <rpc message-id="105" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"
         xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
      <edit-config>
        <target><candidate/></target>
        <config>
          <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface>
              <name>GigabitEthernet2</name>
              <description nc:operation="remove"/>
            </interface>
          </interfaces>
        </config>
      </edit-config>
    </rpc>

    Confirmed commit

    <commit><confirmed/><confirm-timeout>120</confirm-timeout></commit> applies the candidate and starts a timer. If your session drops or nobody sends a plain <commit/> before the timer ends, the device reverts. That is the safety net for changes that might cut your own access. Junos users know the same idea as commit confirmed.

    Platform notes

    IOS-XE needs netconf-yang feature candidate-datastore to offer a candidate. Junos maps NETCONF candidate to its normal candidate configuration.

    Traps

    • Thinking delete and remove are the same. delete errors when the node is missing; remove does not.
    • Editing running directly on a platform that has a candidate, and losing the all-or-nothing behavior.
    How did you do?Own page
  8. How does RESTCONF work? Show me a request and explain the URI.

    Say this first

    RESTCONF (RFC 8040) exposes YANG-modeled data over HTTPS with REST methods. The data tree lives under /restconf/data; the URI is the model path, starting with module-name:node, with list keys after an equals sign. Bodies use application/yang-data+json or application/yang-data+xml. GET reads, POST creates, PUT creates or replaces, PATCH merges, DELETE removes. Each request is its own transaction: there is no candidate, lock or confirmed commit like in NETCONF.

    Go deeper

    Anatomy of a URI

    https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2/description

    PartMeaning
    https://10.0.0.1The device, over HTTPS
    /restconfAPI root
    /dataThe datastore resource
    /ietf-interfaces:interfacesModule name and top-level container
    /interface=GigabitEthernet2List interface, entry whose key is GigabitEthernet2
    /descriptionA leaf inside that entry
    • The root is found at /.well-known/host-meta; on most devices it is /restconf.
    • /restconf/data is the datastore, /restconf/operations lists RPCs.
    • Multiple keys are separated by commas. Reserved characters are percent-encoded: / becomes %2F.

    Enable on IOS-XE

    Cisco IOS
    username netops privilege 15 secret 0 S3cure.pass
    aaa new-model
    aaa authentication login default local
    aaa authorization exec default local
    ip http secure-server
    restconf

    Read one interface

    Shell
    curl -s -k -u netops:S3cure.pass \
      -H "Accept: application/yang-data+json" \
      https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2
    JSON
    {
      "ietf-interfaces:interface": {
        "name": "GigabitEthernet2",
        "description": "to SW1 Gi1/0/48",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "10.1.12.1",
              "netmask": "255.255.255.0"
            }
          ]
        },
        "ietf-ip:ipv6": {
        }
      }
    }

    Note the module: prefix on the top member and wherever a node comes from another module (ietf-ip:ipv4).

    Useful query parameters

    depth=2 limits how deep the reply goes, content=config or content=nonconfig picks config or state, fields=name;description returns only some leafs, with-defaults controls default values.

    Traps

    • Using application/json instead of the YANG media types. Some servers reject it or answer in a different format.
    • Leaving out the module: prefix on the top-level JSON member, which gives a 400.
  9. Compare NETCONF, RESTCONF and gNMI. When would you pick each?

    Say this first

    All three carry YANG-modeled data. NETCONF uses SSH on port 830 and XML, and has the strongest transactions: candidate datastore, lock, validate and confirmed commit. RESTCONF uses HTTPS with JSON or XML and REST methods, so it is the easiest to call from any tool, but each request is its own transaction. gNMI uses gRPC over HTTP/2 with protobuf, has an atomic Set, and its Subscribe RPC is the standard for streaming telemetry. I pick NETCONF for safe config changes, RESTCONF for quick integrations, gNMI for telemetry.

    Go deeper

    Side by side

    NETCONFRESTCONFgNMI
    StandardIETF, RFC 6241IETF, RFC 8040OpenConfig specification
    TransportSSH, TCP 830 (TLS optional)HTTPS, usually 443gRPC over HTTP/2 with TLS; port varies by platform, IANA lists 9339
    EncodingXMLJSON or XMLProtobuf messages; values as JSON, JSON_IETF, proto, bytes or ASCII
    Operationsget, get-config, edit-config, lock, commit, validateGET, POST, PUT, PATCH, DELETECapabilities, Get, Set, Subscribe
    TransactionsCandidate, commit, confirmed commit, rollback-on-errorOne request = one changeOne SetRequest is applied all or nothing
    StreamingNotifications (RFC 5277), YANG-Push subscriptionsEvent streams over server-sent events, rarely usedSubscribe: stream, poll, once
    Typical modelsIETF, nativeIETF, nativeOpenConfig mostly
    Feels likeA config databaseA web APIA telemetry and config bus

    How I choose

    • Risky config changes on many devices: NETCONF with candidate and confirmed commit, where supported.
    • A quick integration, a webhook, a ServiceNow action: RESTCONF, because every language and tool can do HTTPS and JSON.
    • Monitoring at scale: gNMI Subscribe, often with OpenConfig paths across vendors.
    • Platform reality: use what the device does well. Some platforms are strongest on one interface.

    Traps

    • Saying RESTCONF is "NETCONF over HTTP" with the same transaction model. It has no candidate, lock or confirmed commit.
    • Calling gNMI an IETF standard. It is defined by OpenConfig.
  10. How does gNMI work, and what are its subscription modes?

    Say this first

    gNMI is a gRPC service with four RPCs: Capabilities (which models and encodings the target supports), Get, Set (delete, replace and update in one atomic request) and Subscribe. A subscription can be ONCE (send everything and stop), POLL (send when the client asks) or STREAM. Stream subscriptions are SAMPLE (every interval), ON_CHANGE (only when a value changes) or TARGET_DEFINED (the device picks per path).

    Go deeper

    Paths

    gNMI paths are XPath-like with keys in brackets and no quotes: /interfaces/interface[name=Ethernet1]/state/counters/in-octets. An optional origin (often openconfig) says which model tree the path belongs to.

    Subscribe in practice

    Shell
    gnmic -a 10.0.0.31:6030 -u netops -p secret --insecure capabilities
    
    gnmic -a 10.0.0.31:6030 -u netops -p secret --insecure \
      subscribe --path "/interfaces/interface[name=Ethernet1]/state/counters" \
      --stream-mode sample --sample-interval 10s
    
    gnmic -a 10.0.0.31:6030 -u netops -p secret --insecure \
      subscribe --path "/interfaces/interface/state/oper-status" --stream-mode on-change

    --insecure is plaintext gRPC for a lab. With TLS use --tls-ca to verify the device certificate, or --skip-verify to encrypt without checking it.

    Which mode for what

    DataModeWhy
    Interface counters, CPU, memorySAMPLE, 10 to 30 sValues change constantly
    Oper-status, BGP session state, LLDP neighborsON_CHANGERare, important changes, sent at once
    Inventory, software versionONCE or POLLRarely changes

    suppress_redundant and heartbeat_interval let SAMPLE skip unchanged values while still proving the stream is alive.

    Related gRPC services

    gNOI for operations (reboot, file transfer, certificates, ping), gRIBI for injecting routes, P4Runtime for programmable forwarding pipelines.

    Enabling it

    Arista EOS
    management api gnmi
       transport grpc default

    Other platforms have their own commands and default ports; check the release notes.

    Traps

    • Using SAMPLE every second on thousands of paths and overloading the device CPU and the collector.
    How did you do?Own page
  11. What is model-driven telemetry, and why is it replacing SNMP polling?

    Say this first

    With SNMP the collector polls each device every few minutes, and the device walks MIB tables to answer. With model-driven telemetry the device pushes YANG-modeled data on a schedule or when it changes, over gRPC, gNMI or NETCONF. You get seconds instead of minutes, on-change events, structured data, and less CPU per data point. It does not remove SNMP overnight, because many tools and old devices still depend on it.

    Go deeper

    What changes

    SNMP pollingModel-driven telemetry
    ModelPullPush (or a long-lived subscription)
    Typical interval1 to 5 minutes10 s or less, or on change
    DataMIB OIDsYANG paths (OpenConfig or native)
    EncodingBER (ASN.1)Protobuf (GPB, KV-GPB) or JSON
    TransportUDP 161, traps on 162gRPC, gNMI, NETCONF
    MissesMicrobursts and short flaps between pollsFar fewer

    IOS-XE dial-out example

    Cisco IOS
    telemetry ietf subscription 101
     encoding encode-kvgpb
     filter xpath /process-cpu-ios-xe-oper:cpu-usage/cpu-utilization/five-seconds
     source-address 10.0.0.1
     stream yang-push
     update-policy periodic 6000
     receiver ip address 10.0.0.50 57000 protocol grpc-tcp

    update-policy periodic is in centiseconds, so 6000 is every 60 seconds. update-policy on-change works for paths that support it. Newer releases also offer named receivers, configured separately.

    A common collector stack

    Telegraf receives the data (the cisco_telemetry_mdt input for Cisco dial-out, the gnmi input for dial-in), writes it to InfluxDB or Prometheus, and Grafana draws it:

    Output
    [[inputs.cisco_telemetry_mdt]]
      transport = "grpc"
      service_address = ":57000"

    Encodings

    Compact GPB needs the matching .proto files on the collector. KV-GPB carries field names with the values, so it is self-describing at the cost of size. JSON is easiest to read and largest.

    Traps

    • Saying telemetry is "faster SNMP". It is a different model (push, YANG, gRPC), not a tuned poller.
    • Forgetting that update-policy periodic on IOS-XE is in centiseconds, not seconds.
    How did you do?Book 9.3 Infrastructure ApplicationsOwn page
  12. What are network_cli, netconf and httpapi in Ansible, and how do you set them up?

    Say this first

    They are connection plugins from ansible.netcommon. network_cli keeps a persistent SSH session and drives the CLI, used by most ios, nxos and eos modules. netconf opens a NETCONF session on port 830, used by Junos modules and the generic netconf_config and netconf_get. httpapi talks to a device REST API such as Arista eAPI or NX-API. You select one with ansible_connection and the platform with ansible_network_os.

    Go deeper

    Group variables per platform

    YAML
    # group_vars/ios.yml
    ansible_connection: ansible.netcommon.network_cli
    ansible_network_os: cisco.ios.ios
    ansible_become: true
    ansible_become_method: enable
    
    # group_vars/eos.yml
    ansible_connection: ansible.netcommon.httpapi
    ansible_network_os: arista.eos.eos
    ansible_httpapi_use_ssl: true
    ansible_httpapi_validate_certs: true
    ansible_become: true
    ansible_become_method: enable
    
    # group_vars/junos.yml
    ansible_connection: ansible.netcommon.netconf
    ansible_network_os: junipernetworks.junos.junos

    Persistent connections

    network_cli, netconf and httpapi keep one connection per host open across tasks, instead of logging in for every task. Two timers matter: the connect timeout and the command timeout, both 30 seconds by default. A slow show tech or a big config needs a larger command timeout.

    The old way

    Older playbooks used connection: local with a provider dictionary on each task. That pattern is deprecated; set the connection in inventory variables instead.

    Picking one

    NeedPlugin
    IOS, NX-OS, EOS, IOS XR CLI modulesnetwork_cli
    Junos, or any NETCONF device with netconf_confignetconf
    EOS eAPI, NX-API, firewalls and appliances with REST APIshttpapi

    Traps

    • Mixing up ansible_network_os values. It must be the full collection name, such as cisco.ios.ios.
    How did you do?Own page
  13. What are Ansible network resource modules, and what is the difference between merged, replaced and overridden?

    Say this first

    Resource modules manage one feature as structured data, for example cisco.ios.ios_vlans or ios_l3_interfaces, instead of raw CLI lines. The state decides how. merged adds or updates what you list and leaves the rest. replaced makes each listed item match exactly, removing attributes you left out. overridden makes the whole resource on the device match your list, which deletes anything not listed. deleted removes, and gathered, rendered and parsed read or convert without changing anything.

    Go deeper

    The states

    StateTouches the deviceEffect
    mergedYesAdd or update listed items; leave others alone
    replacedYesListed items match exactly; unlisted items untouched
    overriddenYesWhole resource matches the list; unlisted items removed
    deletedYesRemove listed items, or all if none listed
    gatheredReadsReturn the current config as structured data
    renderedNoReturn the CLI commands the config would produce, offline
    parsedNoTurn running-config text you supply into structured data

    Example

    YAML
    - name: Access VLANs
      cisco.ios.ios_vlans:
        config:
          - vlan_id: 10
            name: USERS
          - vlan_id: 20
            name: VOICE
          - vlan_id: 30
            name: GUESTS
        state: merged

    The result holds before, after and commands. If only VLAN 30 is new, commands is ["vlan 30", "name GUESTS"].

    Why they beat ios_config lines

    The module understands the feature, so it knows that shutdown is the opposite of no shutdown, removes attributes correctly in replaced, and stays idempotent without you matching running-config text.

    Traps

    • Using overridden with incomplete data. It removes everything you did not list.
    • Assuming replaced removes unlisted VLANs. It only rewrites the ones you list.
    How did you do?Own page
  14. How do you handle credentials and secrets in network automation?

    Say this first

    Secrets never go into Git in plain text. In Ansible I encrypt them with Ansible Vault or pull them at run time from a secrets manager such as HashiCorp Vault. In CI they are protected, masked pipeline variables. Automation uses its own service accounts with least privilege, authenticated through TACACS+ or RADIUS so every change is logged against that account. If a secret leaks, I rotate it, because Git history keeps it.

    Go deeper

    Ansible Vault

    Shell
    ansible-vault create group_vars/all/vault.yml
    ansible-vault encrypt_string 'S3cure.pass' --name 'vault_netops_password'
    ansible-playbook site.yml --ask-vault-pass
    ansible-playbook site.yml --vault-password-file ~/.vault_pass

    A common pattern keeps vault_ variables in an encrypted file and refers to them from normal variables: ansible_password: "{{ vault_netops_password }}". People can read the variable names in reviews without seeing the values.

    Other places secrets live

    • Environment variables for scripts, set by the CI system or a wrapper, not hard-coded.
    • Secrets managers (HashiCorp Vault, CyberArk, cloud key vaults) with short-lived credentials and audit logs.
    • CI variables marked protected and masked so they only reach protected branches and never print in logs.

    Accounts

    • A separate account per automation system, not a shared admin login.
    • Read-only accounts for collection jobs (backups, inventory).
    • AAA accounting on, so the logs show which account changed what.

    Traps

    • Thinking a private repo is a safe place for passwords. Access spreads and history is forever.
    How did you do?Own page
  15. What does NAPALM give you that Netmiko does not? Explain getters and config replace vs merge.

    Say this first

    NAPALM is a vendor-neutral Python API over several platforms (core drivers for IOS, IOS XR, NX-OS, EOS and Junos). Getters such as get_facts, get_interfaces and get_bgp_neighbors return the same structured dictionaries on every platform. For config it loads a candidate, shows a diff with compare_config, then commit_config, discard_config or later rollback. Merge adds your lines to the running config; replace makes the running config exactly equal to the file you load.

    Go deeper

    Getters

    get_facts, get_interfaces, get_interfaces_ip, get_arp_table, get_mac_address_table, get_lldp_neighbors, get_bgp_neighbors, get_environment, get_config, get_route_to and more. Not every driver implements every getter; unsupported ones raise NotImplementedError.

    Config workflow

    Python
    from napalm import get_network_driver
    
    driver = get_network_driver("eos")
    with driver("10.0.0.21", "netops", "secret") as dev:
        print(dev.get_facts()["os_version"])
        dev.load_merge_candidate(config="ntp server 10.0.0.5\n")
        diff = dev.compare_config()
        if diff:
            print(diff)
            dev.commit_config()
        else:
            dev.discard_config()

    Merge vs replace

    load_merge_candidateload_replace_candidate
    You supplyOnly the lines to add or changeThe full intended config
    Lines on the device you did not mentionStayRemoved
    Good forSmall additive changesFull config management, drift removal
    RiskStale config builds upAn incomplete file deletes real config

    How it works under the hood

    Each driver uses the platform's best mechanism: Junos has a native candidate and commit, EOS uses configuration sessions, IOS XR has its own commit model. IOS has no candidate, so the driver copies the candidate file to flash with SCP and uses configure replace for a replace. That is why IOS needs ip scp server enable for NAPALM config operations.

    Validation

    compliance_report("validate.yml") compares getter results with expected values in a YAML file, for example "these BGP peers are up". It is a quick post-check.

    Traps

    • Assuming every getter works on every driver. Check the support matrix for your platforms.
    How did you do?Own page
  16. What is Terraform, what is the state file, and where does Terraform fit next to Ansible?

    Say this first

    Terraform is a declarative infrastructure as code tool. You describe resources in HCL, providers translate them into API calls, terraform plan shows the difference between your code and reality, and apply makes it so. The state file maps each resource in the code to the real object ID and lets Terraform know what it manages. It must be stored remotely with locking, kept private because it can hold secrets, and never edited by hand. Terraform shines on API-driven platforms (cloud, ACI, firewall managers); Ansible is stronger for configuring CLI-based devices.

    Go deeper

    A small example (ACI tenant)

    Terraform
    terraform {
      required_providers {
        aci = {
          source = "CiscoDevNet/aci"
        }
      }
    }
    
    variable "apic_password" {
      type      = string
      sensitive = true
    }
    
    provider "aci" {
      username = "admin"
      password = var.apic_password
      url      = "https://apic1.example.com"
    }
    
    resource "aci_tenant" "prod" {
      name        = "Prod"
      description = "Managed by Terraform"
    }

    The workflow

    Shell
    terraform init               # download providers, set up the backend
    terraform plan -out tfplan   # compare code, state and reality
    terraform apply tfplan       # apply exactly that plan
    terraform state list         # resources Terraform manages

    State rules

    • Remote backend (S3, Azure Storage, GCS, HCP Terraform, GitLab) with locking, so two people cannot apply at once.
    • Treat it as sensitive: attributes, including some passwords, are stored in plain text.
    • Change it only with terraform state mv, state rm and import (or import blocks).
    • If someone changes a managed object by hand, the next plan shows the drift and offers to put it back.

    Terraform vs Ansible

    TerraformAnsible
    StyleDeclarative, whole desired stateOrdered tasks, each module declarative
    StateState fileNo state file; reads the device each run
    StrengthAPIs: cloud, controllers, firewall managersDevice config over SSH, NETCONF, APIs
    DeletesRemoving code destroys the resourceRemoving a task removes nothing

    Since version 1.6 Terraform uses the Business Source License; OpenTofu is the open source fork with the same workflow.

    Traps

    • Committing terraform.tfstate to Git. It holds secrets and causes conflicting copies.
    • Thinking removing a resource block is harmless. Terraform destroys the real object on the next apply.
    How did you do?Own page

Expert7

  1. Two vendors both support the same OpenConfig model, yet your code works on one and fails on the other. Why?

    Say this first

    Support is declared per leaf, not per model. Vendors publish deviation modules that mark nodes as not supported or change their type or default, they implement only some optional features, and they support different model revisions. I read what the device announces (NETCONF hello capabilities or the YANG library, gNMI Capabilities) and test against that, per platform and per software release.

    Go deeper

    Where the differences hide

    • Deviations. A vendor module that says, for example:
      Output
      deviation "/oc-if:interfaces/oc-if:interface/oc-if:state/oc-if:counters/oc-if:in-fcs-errors" {
        deviate not-supported;
      }
      Other forms are deviate add, replace and delete to change defaults, types or constraints.
    • Features. if-feature makes nodes optional. The server lists the features it implements.
    • Revisions. The same module name with a different revision can rename or move nodes.
    • Semantics. Two implementations can accept the same leaf but apply it differently, for example when an interface needs a sub-interface 0 before an IP address is valid.

    How the device tells you

    NETCONF 1.0-style servers list each module in the hello as a capability URI:

    Output
    urn:ietf:params:xml:ns:yang:ietf-interfaces?module=ietf-interfaces&revision=2014-05-08&features=pre-provisioning,if-mib,arbitrary-names

    A deviations= parameter, when present, names the vendor deviation modules. Newer servers point to the YANG library (ietf-yang-library, RFC 7895 and RFC 8525), which lists modules, revisions, features and deviations as data you can query. gNMI returns supported models in the Capabilities response.

    How I protect the code

    • Pin the platform and release in the test lab to the ones in production.
    • Gather capabilities as part of pre-checks and fail fast if a required module or revision is missing.
    • Keep per-platform translation in one place instead of scattering if vendor == through the code.

    Traps

    • Testing on one release and deploying to another. Model revisions change between releases.
    How did you do?Own page
  2. Walk me through a full NETCONF change session, from connecting to closing, including what can go wrong.

    Say this first

    SSH to port 830 and request the netconf subsystem. Both sides send hello; the server's lists its capabilities and a session-id. I lock the candidate, send edit-config, run validate, then commit with confirmed and a timeout. I run my checks, send a confirming commit, unlock and close-session. Any failure comes back as an rpc-error with a type, tag, severity and message, and I discard-changes or let the confirmed commit roll back.

    Go deeper

    Step by step

    1. Transport. SSH to TCP 830, subsystem netconf. Authentication is normal SSH (password or key, often via AAA).
    2. Hello. The server sends its capabilities and a session-id; the client sends its own. If both list base:1.1, framing switches to chunked.
    3. Lock the candidate (and running, if the platform asks for it).
    4. edit-config to the candidate.
    5. validate the candidate (with :validate).
    6. commit with <confirmed/> and <confirm-timeout>.
    7. Check reachability and state from the automation side.
    8. Confirming commit (a plain <commit/>), then unlock and close-session.

    The same flow with ncclient

    Python
    from ncclient import manager
    
    CONFIG = """
    <config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
      <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
          <name>GigabitEthernet2</name>
          <description>to SW1 Gi1/0/48</description>
        </interface>
      </interfaces>
    </config>
    """
    
    with manager.connect(host="10.0.0.1", port=830, username="netops",
                         password="secret", hostkey_verify=False) as m:
        with m.locked(target="candidate"):
            m.discard_changes()
            m.edit_config(target="candidate", config=CONFIG)
            m.validate(source="candidate")
            m.commit(confirmed=True, timeout="120")
            # run post-checks here; if they fail, do nothing and the device rolls back
            m.commit()

    discard_changes() first clears anything another session left in a shared candidate. hostkey_verify=False is for a lab; in production load known host keys.

    What an error looks like

    Output
    <rpc-reply message-id="102" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
      <rpc-error>
        <error-type>protocol</error-type>
        <error-tag>lock-denied</error-tag>
        <error-severity>error</error-severity>
        <error-info>
          <session-id>31</session-id>
        </error-info>
      </rpc-error>
    </rpc-reply>

    error-type is transport, rpc, protocol or application. For lock-denied, error-info names the session holding the lock. The error-message text varies by vendor.

    Common failures

    • Port 830 refused: NETCONF not enabled, or an ACL on the management plane.
    • MissingCapabilityError in ncclient: you targeted the candidate on a device that does not offer it.
    • unknown-element or invalid-value: wrong namespace, a typo, or a node the platform does not support.
    • lock-denied: another session or a stuck session holds the lock; kill-session frees it if you are sure.

    Traps

    • Forgetting the confirming commit. The change works for two minutes and then quietly reverts.
    • Leaving a lock held by a crashed script. Use a context manager so the lock is always released.
    How did you do?Own page
  3. Design a CI/CD pipeline for network changes. What runs before and after the change?

    Say this first

    A merge request triggers lint and data validation, then renders configs and tests them offline (Batfish) or in a virtual lab (containerlab), then runs a dry run against production and posts the diff for review. After approval and inside the change window, deploy runs pre-checks, pushes in batches, runs post-checks that compare state with the pre-check snapshot, and rolls back if they fail. Every step leaves logs on the ticket.

    Go deeper

    Stages

    1. Lint: yamllint, ansible-lint, Python linters. Catches syntax before anything runs.
    2. Validate data: schema checks and rules (no duplicate VLAN IDs, every IP inside its prefix, names follow the standard).
    3. Render and test: build every device config from templates. Batfish loads them and answers questions: undefined references, ACL behavior, BGP sessions that will not come up, reachability.
    4. Lab: optional containerlab topology with virtual images to test the playbook end to end.
    5. Dry run: --check --diff or NAPALM compare_config against production, posted to the merge request.
    6. Approve and deploy: manual gate, change window, batches.
    7. Post-checks and rollback: compare with the pre-check snapshot; on failure, roll back automatically.

    Pre-checks and post-checks

    Capture: interfaces up, BGP and OSPF neighbors and prefix counts, route table size, MAC and ARP counts, STP root, CPU. Pre-checks also act as a gate: do not change a device that is already unhealthy. pyATS can learn features before and after and diff them; NAPALM getters and compliance_report work too.

    GitLab CI example

    YAML
    stages: [lint, test, plan, deploy]
    
    lint:
      stage: lint
      image: python:3.12
      script:
        - pip install yamllint ansible-lint
        - yamllint .
        - ansible-lint playbooks/
    
    validate-data:
      stage: test
      image: python:3.12
      script:
        - pip install -r requirements.txt
        - python tests/check_data.py
    
    plan:
      stage: plan
      script:
        - ansible-playbook playbooks/vlans.yml --check --diff
      rules:
        - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    
    deploy:
      stage: deploy
      script:
        - ansible-playbook playbooks/precheck.yml
        - ansible-playbook playbooks/vlans.yml
        - ansible-playbook playbooks/postcheck.yml
      rules:
        - if: $CI_COMMIT_BRANCH == "main"
          when: manual
      environment: production

    The plan and deploy jobs run on a runner inside the management network with access to devices and Vault.

    Traps

    • A pipeline with no post-checks. A push that "succeeded" can still have broken the network.
    • Running the deploy job from a shared cloud runner that cannot, and should not, reach production devices.
    How did you do?Own page
  4. You need to push a change to 800 switches with automation. How do you make it safe?

    Say this first

    I treat it as a staged rollout. Pre-checks gate each device, a dry run diff is reviewed, and the change goes to one or two canary devices first, then a small batch, then the rest, stopping on the first failure. Where the platform has it I use confirmed commit so a change that cuts my access rolls itself back. Post-checks run after every batch, and I keep out-of-band access, a tested rollback and a kill switch.

    Go deeper

    Before

    • Peer-reviewed merge request with a dry run diff for a sample of device types.
    • Tested on a lab device with the same platform and release.
    • Pre-checks: device reachable, healthy, config matches the expected baseline. Skip and report devices that fail.
    • Rollback written and tested, not just "re-run the old playbook".

    During

    YAML
    - name: Push new AAA config
      hosts: access_switches
      gather_facts: false
      serial: [1, 5, "10%", "25%"]
      max_fail_percentage: 0
      tasks:
        - name: Apply template
          cisco.ios.ios_config:
            src: aaa.j2
            save_when: modified

    serial grows the batches, and max_fail_percentage: 0 stops the rollout as soon as any device in a batch fails. forks limits how many devices are touched at once, which also protects TACACS servers.

    Safety nets per platform

    • Junos and NETCONF candidate: commit confirmed with a few minutes timeout.
    • IOS-XE with archive configured: configure terminal revert timer then configure confirm.
    • Changes that touch management access (AAA, ACLs on vty, management VRF) go last and slowest.

    After

    Post-checks per batch, compare with pre-checks, then a final report: changed, unchanged, failed, skipped.

    Traps

    • Running against all 800 at once because "it worked in the lab".
    • Changing AAA or management ACLs without a confirmed-commit or console fallback, and locking yourself out everywhere.
    How did you do?Own page
  5. How do you detect and deal with configuration drift?

    Say this first

    Drift is the difference between intended config (rendered from the source of truth and templates) and what is actually running. I detect it on a schedule by rendering the full intended config and diffing it against the device: NAPALM load_replace_candidate plus compare_config, Ansible check mode with diff, or controller compliance reports. Each difference is either an approved emergency change that goes back into the source of truth, or an unauthorized one that gets remediated by pushing intent again.

    Go deeper

    Where it comes from

    • CLI fixes during incidents that never made it back to the data.
    • Partial pushes that failed halfway.
    • Software upgrades that change defaults or how config is displayed.
    • Other tools writing to the same device.

    Why merge-only automation cannot see it

    If you only merge lines, extra config added by hand never shows up in your diff. Only a full intended config compared with the full running config (replace semantics) reveals additions as well as changes.

    Practical setup

    • Nightly job renders intended configs and diffs them with the running configs.
    • Ignore volatile lines: ! Last configuration change at ..., certificate blobs, ntp clock-period.
    • Report per device and per section, and track the drift count over time.
    • Backups (Oxidized or RANCID style) in Git give a timeline of when each line appeared.
    • Ansible: ios_config with diff_against: intended and intended_config, run with --check --diff.

    Policy

    Decide per section: some are enforced (AAA, SNMP, NTP, ACLs), where intent always wins and drift is corrected automatically; others are report-only while the team builds trust.

    Traps

    • Auto-remediating everything from day one. A wrong template then "fixes" correct configs across the estate.
    How did you do?Own page
  6. How would you bring an existing, hand-built network under automation?

    Say this first

    Read-only first: back up every config, build the inventory and source of truth from what exists, parse configs into data. Then take control one section at a time, starting with low-risk global config such as NTP, syslog, SNMP and banners. For each section I write the template, render it for every device and diff against the live config until the diff is empty or every remaining line is an agreed fix. Only then is that section enforced by automation and closed to CLI changes. Over months the coverage grows to the whole config.

    Go deeper

    Phases

    1. Discover: inventory, backups, NAPALM getters or gathered from resource modules, LLDP for topology.
    2. Populate the source of truth: import devices, interfaces, IPs and VLANs into NetBox. Clean the data as you go; this is where you find the duplicates and mysteries.
    3. Standardize: agree a standard per section, and accept that some devices must change to meet it.
    4. Template per section: render, diff, adjust the template or the data until the diff is understood.
    5. Enforce: push, then let drift checks protect the section.
    6. Expand: interfaces, VLANs, routing, ACLs, and finally full-config replace where the platform supports it.

    Why sections and not whole configs

    A whole-config replace on day one means every inconsistency in the estate becomes a change at once. Per section, each step is small, reviewable and reversible, and the team learns the tools on low-risk config.

    Traps

    • Starting with a full-config replace on production devices.
    How did you do?Own page
  7. Your Python script takes six hours to log in to 2,000 devices. How do you make it fast and safe?

    Say this first

    The job is I/O bound: most of the time is waiting on SSH and prompts, so run devices in parallel with a thread pool, asyncio or Nornir, which runs tasks across an inventory with 20 worker threads by default. Then add the safety parts: a sensible concurrency limit that the TACACS servers and vty lines can take, timeouts and limited retries per device, per-device results and logs, and a summary of what failed instead of stopping on the first error.

    Go deeper

    Thread pool with Netmiko

    Python
    import os
    from concurrent.futures import ThreadPoolExecutor, as_completed
    
    from netmiko import ConnectHandler
    from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException
    
    USER = os.environ["NET_USER"]
    PASSWORD = os.environ["NET_PASS"]
    
    def get_uptime(host):
        device = {"device_type": "cisco_ios", "host": host,
                  "username": USER, "password": PASSWORD}
        with ConnectHandler(**device) as conn:
            return conn.send_command("show version | include uptime")
    
    with open("hosts.txt") as f:
        hosts = [line.strip() for line in f if line.strip()]
    
    results, failures = {}, {}
    with ThreadPoolExecutor(max_workers=30) as pool:
        futures = {pool.submit(get_uptime, h): h for h in hosts}
        for fut in as_completed(futures):
            host = futures[fut]
            try:
                results[host] = fut.result()
            except (NetmikoTimeoutException, NetmikoAuthenticationException) as exc:
                failures[host] = type(exc).__name__
    
    print(f"{len(results)} ok, {len(failures)} failed")

    The same with Nornir

    Python
    from nornir import InitNornir
    from nornir_netmiko.tasks import netmiko_send_command
    from nornir_utils.plugins.functions import print_result
    
    nr = InitNornir(config_file="config.yaml")
    result = nr.filter(site="dc1").run(task=netmiko_send_command,
                                       command_string="show version")
    print_result(result)
    print(sorted(result.failed_hosts))

    Limits that bite

    • AAA: 30 parallel logins per second can overload TACACS+ servers. Ask before you raise concurrency.
    • vty lines: IOS often has only line vty 0 4 (five sessions). Your job plus engineers can run out.
    • Jump hosts and firewalls: connection rate limits and SSH session caps.
    • Errors: catch per device, retry transient failures once or twice with backoff, never retry auth failures in a loop (account lockout).

    Traps

    • Using multiprocessing for an I/O-bound job. It works but costs far more memory than threads for no gain.
    • Retrying failed logins in a tight loop and locking the service account in AAA.
    How did you do?Own page