Skip to content

Commit ae823c5

Browse files
authored
Merge pull request #87 from CiscoDevNet/kl-redirects
Updating lab IDs and implementing redirects
2 parents b2374ca + b43aa71 commit ae823c5

34 files changed

+848
-8
lines changed

labs/coding-101-rest-basics-ga/coding-101-rest-basics-ga.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"redirectTo": "what-are-rest-apis",
23
"labId": "coding-101-rest-basics-ga",
34
"title": "Coding 101 - REST API Basics",
45
"slug": "Learn the basics of REST APIs. Use POSTMAN and the API for the DNA Center to learn how to make a REST API call.",
@@ -66,6 +67,5 @@
6667
],
6768
"authors": [
6869
{"name": "mcampos", "email": "[email protected]"}
69-
],
70-
"redirectTo": "what-are-rest-apis"
70+
]
7171
}

labs/coding-102-rest-python-ga/coding-102-rest-python-ga.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"redirectTo": "intro-python-part2",
23
"labId": "coding-102-rest-python-ga",
34
"title": "Coding 102: Calling REST APIs from Python",
45
"slug": "Learn the basics of how to call and consume a REST API in Python",
@@ -63,6 +64,5 @@
6364
"authors": [
6465
{"name": "brtiller", "email": "[email protected]"}
6566
],
66-
"active": true,
67-
"redirectTo": "intro-python-part2"
67+
"active": true
6868
}

