04-21-2020 08:13 AM
Hi Experts,
I'm working to automate infrastructure deployment using Ansible and would like to implement the following:
There is an ACI fabric containing 5 APICs. I have a script that knows how to connect to an APIC and send some configuration requests. However, it does not:
1. Select a controller from the list
2. Check if the controller is healthy and ready to receive configuration requests.
I can take care of 1. However, can someone point me in the right direction and clarify what API calls need to be sent to validate that the controller is not in read-only mode. I'm fairly new to ACI and tried to find some example in Internet - and couldn't find anything.
Basically what I'm trying to achieve, is the following:
1. Choose an APIC from the list of 5 APIC controllers
2. Authenticate and check it's status.
3. If the status is read-only - then pick the next APIC in the list and start at 2.
4. If the status is "healthy" - then send all the configuration requests.
Any help would be appreciated
Regards
Alex
Solved! Go to Solution.
04-21-2020 08:54 AM - edited 04-21-2020 08:54 AM
You can collect the APIC info using aci_rest module, and save the returned data.
- name: Get APIC info aci_rest: host: apic username: admin password: SomeSecretPassword method: get path: /api/node/class/infraWiNode.json delegate_to: localhost register: apic_info
You can then run the rest of the tasks if you have fully-fit present in your variable.
However, I would like to add some notes to your logic: there is no need to check for fully-fit all your APICs. If one if not fully fit, then simply do not run your playbook. Why? Because most likely all the other ones are not fully-fit. And even if they are fully fit, you should not push config because you could break your configuration, and then the recovery would be troublesome.
Cheers,
Sergiu
04-21-2020 12:57 PM
Hey @AlexNastas74909
Just to build on what Sergiu shared with you, you can go through the returned content and print out just the health of each APCI and you can print out just the ones that are NOT fully-fit and of course use that in follow on tasks.
Having automated ACI fabric builds several ways (started out with ACIToolkit, then using Python and requests, and then Ansible) in my opinion, it is far simpler to use Python if you want to add logic to your workflow. Not saying you can't do it with Ansible, its just a bit more convoluted (I find).
Sample:
tasks: - name: "Execute REST Call Action: {{ method | upper }} Query: {{ query_path }} " aci_rest: host: "{{ aci_host }}" username: "{{ aci_user }}" password: "{{ aci_pwd }}" validate_certs: no method: "{{ method }}" path: "{{ query_path }}" delegate_to: localhost register: query_result - name: Display RAW APIC Results debug: var: query_result - name: Summary of APIC Cluster Health debug: msg: "APIC: {{ item['infraWiNode']['attributes']['nodeName'] }} Health is {{ item['infraWiNode']['attributes']['health'] }} " with_items: "{{ query_result['imdata'] }}" - name: Look for APICs which are NOT FULLY FIT debug: msg: "APIC NOT FULLY FIT: {{ item['infraWiNode']['attributes']['nodeName'] }} Health is {{ item['infraWiNode']['attributes']['health'] }} " with_items: "{{ query_result['imdata'] }}" when: item['infraWiNode']['attributes']['health'] != 'fully-fit'
Outputs something like this:
TASK [Summary of APIC Cluster Health] **************************************************************************************************************** ok: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) => { "msg": "APIC: apic1 Health is fully-fit " } TASK [Look for APICs which are NOT FULLY FIT] ******************************************************************************************************** skipping: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) skipping: [sandboxapicdc.cisco.com] TASK [Look for APICs which ARE FULLY FIT] ************************************************************************************************************ ok: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) => { "msg": "APIC FULLY FIT: apic1 Health is fully-fit " } PLAY RECAP ******************************************************************************************************************************************* sandboxapicdc.cisco.com : ok=4 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 root@38c3258b2fd7:/ansible_local/cisco_aci#
I have a public repository with some sample playbooks in case they can be of help to you:
04-21-2020 08:42 AM
you can verify if the controller is healthy with the following command..
a-apic1# show controller detail id 1
ID : 1*
Name : a-apic1
UUID : 003ea778-57ea-11ea-b84f-85e33067b015
Pod ID : 1
Address : 10.0.0.1
In-Band IPv4 Address : 14.2.104.228
In-Band IPv6 Address : fc00::1
OOB IPv4 Address : 10.122.141.98
OOB IPv6 Address : fe80::5a97:bdff:fe5:dd5a
Serial Number : FCH1929V153
Version : 4.2(3l)
Commissioned : YES
Registered : YES
Approved : APPROVED
Valid Certificate : yes
Validity End : 2025-07-29T17:05:58.000+00:00
Up Time : 55:21:50:30.000
Health : fully-fit. <<<<<
Failover Status : 0
a-apic1#
other ways to get the same info is via
1. moquery the following object. >>> moquery -c infra.WiNode
2. or via API >>> https://<APIC_IP>/api/node/class/infraWiNode.json
04-21-2020 08:54 AM - edited 04-21-2020 08:54 AM
You can collect the APIC info using aci_rest module, and save the returned data.
- name: Get APIC info aci_rest: host: apic username: admin password: SomeSecretPassword method: get path: /api/node/class/infraWiNode.json delegate_to: localhost register: apic_info
You can then run the rest of the tasks if you have fully-fit present in your variable.
However, I would like to add some notes to your logic: there is no need to check for fully-fit all your APICs. If one if not fully fit, then simply do not run your playbook. Why? Because most likely all the other ones are not fully-fit. And even if they are fully fit, you should not push config because you could break your configuration, and then the recovery would be troublesome.
Cheers,
Sergiu
04-21-2020 12:57 PM
Hey @AlexNastas74909
Just to build on what Sergiu shared with you, you can go through the returned content and print out just the health of each APCI and you can print out just the ones that are NOT fully-fit and of course use that in follow on tasks.
Having automated ACI fabric builds several ways (started out with ACIToolkit, then using Python and requests, and then Ansible) in my opinion, it is far simpler to use Python if you want to add logic to your workflow. Not saying you can't do it with Ansible, its just a bit more convoluted (I find).
Sample:
tasks: - name: "Execute REST Call Action: {{ method | upper }} Query: {{ query_path }} " aci_rest: host: "{{ aci_host }}" username: "{{ aci_user }}" password: "{{ aci_pwd }}" validate_certs: no method: "{{ method }}" path: "{{ query_path }}" delegate_to: localhost register: query_result - name: Display RAW APIC Results debug: var: query_result - name: Summary of APIC Cluster Health debug: msg: "APIC: {{ item['infraWiNode']['attributes']['nodeName'] }} Health is {{ item['infraWiNode']['attributes']['health'] }} " with_items: "{{ query_result['imdata'] }}" - name: Look for APICs which are NOT FULLY FIT debug: msg: "APIC NOT FULLY FIT: {{ item['infraWiNode']['attributes']['nodeName'] }} Health is {{ item['infraWiNode']['attributes']['health'] }} " with_items: "{{ query_result['imdata'] }}" when: item['infraWiNode']['attributes']['health'] != 'fully-fit'
Outputs something like this:
TASK [Summary of APIC Cluster Health] **************************************************************************************************************** ok: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) => { "msg": "APIC: apic1 Health is fully-fit " } TASK [Look for APICs which are NOT FULLY FIT] ******************************************************************************************************** skipping: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) skipping: [sandboxapicdc.cisco.com] TASK [Look for APICs which ARE FULLY FIT] ************************************************************************************************************ ok: [sandboxapicdc.cisco.com] => (item={u'infraWiNode': {u'attributes': {u'status': u'', u'dn': u'topology/pod-1/node-1/av/node-1', u'uid': u'0', u'routableIpAddr': u'0.0.0.0', u'extMngdBy': u'', u'adminSt': u'in-service', u'mutnTs': u'2020-04-19T19:35:08.522+00:00', u'chassis': u'10220833-ea00-3bb3-93b2-ef1e7e645889', u'nameAlias': u'', u'apicMode': u'active', u'targetMbSn': u'', u'id': u'1', u'mbSn': u'TEP-1-1', u'childAction': u'', u'lcOwn': u'local', u'nodeName': u'apic1', u'addr': u'10.0.0.1', u'failoverStatus': u'idle', u'podId': u'1', u'name': u'', u'operSt': u'available', u'health': u'fully-fit', u'monPolDn': u'uni/fabric/monfab-default', u'modTs': u'2020-04-19T19:35:50.720+00:00', u'annotation': u'', u'cntrlSbstState': u'approved'}}}) => { "msg": "APIC FULLY FIT: apic1 Health is fully-fit " } PLAY RECAP ******************************************************************************************************************************************* sandboxapicdc.cisco.com : ok=4 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 root@38c3258b2fd7:/ansible_local/cisco_aci#
I have a public repository with some sample playbooks in case they can be of help to you:
04-22-2020 06:29 AM
Wow, these answers are great. Thanks a lot for the clarification.
Regards
Alex
12-10-2021 09:29 AM
We are using following task. The core is the same as the other comments. Code was taken from
ansible documentation of aci collection - operational examples.
This the first task we are running before other ansible tasks. It waits till the apic cluster is fully-fit.
- name: "Waiting for all APICs to be fully-fit"
aci_rest:
host: "{{cred.host}}"
username: "{{cred.username}}"
password: "{{cred.password}}"
validate_certs: "{{cred.validate_certs}}"
#
path: /api/node/class/infraWiNode.json?query-target-filter=wcard(infraWiNode.dn,"topology/pod-1/node-1/av")
register: infrawinode
until: >
infrawinode and
infrawinode.totalCount|int >= groups['apic']|count >= 3 and
infrawinode.imdata[0].infraWiNode.attributes.health == 'fully-fit' and
infrawinode.imdata[1].infraWiNode.attributes.health == 'fully-fit' and
infrawinode.imdata[2].infraWiNode.attributes.health == 'fully-fit'
retries: 30
delay: 30
delegate_to: localhost
when: not ansible_check_mode
tags: always
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide