ACI Best Practices Configurations for Curling

You’ve probably read through our ACI Best Practices Configuration post. If you haven’t , please do so now. Go ahead, I’ll wait.

(Are you done clicking those buttons in the GUI yet? Take your time. I won’t leave you, but I may nip out for a quick coffee while I wait).

All done? Welcome back! If you’re not hot for all that button-clicking, or if you want to check that the options are enabled as part of an Operations Dashboard or a script, you may want to configure these via the API. 

Not a programmer? Not a problem! We’ll use the curl utility to GET/POST data to demonstrate the API interfaces involved.

If you haven’t used curl before, or are not experienced with using curl to send API requests from the command line, check out our post Getting Started With Curl

Each option below assumes you have already authenticated and curl is storing the token as a cookie for use in each request (-b COOKIE). For more details on what each option is and why you want to configure it, please refer to Getting started with curl.

Also, we’ll be operating on each object directly, as opposed to performing operations on the top-level uni.xml object. This simplifies our JSON/XML data. Cisco has a whitepaper introducing the APIC object model, while our own post Getting Cozy With The API is a short introduction to the tools to help you explore the API and object model.

We’ve also included a “best_practices_for_curling.txt” file in the same Github repo to get you started with your own scripts. It contains just the curl commands and comments, without the shell script.

The Goods:

The demonstration code is posted at

It’s written in shell, and should work on any modern Unix/Linux system with bash 4.x. To install, simply clone the repo with git or use the download link to get a zip file, then follow the instructions in the README.

Before we dive into the curl specifics, let’s review the structure of the script. If you’re not familiar with writing shell scripts, The Linux Documentation Project has a beginner HOWTO and an advanced HOWTO, though I personally like this tutorial as a good intro for the beginner (all three links are ad-free sites). If you like paper manuals, the OG Unix Programming Environment from Kernighan and Pike is the definitive resource despite its age. I challenge you to find any other computer-related reference manual still relevant after 35 years!

To run the script and configure the settings on your APIC, just pass the word “enable” or “disable” on the command line. The script will then run each option to enable or disable accordingly. Simple, no?

$ ./ enable
Activating MisCabling Protocol
Activating "Disable Remote Endpoint Learning" and "Enforce Subnet Check"
Activating Endpoint Loop Protection
Activating IP Aging
Activating Rogue Endpoint Detection
Activating Strict COOP Group Policy
Activating BFD for Fabric Facing Interfaces
Activating Preserve COS through the ACI Fabric.

$ ./ disable
Deactivating MisCabling Protocol
Dectivating the "Disable Remote Endpoint Learning" and "Enforce Subnet Check"
Deactivating Endpoint Loop Protection.
Deactivating IP Aging
Deactivating Rogue Endpoint Detection
Deactivating Strict COOP Group Policy
Deactivating BFD for Fabric Facing Interfaces
Deactivating Preserve COS through the ACI Fabric.

The Walkthrough

Let’s review the script itself. First thing you see under Configuration section is your username, password, and controller hostname/IP address. The HOST variable will be used to construct all API requests, while USER/PASS are used for authentication. Input your information here. Examples are included for reference. Note, this is stored in plain text, so if you share the script with your colleagues, be sure to scrub your user info first, or point them to the github repo to get it directly.


We also need to configure the script to run each setting listed in the original article. Since you may have a need to override a specific setting (eg, you might need a different setting for Endpoint Loop Detection), the next section will be to enable/disable each configuration option for the script. Note, this does not enable/disable the setting on your controller! This only tells the script whether to run the option or not.

The “export” tells the script to make this variable available to any subprocesses. Setting YES to any option will tell the script to execute the config setting. Change to NO or set to anything else to skip the config option when run.

export MCP=YES
export REL_ESC=YES

There are a couple of curl-related options to configure, but are optional. COOKIEFILE controls where the cookie/token/session info is kept. It defaults to your current working directory, which may not be writable. You can specify an alternate location writable by your user, if needed.

We also want to configure specific options for our environment. We include the -k and -s as defaults. The -k switch allows curl to accept certificates without a proper chain of trust (eg, self-signed). If you are using an internal CA or otherwise have trusted certificates, you can omit this. The -s option suppresses the status output, which gives you a progress bar and other niceties that are useful for interactive work, but not good for scripts.