labs/coding-201-parsing-xml/coding-201-parsing-xml.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
2-
"labId": "parsing-xml",
2+
"redirectTo": "parsing-xml",
3+
"labId": "coding-201-parsing-xml",
34
"title": "Parsing XML",
45
"slug": "Learn the basics of how to use parse XML results using Python.",
56
"byod": true,
@@ -13,7 +14,7 @@
1314
"file": "3.md"},
1415
{"title": "Step 4: Get XML elements in Python",
1516
"file": "4.md"},
16-
{"title": "Step 5: Find additional resources",
17+
{"title": "Step 5: Find additional resources",
1718
"file": "5.md"}
1819
],
1920
"tags": [

labs/coding-202-parsing-json/coding-202-parsing-json.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
2-
"labId": "parsing-json",
2+
"redirectTo": "parsing-json",
3+
"labId": "coding-202-parsing-json",
34
"title": "Parsing JSON",
45
"slug": "Learn the basics of how to use parse JSON results using Python.",
56
"byod": true,
67
"time": "15",
78
"files": [
89
{"title": "Step 1: Make an HTTP REST call with Python",
910
"file": "1.md"},
10-
{"title": "Step 2: Use the JSON Python library",
11+
{"title": "Step 2: Use the JSON Python library",
1112
"file": "2.md"},
1213
{"title": "Step 3: Iterate through a JSON dictionary in Python",
1314
"file":"3.md"},

labs/parsing-json/1.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Parsing JSON using Python
2+
3+
In this Learning Lab, you learn the basics of parsing JSON content using Python. The example runs a query on the [ACI Always-On Sandbox](https://devnetsandbox.cisco.com/RM/Diagram/Index/5a229a7c-95d5-4cfd-a651-5ee9bc1b30e2?diagramType=Topology "ACI Always-On Sandbox") to request data for available configuration on the fabric. You will then parse this returned data and print it to the screen for human readability.
4+
5+
To learn more about ACI and its API, you can review the DevNet [Learning Lab track for ACI programmability](https://developer.cisco.com/learning/tracks/aci-programmability), which teaches a variety of different ways to interact with ACI programmatically. ACI is used as a basis for these labs as the managed object model within ACI allows us to query the same object or class within the fabric and return both XML and JSON data structures, simply based on changing the file-type within the REST URL.
6+
7+
## Objectives
8+
9+
When you have completed this Learning Lab, you will be able to:
10+
11+
* Understand the basics of reading and parsing HTTP content using Python.
12+
* Learn how to use Python to extract only the JSON data you want using the JSON library.
13+
14+
## Prerequisites
15+
16+
* Complete the [Coding Fundamentals](https://developer.cisco.com/learning/modules/programming-fundamentals) and [REST API Fundamentals](https://developer.cisco.com/learning/modules/rest-api-fundamentals) Learning Lab modules if you are unfamiliar with Python and retrieving results from a RESTful service and the [Parsing XML using Python Lab](lab/coding-201-parsing-xml/step/1 "Parsing XML using Python Lab") for a similar approach to retrieving data using XML.
17+
18+
* You should also have a basic familiarity with JSON. Otherwise, consider visiting the [W3Schools JSON Tutorial](https://www.w3schools.com/js/js_json_intro.asp "W3Schools JSON Tutorial") to get a firm base to build upon.
19+
20+
* For this lab, use Python 3.4+. To verify your version, enter the following command in a terminal:
21+
22+
```
23+
python --version
24+
```
25+
26+
If you are on a DevNet Zone station, the correct version of Python should already be installed.
27+
28+
# Step 1: Make an HTTP REST call with Python
29+
30+
> **Note**: This lab leverages the DevNet Always-On ACI Sandbox, however you can substitute this with a local APIC if you have one available. The sets of sample code contained within this lab will not perform any write or configuration actions against the fabric; they will only request currently configured data.
31+
32+
To get started, this walk-through shows creating a simple Python script that sends an HTTP request to the ACI sandbox.
33+
34+
1. Open a text editor and create a file named `get-tenant-json.py`.
35+
3. Add the following lines to `get-tenant-json.py`:
36+
37+
```python
38+
import requests
39+
# We need to import the JSON library just to handle our request to the APIC for login
40+
import json
41+
# We need to log in to the APIC and gather a token, before we can access any data
42+
# Let's construct a request with a body
43+
44+
# We'll need to disable certificate warnings
45+
requests.packages.urllib3.disable_warnings()
46+
47+
# We need to have a body of data consisting of a username and password to gather a cookie from APIC
48+
encoded_body = json.dumps({
49+
"aaaUser": {
50+
"attributes": {
51+
"name": "admin",
52+
"pwd": "ciscopsdt"
53+
}
54+
}
55+
})
56+
57+
# Now lets make the request and store the data
58+
resp = requests.post("https://sandboxapicdc.cisco.com/api/aaaLogin.json", data=encoded_body, verify=False)
59+
60+
# This stores the received APIC-cookie from the login as a value to be used in subsequent REST calls
61+
header = {"Cookie": "APIC-cookie=" + resp.cookies["APIC-cookie"]}
62+
63+
# Now we make a call towards the tenant class on the ACI fabric with the proper header value set.
64+
# We leverage the .xml ending to receive the data back as XML. We're adding health and faults to the printout to ensure that we get levels of data back from the APIC
65+
tenants = requests.get("https://sandboxapicdc.cisco.com/api/node/class/fvTenant.json?rsp-subtree-include=health,faults", headers=header, verify=False)
66+
67+
# Requests stores the text of the response in the .text attribute. Lets print it to see raw JSON
68+
print(tenants.text)
69+
```
70+
There is a lot of manipulation going on with the top part of this script, but most of it deals with the methods needed to authenticate towards the ACI fabric, needing a username and password to authenticate, receive the response from the fabric and gather a token, then send this token in a subsequent call to the fabric to pull the ACI tenant information out in JSON format. However, this snippet:
71+
72+
- imported the `request` library to easily interact with the ACI fabric's REST APIs
73+
- constructed a call to authenticate against the APIC and store the authentication token as a cookie to be used later
74+
- created a request to ask for all configured tenants in the fabric using the stored authentication cookie
75+
- accessed the response from the query and printed the raw JSON output to the console/terminal <br>
76+
77+
4. Save the `get-tenant-json.py` file. To download or review the current code, you can get it from GitHub [here](https://github.com/CiscoDevNet/coding-skills-sample-code/blob/master/coding202-parsing-json/get-tenant-json-1.py).
78+
79+
5. Open a command prompt.<br>
80+
<br>
81+
For Windows, enter:
82+
83+
```
84+
cd %USERPROFILE%\Desktop
85+
```
86+
87+
For OS X, enter:
88+
89+
```
90+
cd ~/Desktop
91+
```
92+
93+
6. In the command prompt window, enter:
94+
95+
```
96+
python get-tenant-json.py
97+
```
98+
99+
7. The command line/terminal displays the JSON data retrieved by the script.
100+
101+
![](assets/images/json-output-new.png)
102+
103+
--------------------------------------------------------------------------------
104+
105+
This presentation of the JSON data isn't very useful. Next, clean up the presentation to get a better view of the structure and content of the data.
106+
107+
**Next step:** Proceed to Step 2: Use the JSON Python library.

labs/parsing-json/2.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
## Step 2: Use the JSON Python library
2+
3+
### Use Python's JSON object
4+
5+
The large JSON result you got in the previous step is a string. It has no special properties beyond that. To parse it, you would parse it as a string. However, Python comes with a standard `json` library for encoding/decoding and accessing JSON content. This library uses patterns that are familiar to Python developers. When you load a string using the `json` decoder, it converts JSON objects into Python [dictionaries](https://docs.python.org/3.4/reference/expressions.html#dictionary-displays) and JSON arrays into Python [lists](https://docs.python.org/3.4/reference/expressions.html#list-displays). This is useful because it then enables you to access and iterate through JSON as Python data.
6+
7+
Another useful tool of the JSON library is it's ability to "pretty print" the JSON output into a hierarchical structure. This makes it easier to see the overall structure of the data. Now, use that capability to print the data into a more human-readable output.
8+
9+
> **Note**: The `requests` library has the ability to return the data from the query in JSON without any additional lines of code, however, in this lab we will not be using this ability in order to understand how the `json` library works within Python.
10+
11+
1. Normally, we'd need to odify the second line of `get-tenant-json.py` to import the JSON library, however, due to the construct of the ACI authentication, we've already imported it. You can verify that this line is present in the file:
12+
13+
```
14+
import json
15+
```
16+
17+
2. Comment out the `print(tenants.text)` line by putting a pound or hash sign at the front of the line:
18+
19+
```
20+
# print(tenants.text)
21+
```
22+
You are commenting out this line because you are going to use the `json` library to pretty print your output.
23+
24+
3. Update `get-tenant-json.py` by adding the following line below `#print(tenants.text)`:
25+
26+
```
27+
json_response = json.loads(tenants.text)
28+
```
29+
30+
The `json.loads()` method loads the `tenants.text` into a JSON object. If it is successful, you can use square brackets to get a particular element from `json_response`.
31+
32+
4. Add the following line just below the previous line you added:
33+
34+
```
35+
print(json.dumps(json_response, sort_keys=True, indent=4))
36+
```
37+
38+
This line prints the elements `dumps()` method returns from the `json_response`. The arguments control how the output is displayed.
39+
40+
5. Save the `get-tenant-json.py` file. To download or review the current code, you can get it from GitHub [here](https://github.com/CiscoDevNet/coding-skills-sample-code/blob/master/coding202-parsing-json/get-tenant-json-2.py).
41+
42+
6. To run your newly created file, enter:
43+
44+
```
45+
python get-tenant-json.py
46+
```
47+
48+
7. The Python script gets and displays the following JSON data.
49+
50+
![](assets/images/json-output-pretty-new.png)
51+
52+
--------------------------------------------------------------------------------
53+
54+
The content and structure of this data is easier for you to read and understand.
55+
56+
### Understanding the returned JSON data
57+
58+
Because you are getting JSON data, the content you get back has a particular order or schema. Elements of the returned 'fv-Tenant' ACI Managed Object (MO) class parsed in JSON data have sub-objects and even sub-dictionaries and sub-lists. For this exercise, you are interested in the **name**, **dn**, and **health** of each of the tenants configured within the fabric. This approach returns the desired information, as well as additional bits returned as part of the query to the `fv-Tenant` class.
59+
60+
```
61+
{fvTenant}
62+
{attributes}
63+
annotation
64+
childAction
65+
descr
66+
dn
67+
extMngdBy
68+
lcOwn
69+
modTs
70+
monPolDn
71+
name
72+
nameAlias
73+
ownerKey
74+
ownerTag
75+
status
76+
uid
77+
{children}
78+
{healthInst}
79+
{attributes}
80+
...
81+
...
82+
...
83+
```
84+
85+
In the example shown, `fvTenant` is dictionary of multiple tenant objects - each with their `name`, `dn`, and other attributes.
86+
87+
Now that you have seen this structure, go to the next step and learn how to get only the data you care about.
88+
89+
**Next step:**
90+
91+
Proceed to Step 3: Iterate through a JSON dictionary in Python.

labs/parsing-json/3.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## Step 3: Iterate through a JSON dictionary in Python
2+
3+
### Loop through a Python dictionary
4+
5+
In the previous step, you decoded the JSON content into a `json_response` variable. This conversion allows a developer to easily access the information contained within using loops to iterate through the data. Using this method, we will loop through the tenants, print their name, DN, and their health score.
6+
7+
To loop through the tenants within a dictionary, first get the tenants using the `json` library. Then, use a Python `for` loop to go through each element of the list. A second loop won't be required, as the list contained within the `children` element returned only contains a single value, `healthInst`.
8+
9+
Keep in mind that you may need to reference the pretty-print JSON that we created in the previous section, focusing on a single tenant within the output, in order to iterate through the object correctly. You can also run the script from the previous section in "interactive" mode using `python -i`, which will allow you to interact with the stored variables to determine what the nested types are and how to access different elements to get to the desired key-value pairs in the JSON.
10+
11+
> **Note**: this looks like a lot of manipulation and parsing, but it will provide a very good testbed to understand how to iterate through JSON
12+
13+
1. Preserving the indentation, comment out the previous print statement (wherein we printed the "pretty" JSON) and add the following lines:
14+
15+
```python
16+
json_tenants = json_response['imdata']
17+
for tenant in json_tenants:
18+
tenant_name = tenant['fvTenant']['attributes']['name']
19+
tenant_dn = tenant['fvTenant']['attributes']['dn']
20+
tenant_health = tenant['fvTenant']['children'][0]['healthInst']['attributes']['cur']
21+
output = "Tenant: " + tenant_name + "\t Health Score: " + tenant_health + "\n DN: " + tenant_dn
22+
print(output.expandtabs(40))
23+
```
24+
25+
This snippet:
26+
27+
- Starts the initial entry point inside of the `imdata` dictionary
28+
- Creates a `for` loop to iterate through the list of `fvTenant` objects sequentially
29+
- Gathers the `name` and `dn` values within the `attributes` dictionary contained within the `fvTenant` dictionary
30+
- Gathers the current health score by pulling the value from the `children` dictionary. This is pulled from the list of child objects (there's only a single item, which is why this can be safely set to `0`), and then moving through the `healthInst` dictionary, followed by the `attributes` dictionary and pulling the value from `cur`
31+
- Prints the resulting data with nice formatting, using the `expandtabs` method. If the resulting printed data is not aligned, increase this value to 50 or so (results will vary due to length of configured tenant names on the fabric at any one time)
32+
33+
2. Save the `get-tenant-json.py` file. To download or review the current code, you can get it from GitHub [here](https://github.com/CiscoDevNet/coding-skills-sample-code/blob/master/coding202-parsing-json/get-tenant-json-3.py)
34+
35+
3. Enter the following command to run your file.
36+
37+
```
38+
python get-tenant-json.py
39+
```
40+
41+
4. When you run the Python script, at the end of the output, you should get the JSON data values returned for each tenant in a format that looks similar to this:
42+
43+
![](assets/images/json-output-parse-001-new.png)
44+
45+
--------------------------------------------------------------------------------
46+
47+
This is a very direct way to use Python to get data from a REST service using JSON and parse out only the information you care about. Depending on the capabilities of the REST API, you can also sometimes pass in query parameters or other information to reduce the scope of returned data or search for a single or group of specific items.
48+
49+
For a summary of resources, continue on to the last step.
50+
51+
**Next step:**
52+
53+
Proceed to Step 4: Find additional resources.

labs/parsing-json/4.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Step 4: Find additional resources
2+
3+
For more information on the topics covered, visit the following resources.
4+
5+
### JavaScript Object Notation (JSON)
6+
7+
For more information on JavaScript Object Notation (JSON), visit [https://www.json.org/](https://www.json.org/ "JSON.org").
8+
9+
Also consider visiting the JavaScript Object Notation (JSON) tutorials at [https://www.w3schools.com/js/js_json_intro.asp](https://www.w3schools.com/js/js_json_intro.asp "W3Schools JSON")
10+
11+
### Python
12+
13+
Python is a powerful tool. To learn more, visit [https://docs.python.org/3.4/tutorial/](https://docs.python.org/3.4/tutorial/ "Python Tutorial").
14+
15+
**Python JSON Libraries**<br>
16+
The documentation for the JSON Python library can be found at [https://docs.python.org/3.4/library/json.html](https://docs.python.org/3.4/library/json.html "Python JSON").
17+
18+
### Application Centric Infrastructure
19+
20+
**Product Information**<br/>
21+
[https://www.cisco.com/c/en/us/solutions/data-center-virtualization/application-centric-infrastructure/index.html](https://www.cisco.com/c/en/us/solutions/data-center-virtualization/application-centric-infrastructure/index.html "Application Centric Infrastructure (ACI) Product Site")
22+
23+
**Developer Resources**<br/>
24+
[https://developer.cisco.com/site/aci/](https://developer.cisco.com/site/aci/ "Application Centric Infrastructure Developer Site")
25+
26+
**Application Centric Infrastructure (ACI) API Documentation**<br/>
27+
[https://developer.cisco.com/docs/aci/](https://developer.cisco.com/docs/aci/ "ACI API Documentation")
Loading
Loading
Loading
Loading
Loading
Loading

labs/parsing-json/byod.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div>
2+
For this lab we will be using Python 3.4+. To verify your version run the following command in a terminal window:
3+
<code>
4+
python --version
5+
</code>
6+
</div>

labs/parsing-json/get-ap-json.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from urllib.request import Request, urlopen
2+
import json
3+
4+
# Let's construct a request with headers
5+
req = Request('https://devnetapi.cisco.com/sandbox/mse/api/config/v1/maps/info/DevNetCampus/DevNetBuilding/DevNetZone')
6+
req.add_header('Authorization', 'Basic bGVhcm5pbmc6bGVhcm5pbmc=')
7+
req.add_header('Accept', 'application/json')
8+
9+
# Actually open the request
10+
response = urlopen(req)
11+
12+
# Read the response into a JSON String
13+
responseString = response.read().decode("utf-8")
14+
15+
#res = json.dump(obj, out, sort_keys=True, indent=4, separators=(',', ': '))
16+
# Print out the response string
17+
#print(responseString)
18+
19+
# Load the JSON string into a JSON object
20+
json_object = json.loads(response_string)
21+
#print(json_object)
22+
print(json.dumps(json_object, sort_keys=True, indent=4))
23+
24+
# Only output what we are interested in - the AP name and IP address
25+
#access_points = json_object['accessPoints']
26+
#for ap in access_points:
27+
# print('Access Point: ' + ap['name'] + '\t mac: ' + ap['radioMacAddress'])
28+
29+
# Let's close the response we opened
30+
response.close()

0 commit comments

Comments
 (0)