cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1239
Views
5
Helpful
3
Replies

Automate Port Provisioning for Host Onboarding with Ansible/Python

Mike.Cifelli
VIP Alumni
VIP Alumni

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!

1 Accepted Solution

Accepted Solutions

Mike.Cifelli
VIP Alumni
VIP Alumni

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.

View solution in original post

3 Replies 3

Mike.Cifelli
VIP Alumni
VIP Alumni

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.

@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.

Mike.Cifelli
VIP Alumni
VIP Alumni

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.

Review Cisco Networking for a $25 gift card