12-01-2021 10:25 AM - edited 12-01-2021 10:26 AM
A while back I created a python script that would essentially automate 3/6 phases for deploying a new edge node to an existing fabric. I have now migrated things into ansible and currently can accomplish automating 5/6 phases via consuming APIs. An overview of the 6 phases are as follows:
1 - Add NAD to inventory (dnac_add_device_playbook_scrubbed.yml)
2 - Add Device to Site (dnac_add_nad_to_site_playbook_scrubbed.yml)
3 - Update NAD Details in ISE (ise_sda_update_nad_det_playbook_scrubbed.yml)
4 - Provision NAD (dnac_prov_nad_playbook_scrubbed.yml)
5 - Add device to Fabric (dnac_add_en_to_fabric_playbook_scrubbed.yml)
6 - Provision ports (stuck here) (custom_dnac_en_int_prov_playbook_scrubbed.yml)
So for phase 6 I decided to break out into a custom ansible module to rely on python to accomplish the goal. The code for p6 is as follows:
#!/usr/bin/python
import requests
import json
from ansible.module_utils.basic import AnsibleModule
def main():
# Define your module arguments that a user will pass to the module
module_args = dict(
switch_type=dict (type="str", required=True),
ip_addr=dict (type='str', required=True),
)
# Create an instance of AnsibleModule class
module = AnsibleModule(argument_spec=module_args)
result = dict(changed=False, msg="")
# Extract the arguments provided from user
switch_type = module.params["switch_type"]
ip_addr = module.params["ip_addr"]
#Select proper interface names based on user var
if switch_type == "WS-C3850-24XS-E":
file_name = "WS-C3850-24XS-E_Int_Names.txt"
elif switch_type == "WS-C3650-24TS-S":
file_name = "WS-C3650-24TS-S_Int_Names.txt"
elif switch_type == "C9300-24T":
file_name = "C9300-24T_Int_Names.txt"
elif switch_type == "C9300-48S":
file_name = "C9300-48S_Int_Names.txt"
elif switch_type == "C9300-48T":
file_name = "C9300-48T_Int_Names.txt"
elif switch_type == "C9300-48UXM":
file_name = "C9300-48UXM_Int_Names.txt"
elif switch_type == "C9500-16X":
file_name = "C9500-16X_Int_Names.txt"
else:
file_name = "C9500-40X_Int_Names.txt"
#Looping Switch Type file to get interface names and consuming api for port provisioning
file = open("/usr/share/ansible/sda_en_types/%s"%file_name)
intf = file.readline()
while intf:
#Get Authz Token for API Interaction
data = get_token()
#Consuming DNAC API to add port assignment for user device in SDA Fabric
api_url = 'https://x.x.x.x/dna/intent/api/v1/business/sda/hostonboarding/user-device'
auth = {
'X-Auth-Token' : data,
'Content-type' : 'application/json',
'Accept' : "application/json",
}
payload ={
"deviceManagementIpAddress": ip_addr,
"siteNameHierarchy": "Global/APG/C5ISR_CENTER",
"interfaceName": intf,
"dataIpAddressPoolName": "",
"voiceIpAddressPoolName": "",
"authenticateTemplateName": "Closed Authentication",
"interfaceDescription": "User Device",
}
r = requests.post(url=api_url, headers=auth, json=payload, verify=False)
file.close()
result["msg"] = r
# After successful module execution, pass the key/value results and exit module
module.exit_json(**result)
def get_token():
API_URL = 'https://x.x.x.x/api/system/v1/identitymgmt/token'
AUTH = 'xxxx','xxxx'
HEADER = {'content-type': 'application/json'}
r = requests.post(url=API_URL, auth=AUTH, headers=HEADER, verify=False)
data = r.text
key = json.loads(data)
token = key['Token']
return token
if __name__ == "__main__":
main()
Unfortunately at the moment it never seems to finish and actually provision the ports as desired so I am still troubleshooting. I can see multiple audit logs in DNAC stating: "Intent API "Add Port assignment for user device in SDA Fabric" Executed"
Note that I do not see it appear under tasks either, which I find weird. The idea is for admin to launch the ansible workflow template and pump in variables. Based on user platform selection and other vars, interface names are selected to ensure payload is accurate.
DNAC version: 2.2.3.3
See attached for phases 1-5 Ansible playbooks. Note that vars will/would need updated as I scrubbed some content. Seeking thoughts/suggestions/experiences on how to accomplish p6. Lastly, hopefully the playbooks will help others. TIA!
Solved! Go to Solution.
12-10-2021 10:46 AM
FYSA per TAC this is a limitation with the API. The API can be used to automate port provisioning for static assignment. Hence the requirement of either or (data/voice) in the payload. As of today (2.2.3.3) you cannot consume this API (Add Port Assignment For User Device In SDA Fabric) and automate port provisioning for dynamic assignment. I submitted a request via make-a-wish and shared info with my Cisco reps.
12-09-2021 04:48 AM
I was able to further troubleshoot the issue by narrowing it down. The issue is with the payload. Here is the returned status of the failed job:
"status" : "failed",
"description" : "Both dataIpAddressPoolName and voiceIpAddressPoolName are empty or invalid in request body. Provide a valid dataIpAddressPoolName and or voiceIpAddressPoolName and try again.",
}
payload ={
"deviceManagementIpAddress": ip_addr,
"siteNameHierarchy": "Global/xxx/xxx",
"interfaceName": intf,
"dataIpAddressPoolName": null,
"voiceIpAddressPoolName": null,
"authenticateTemplateName": "Closed Authentication",
"interfaceDescription": "User Device",
}
Within the UI we can leave them blank and rely on ISE to dynamically assign policy. Is this possible with the API? If so, how do I pass the vars since they are both required in the payload per api docs? I have tried "null" and "" which both result in failure.
12-09-2021 09:40 AM
@Mike.Cifelli I think it's reasonable to expect that the UI and API allow for the same behavior. I did a quick internal search for the error description you posted and came up empty. Please open a TAC case so they can get this fixed for you.
12-10-2021 10:46 AM
FYSA per TAC this is a limitation with the API. The API can be used to automate port provisioning for static assignment. Hence the requirement of either or (data/voice) in the payload. As of today (2.2.3.3) you cannot consume this API (Add Port Assignment For User Device In SDA Fabric) and automate port provisioning for dynamic assignment. I submitted a request via make-a-wish and shared info with my Cisco reps.
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