We also configure the content-type as XML since that’s the data we’re passing along with POST as the default HTTP method (because we’re POSTing the XML data to the API web service).

The COOKIEFILE is where to store the session/authentication token. It defaults to YOUR current working directory, which may not be writable, You can specify a specific location if needed (eg, /tmp, or $HOME).

The overall program flow in the demo works like this:

The MAIN section at the bottom is where the program looks for an argument on the command line and decides what action to take. If no parameter is passed (tested against the shell variable ?#, which is a special variable telling you the number of arguments passed to the script), then it runs the usage function, which in turn prints a usage/help text.

If you type “ enable”, then it executes two defined functions, “auth” and “enable_all”, in sequence. Likewise, the “disable” command runs auth and disable_all. Pretty simple.

The enable_all and disable_all consequently runs a command/function for each setting, to enable or disable respectively. Each function must be defined first before we can use them, which is why the MAIN section is all the way at the bottom. If we were writing a much larger program in shell, we might move the functions into a separate file and simply load them at the top.


The FUNCTIONS section in the middle is where the magic happens.

Each of the Best Practices suggestions are broken out into separate functions for each configuration option, along with a further split for each “enable” and “disable setting”.

The first function defined is the auth() function, which is called to create a session token/cookie to store info. In this demo script, I call it each time the script is executed, since it’s very likely that the token will expire in between uses.

When the function is called, it runs the command “curl”, followed by some options:

auth() {
	curl $CURL_OPTS http://$HOST/api/mo/aaaLogin.xml -d "<aaaUser name=$USER pwd=$PASS/>" -c $COOKIEFILE > /dev/null


  • $CURL_OPTS is from our curl options variable above. It includes the -k, -s, and HTTP headers so we can configure it once and apply it to all statements
  • https://$HOST/api/mo/aaaLogin.xml is the URI for authentication. The HOST variable is defined at the top.
  • -d “<aaaUser name=$USER pwd=$PASS> is the XML data for authentication. The variables $USER and $PASS were also defined at the top of the script.
  • -c $COOKIEFILE tells the script to store the session info in the COOKIEFILE we defined earlier.
  • Finally, we redirect the output to /dev/null. The curl command outputs response information in XML to screen. For a script like this, we don’t want that. For a more interactive program, we might capture the output and then perform another action based on it.

Next is the usage() function, which is executed whenever you run the script with no arguments or when you type something other than enable/disable. It’s nice to provide guidance to other people using your tools.

usage() {
	echo " "
	echo "This is a simple demo script to enable/disable specific settings based on the Best Practices article"
	echo "at"
	echo " "
	echo "For the full list of options enabled/disabled or to read a walk-thru of this script, "
	echo "please see [ insert new link ]. "
	echo " "
	echo "Usage:"
	echo " enable"
	echo " disable"

The skip() function simply gives us info when a configuration is skipped; that is, when the option is not set to YES in the configuration section above. There’s a new variable called $OPTION, which we’ll cover in a moment. When a command completes successfully, there’s usually an exit code of 0 to indicate this. Any non-zero code tells us there’s a problem, or at least something happened that we want to know about. I used “return 1” to send back a non-zero exit status indicating that the option was not executed.

You can test for that value (using the “$?” special variable which records the exit status of the previous command) and do something with it.

Now we’re gonna get into the good stuff.

Each APIC configuration function includes a comment for where each setting is in the GUI if you wanted to double-check it. For example, in the Mis-Cabling Protocol setting, you can verify it in the GUI by going to Fabric > Access Policies > Global Policies > MCP Instance Policy Default.

enable_mcp() {
	if [ $MCP = "YES" ]; then 
		echo "Activating MisCabling Protocol"
		curl $CURL_OPTS https://$HOST/api/node/mo/uni/infra/mcpInstP-default.xml -d '<mcpInstPol adminSt="enabled" annotation="" ctrl="pdu-per-vlan" descr="" dn="uni/infra/mcpInstP-default" initDelayTime="180" loopDetectMult="2" loopProtectAct="port-disable" name="default" nameAlias="" ownerKey="" ownerTag="" txFreq="2" txFreqMsec="0"/>' -b $COOKIEFILE  > /dev/null

Let’s review the code snippet above.

First, the function name is enable_mcp(), cleverly chosen because MCP is what we’re working with, and enable is the action we want to take. Ta Da!

We include a test for the variable MCP and whether it is set to YES. Remember, MCP is the variable we configured earlier to tell the script we want to change the setting. If we kept the YES setting, then we print “Activating MCP Protocol” and then run the following curl command with the provided POST data.

The DN of the object we’re working with is represented by the URI: https://$HOST/api/node/mo/uni/infra/mcpInstP-default.xml

The XML data we’re providing starts again with the DN of the object in the data model, mcpInstPol, and each setting we want as a key=value pair. The ‘adminSt=”enabled”‘ enables the policy setting (boolean: enabled/disabled) while the other options configure specific attributes.

The else statement tells us that if the MCP setting is NOT set to “YES”, then we should run this instead of the curl command. First, it sets the OPTION variable based on the configuration parameter for MCP, and then runs the “skip” function we discussed earlier. If you remember, the skip function provide information that the configuration is being skipped, probably because we didn’t set it to YES. Setting the $OPTION variable lets us reuse the function each time a config is skipped.

Let’s look at the disable_mcp function.

disable_mcp() {
	if [ $MCP = "YES" ]; then 
		echo "Deactivating MisCabling Protocol"
		curl $CURL_OPTS https://$HOST/api/node/mo/uni/infra/mcpInstP-default.xml -d '<mcpInstPol adminSt="disabled"/>' -b $COOKIEFILE > /dev/null

We’re doing the same thing here as we did for enable. Testing that $MCP=YES in the config at the top, otherwise skip this option and tell the user that MCP options needs to be configured before we can do anything to it.

The curl statement is just like the enable_mcp curl statement, except the XML data is a bit different. In this case, we set adminSt to “disabled”, which turns off the setting. None of the other parameters (like initDelayTime=”180″) are needed, because we’re simply turning all this off. If you wanted to update the parameters, like change initDelayTime=”180″ to initDelayTime=”90″, you would use the curl statement from enable_mcp, but change the XML as needed.

The other functions are constructed the same way, one for each enable and disable setting, for each configuration option in our Best Practices article. Take a look at the best_practices_for_curling.txt file in the github repo and the original article.

You may have noticed that I used the term “Activating” and “Deactivating” rather than enable/disable in the script itself. I did this because the Disable Remote EP Learning is a setting where you enable the setting to disable the feature. So technically, we’re disabling remote EP learning, but we’re doing it by enabling the setting. Confused? Yeah, me too.

Enable to disable. Disable to enable. Fnord.

When confronted with lingual gymnastics like this, I tend to side with the users who just want to get their dang work done so they can go home and drink a beer. So, I chose to use activate/deactivate to refer to the actual setting itself (we’re activating the “Disable Remote EP Learning” setting), while enable/disable is the broader objective of the script itself. We want to “enable” the best practices, which includes activating settings like Disable Remote EP Learning.


You may have wondered why there isn’t a command to simply list the existing parameters. The reason is that reading and parsing the XML data for displaying on screen is a bit more complex than POSTing the prepared XML. Not to worry! We’ll cover that in a future post very soon. We could also add a “reset defaults” option for completeness.

Did you notice that each time you run the script, you’re sending up to nine API calls each time it’s executed? That’s not usually an issue for a one-off script, but isn’t a good practice for a utility in regular use (like an actual Ops Dashboard). A more robust approach would be to use each supplied configuration to construct a single XML data file, then send one POST request to the root/base URI with the new configuration.

We could also include a check on the session token/cookie data to determine if the token is still valid before running another authentication request, while also accepting user/pass as command line parameters as well (or at least store them in a config file separate from the script itself), and throw a helpful error if no credentials are found.

Finally, since we’re using the same URI for each enable/disable function, we could combine those into a single function that reads the original parameter (enable/disable) and sends the correct data accordingly.

If you want to make any of these improvements to the script, feel free to send us a pull request on github. If you have a script of your own that you would like to share with other ACI users, let us know!


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.