Showing posts with label curl. Show all posts
Showing posts with label curl. Show all posts

Wednesday, October 21, 2015

Using cURL to edit and delete RESTful API Objects in a Palo Alto Networks Firewall

XML is a very hard language to understand when you are first working with it. For example when you refer to an element like:

<ship>Titanic</ship>

you refer to everything including the start tag and the end tag. The information inside the tags is text.

You need to keep this in mind when trying to reference things.

Another confusing thing is xpath and attributes. Take this XML example:

<rules>
  <entry name="rule1">
   <from>
       <member>
            Trust
       </member>
   </from>
  </entry>
  <entry name="rule2">
   <from>
       <member>
            UnTrust
       </member>
   </from>
  </entry>

<rules>

If you want to reference something say 'rule2' then what you want is the attribute value. You would use entry[@name='rule2'] entry is the element, name is the attribute and 'rule2' is the attribute value

If you want to reference the text value within an element then you would use element[text()='value'].

For example if you want to reference 'Trust' you can use member[text()='Trust']

This leads to why it could get a little confusing when trying to edit and delete specific values using the Palo Alto Networks API. Let's look at the following rule.




To delete a source-user member named 'acme\bob' in a group of source users
, use the below xpath:

xpath=/config/devices/entry[@name='<domain>']/vsys/entry[@name='<vsysname>']/rulebase/security/rules/entry[@name='<rulename>']/source-user/member[text()='acme\bob']


$curl -k "https://192.168.1.1/api/?type=config&action=delete&xpath=/config/devices/entry\[@name='localhost.localdomain'\]/vsys/entry\[@name='vsys1'\]/rulebase/security/rules/entry\[@name='deny-rule1'\]/source-user/member\[text()='acme\bob'\]&key=<API-KEY>"

<response status="success" code="20"><msg>command succeeded</msg>


If you want to edit a member value, then you need to reference the original member value with member\[text()='<value>'\] and then use the element parameter for the modified member text value: element=<xml code>

for example using curl

$ curl -k "https://192.168.1.1/api/?type=config&action=edit&xpath=/config/devices/entry\[@name='localhost.localdomain'\]/vsys/entry\[@name='vsys1'\]/rulebase/security/rules/entry\[@name='deny-rule1'\]/source-user/member\[text()='acme\bob'\]&element=<member>acme\calvin</member>&key=<API-KEY>"

<response status="success" code="20"><msg>command succeeded</msg></response>

Below are all xpath expressions you can use when accessing the Palo Alto Networks api.

Examples:

/source-user/member[position()<4]
Selects the first three member elements that are children of the source-user element

$ curl -k "https://192.168.1.1/api/?type=config&action=get&xpath=/\[@name='localhost.localdomain'\]/vsys/entry\[@name='vsys1'\]/rulebase/security/rules/entry\[@name='rule1'\]/source-user/member\[position()<4\]&key=<API-KEY>"
<response status="success" code="19"><result total-count="3" count="3">
  <member admin="admin" time="2015/10/21 13:42:11">acme\amy</member>
  <member admin="admin" time="2015/10/21 13:42:11">acme\bob</member>
  <member admin="admin" time="2015/10/21 13:42:11">acme\calvin</member>

/source-user/member[2] Selects the second member element that is the child of the source-user element


$ curl -k "https://192.168.1.1/api/?type=config&action=get&xpath=/\[@name='localhost.localdomain'\]/vsys/entry\[@name='vsys1'\]/rulebase/security/rules/entry\[@name='rule1'\]/source-user/member\[2\]&key=<API-KEY>" <response status="success" code="19"><result total-count="1" count="1">
  <member admin="admin" time="2015/10/21 13:42:11">acme\bob</member>

Sunday, October 18, 2015

Using cURL to access the RESTful API of a Palo Alto Networks Firewall

There may be a situation where you would need to access the API of a Palo Alto Networks firewall. Some IT administrators may be more comfortable using cURL to access an API than a scripting language like PYTHON. Here's a few examples of how to perform some tasks using cURL.

The first thing you need to do is get an API key. How do you get an API Key? You query the firewall itself.

?type=keygen is the parameter to use.

curl -k "https://<firewall ip>/api/?type=keygen&user=<username>&password=<password>"


Make sure you wrap your url with Quotes. I tried this before and would get an error 400 response. 

You will get a response in xml. The value for key is the API key.

curl -k "https://192.168.1.1/api/?type=keygen&user=admin&password=admin"

<response status = 'success'>
<result>     
 <key>
   1234....
 </key>
 </result>
</response>


Then you can query the firewall using the API key instead of passing a username and password.

curl -k "https://<firewall ip>/api/?type=<api type>&<parameters>&key=<api-key>"

To get a list of api types and what commands to pass, you can use the REST API browser on the firewall. Just bring up a browser and authenticate against your firewall. After that you can add the path /api to the url.   https://<firewall ip>/api



Here you have the different api "types" you can query the firewall. Each of these types have some caveats as to the way you send your


For our first example, lets execute an operational command. From the cli the equivalent command would be "show system info".


If you drill down the Operational Commands to show system and info you will see the REST API URL at the bottom of the page. This is the url path you would copy to your curl command.










user@ubuntu-vm:~/API/curl$ curl -k "https://192.168.1.1/api/?

type=op&cmd=<show><system><info></info></system></show>&key=<API-KEY>"

<response status="success">
<result>
<system>
<hostname>NG-FW</hostname>
<ip-address>192.168.1.1</ip-address>
<netmask>255.255.255.0</netmask>
<default-gateway>192.168.1.254</default-gateway>
... TRUNCATED...

Notice that the command is in XML format. The firewall takes input and outputs in XML. 
This is important to note as you'll need to know the XPATH for most queries.

The following is to query the hostname with. You will need to add "action=get" as one of the parameters to read the hostname.


user@ubuntu-vm:~/API/curl$ curl -k "https://192.168.1.1/api/?type=config&action=get&xpath=/config/devices/entry\[@name='localhost.localdomain'\]/deviceconfig/system/hostname&key=<API-KEY>"
<response status="success" code="19"><result total-count="1" count="1">
  <hostname>NG-FW</hostname>

Notice that I had to put the literal '\[' for special characters like the square brackets. cURL would complain if I didn't have this.

curl: (3) [globbing] bad range specification in column 78


Now that we did a get. Let's try a set. When we do a set command, we need an extra parameter "element" that will contain the xml of the new content.

user@ubuntu-vm:~/API/curl$ curl -k "https://192.168.1.1/api/?type=config&action=set&xpath=/config/devices/entry\[@name='localhost.localdomain'\]/deviceconfig/system&element=<hostname>test</hostname>&key=<API-KEY>"
<response status="success" code="20"><msg>command succeeded</msg></response>

Doing a get command again shows us the hostname is changed.

user@ubuntu-vm:~/API/curl$curl -k "https://192.168.1.1/api/?type=config&action=get&xpath=/config/devices/entry\[@name='localhost.localdomain'\]/deviceconfig/system/hostname&key=<API-KEY>"
<response status="success" code="19"><result total-count="1" count="1">
  <hostname admin="admin" time="2015/10/14 11:08:11">test</hostname>

Next you need to commit the set command.

user@ubuntu-vm:~/API/curl$curl -k "https://192.168.1.1/api/?type=commit&cmd=<commit></commit>&key=<API-KEY>"
<response status="success" code="19"><result><msg><line>Commit job enqueued with jobid 699</line></msg><job>69

The response code will return a job id.

The last API type I'll cover are logs. Log retrieval is an asynchronous process so you need to query the particular log type first which will create a job id in the response and then query the FW again with the job id. Here’s an example using a threat log.

The first one is to generate the job id.
curl -k "https://<FW-IP>/api/?type=log&log-type=threat&key=<API-KEY>

The second will show you the results after the job task is finished.
curl -k "https://<FW-IP>/api/?type=log&action=get&jobid=<JOB-ID>&key=<API-KEY>

user@ubuntu-vm:~$ curl -k "https://192.168.1.1/api/?type=log&log-type=threat&key=<API-KEY>"
<response status="success" code="19"><result><msg><line>query job enqueued with jobid 3605</line></msg><job>3605</job></result></response>


user@ubuntu-vm:~$ curl -k "https://192.168.1.1/api/?type=log&action=get&jobid=3605&key=<API-KEY>"
<response status="success"><result>
  <job>
    <tenq>17:57:29</tenq>
    <tdeq>17:57:29</tdeq>
    <tlast>17:57:30</tlast>
    <status>FIN</status>
    <id>3605</id>
    <cached-logs>33</cached-logs>
  </job>
  <log>
   ... TRUNCATED ...
  </log>
  <meta>
    <devices>
      <entry name="localhost.localdomain">
        <hostname>localhost.localdomain</hostname>
        <vsys>
          <entry name="vsys1">
            <display-name>vsys1</display-name>
          </entry>
        </vsys>
      </entry>
    </devices>
  </meta>


Friday, October 10, 2014

How to use cURL to spin up an OpenStack VM

I know this is a little long but there are dependencies on how this works. Hopefully you can understand it. The last cURL command is the piece that puts it all together and spins up a VM. The last command will spin up a instance called "instance1", use a flavor and an cirros image and place it in the "VN2" network


LAPTOP:SCRIPTS$cat openstack-create.sh
TOKEN=""

#Do a POST to GET the AUTH TOKEN
TOKEN=$(curl -i \
  -H "Content-Type: application/json" \
  -d '
{ "auth": {
    "identity": {
      "methods": ["password"],
      "password": {
        "user": {
          "name": "admin",
          "domain": { "id": "default" },
          "password": "password",
          "tenantName": "admin"
        }
      }
    },
    "scope": {
      "project": {
        "name": "demo",
        "domain": { "id": "default" }
      }
    }
  }
}' \
  http://192.168.1.4:5000/v3/auth/tokens | grep X-Subject-Token: | sed -e "s/X-Subject-Token: //")

#STORE TOKEN
header='X-Auth-Token: '$TOKEN
#echo "$header"

#NOW USE the AUTH TOKEN TO CHECK TENANTS
curl -X GET http://192.168.1.4:5000/v2.0/tenants/ -H "$header" | python -m json.tool

#CHECK IMAGES FOR TENANT/PROJECT CALLED DEMO
# TENANT ID FOR DEMO = 6d0ebb466f01454e897e518289b3b785
echo "IMAGES"
curl -H "X-Auth-Project-Id: demo" -H "$header" -X GET http://192.168.1.4:8774/v2/6d0ebb466f01454e897e518289b3b785/images | python -m json.tool


# CHECK FLAVORS
echo "FLAVORS"

curl -H "X-Auth-Project-Id: demo" -H "$header" -X GET http://192.168.1.4:8774/v2/6d0ebb466f01454e897e518289b3b785/flavors | python -m json.tool

# CHECK SERVERS
echo "SERVERS"

curl -H "X-Auth-Project-Id: demo" -H "$header" -X GET http://192.168.1.4:8774/v2/6d0ebb466f01454e897e518289b3b785/servers | python -m json.tool

# CHECK NETWORK ID - THIS IS NEUTRON AND HAS IT'S OWN PORT NUMBER 9696
echo "NETWORKS"

curl -H "Content-Type: application/json" -H "$header" -X GET http://192.168.1.4:9696/v2.0/networks | python -m json.tool

#CREATE VM
echo "CREATE VM"

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Auth-Project-Id: demo" -H "$header" -X POST http://192.168.1.4:8774/v2/6d0ebb466f01454e897e518289b3b785/servers -d '{"server": {"name": "instance1", "imageRef": "d5f30886-e8ce-4e89-9709-c49a40717b5b", "flavorRef": "2", "max_count": 1, "min_count": 1,"key_name": "xyz", "networks": [{"network": "bd912f99-c345-4f3e-8026-88e1dde42255", "uuid": "bd912f99-c345-4f3e-8026-88e1dde42255"}]}}'


-----------------------


I don’t execute the script and print it out as it would not look too pretty.

The most annoying part of this is all the curly brackets I had to dig around for the formatting of "networks" part as this was the hardest part.

I found out the formatting was like this:

networks: [{"port": String, "fixed_ip": String, "uuid": String, "network": String}, {"port": String, "fixed_ip": String, "uuid": String, "network": String}, ...]

Here's the api that I found useful:



Below is how it would look like if you executed the commands directly on the compute node. Note how image-list has the id for "imageRef"

root@openstack:/# nova flavor-list
+----+-----------+-----------+------+-----------+------+-------+-------------+-----------+
| ID | Name      | Memory_MB | Disk | Ephemeral | Swap | VCPUs | RXTX_Factor | Is_Public |
+----+-----------+-----------+------+-----------+------+-------+-------------+-----------+
| 1  | m1.tiny   | 512       | 1    | 0         |      | 1     | 1.0         | True      |
| 2  | m1.small  | 2048      | 20   | 0         |      | 1     | 1.0         | True      |
| 3  | m1.medium | 4096      | 40   | 0         |      | 2     | 1.0         | True      |
| 4  | m1.large  | 8192      | 80   | 0         |      | 4     | 1.0         | True      |
| 5  | m1.xlarge | 16384     | 160  | 0         |      | 8     | 1.0         | True      |
+----+-----------+-----------+------+-----------+------+-------+-------------+-----------+
root@openstack:/# nova image-list
+--------------------------------------+--------------+--------+--------------------------------------+
| ID                                   | Name         | Status | Server                               |
+--------------------------------------+--------------+--------+--------------------------------------+
| d894be99-6c35-46be-b0bf-01149d724aec | c1           | ACTIVE |                                      |
| d5f30886-e8ce-4e89-9709-c49a40717b5b | cirros       | ACTIVE |                                      |
| 10a33e93-84da-43cb-862b-fbf0b0ea9d64 | ubuntu-cloud | ACTIVE |                                      |
+--------------------------------------+--------------+--------+--------------------------------------+
root@openstack:/# nova keypair-list
+------+-------------------------------------------------+
| Name | Fingerprint                                     |
+------+-------------------------------------------------+
| xyz  | 88:c3:9d:32:d0:33:c9:1d:c3:9a:ed:cc:51:0c:55:30 |
+------+-------------------------------------------------+
root@openstack:/# nova network-list
+--------------------------------------+-----------------------------+------+
| ID                                   | Label                       | Cidr |
+--------------------------------------+-----------------------------+------+
| 4c626c1c-26e2-45a3-8fa6-336fb297ffea | west-network                | None |
| 3d608137-9e84-452a-9b9b-dd055f21a095 | right-nw                    | None |
| cfcd7fd5-4dae-4b0d-bf77-41df699e7bfb | vn-blue                     | None |
| c72cad3d-8f41-48a9-8ebc-73f6f4d8be87 | VN1                         | None |
| 80d71671-0b00-422e-911f-cb599e5b462a | external-floating-ip        | None |
| a8d51e11-34a7-4e72-bd91-7e51d7fbdc6c | default-virtual-network     | None |
| c9a0a923-d583-4513-b2b6-c2c6d805ff18 | management-network-poc-demo | None |
| f7681c05-30e9-4951-b2fb-1796a0b7c41f | left-vn                     | None |
| 3a156ca5-54de-4654-b344-5e75a1a4369c | ip-fabric                   | None |
| bd912f99-c345-4f3e-8026-88e1dde42255 | VN2                         | None |
| a77a4bec-51d8-4a6f-8e93-01d8e2d21512 | management                  | None |
| d4b022f2-88ff-4d63-84cb-c79b76153799 | network1                    | None |
| 632269aa-74ac-4306-820d-3b27835f5951 | __link_local__              | None |
| f2bc533f-ba1d-412d-a788-875670883e9b | svc-vn-mgmt                 | None |
| 1072c78f-f464-4a6f-91a5-0caac6cf2f26 | east-network                | None |
| 77c40c19-07d1-4416-86d5-1a00b2545a88 | public-fip                  | None |
+--------------------------------------+-----------------------------+------+
root@openstack:/#



Sunday, September 28, 2014

Exploring the REST API on contrail.

I noticed on github that there was a tutorial on how to access the REST API on Juniper Contrail.

https://juniper.github.io/contrail-vnc/api-doc/html/tutorial_with_rest.html#

The easiest way to access this is by using cURL. Contrail uses tcp port 8082 for accessing it's REST API.

The url http:/contrail-ip/virtual-networks prints out a list of configured virtual networks on Contrail.

$ curl -X GET -H "Content-Type: application/json; charset=UTF-8" http://172.16.1.4:8082/virtual-networks | python -mjson.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1229  100  1229    0     0   2129      0 --:--:-- --:--:-- --:--:--  2129
{
    "virtual-networks": [
        {
            "fq_name": [
                "default-domain",
                "default-project",
                "__link_local__"
            ],
            "href": "http://172.16.1.4:8082/virtual-network/4092df7b-997a-4ee7-a5cc-46d5db1187d4",
            "uuid": "4092df7b-997a-4ee7-a5cc-46d5db1187d4"
        },
        {
            "fq_name": [
                "default-domain",
                "default-project",
                "default-virtual-network"
            ],
            "href": "http://172.16.1.4:8082/virtual-network/34579f9a-064e-4048-96a7-a30355c54e44",
            "uuid": "34579f9a-064e-4048-96a7-a30355c54e44"
        },
        {
            "fq_name": [
                "default-domain",
                "default-project",
                "ip-fabric"
            ],
            "href": "http://172.16.100.104:8082/virtual-network/db7d1afe-bcaa-456b-b33a-9a36f6d176fe",
            "uuid": "db7d1afe-bcaa-456b-b33a-9a36f6d176fe"
        },
        {
            "fq_name": [
                "default-domain",
                "demo",
                "Network1"
            ],
            "href": "http://172.16.1.4:8082/virtual-network/380c0f4e-34b6-4901-98b2-61a8ed7afd7d",
            "uuid": "380c0f4e-34b6-4901-98b2-61a8ed7afd7d"
        },
        {
            "fq_name": [
                "default-domain",
                "demo",
                "Network2"
            ],
            "href": "http://172.16.100.104:8082/virtual-network/ffe03354-aaa2-4305-b615-654e14111134",
            "uuid": "ffe03354-aaa2-4305-b615-654e14111134"
        },
        {
            "fq_name": [
                "default-domain",
                "demo",
                "Network3"
            ],
            "href": "http://172.16.1.4:8082/virtual-network/93317422-eea4-4b77-88cb-5aaac3bb58b6",
            "uuid": "93317422-eea4-4b77-88cb-5aaac3bb58b6"
        }
    ]
}

However cURL, at least for me, isn’t capable of abstracting the data structures programatically. It's more like screen scraping. So I dug around the internet and noticed that python has a curl module.

http://pycurl.sourceforge.net/doc/quickstart.html#

Now I can use python to execute cURL to pull data from a REST API. 

There are a few parts to this script.

The first part is a function to issue the cURL command.

The second part is to abstract the route-target from a virtual network created in the Contrail controller.

This could then be used later on with another script to create a template configuration and program the underlay gateway router with a VRF.

In this script I cheated a little by grabbing a specific virtual-network’s url address from the previous curl command. Then parsed the data to grab the information I was looking for.

—————————————

import pycurl
import StringIO
import json

#Function to issue cURL command
def get_url(WEB):
  buf = StringIO.StringIO()
  c = pycurl.Curl()
  c.setopt(c.URL, WEB)
  c.setopt(c.WRITEFUNCTION, buf.write)
  c.perform()
  body = buf.getvalue()
  network = json.loads(body)
  buf.close()
  return network

#URL of virtual network on contrail

SITE = 'http://172.16.1.4:8082/virtual-network/380c0f4e-34b6-4901-98b2-61a8ed7afd7d'
objects = get_url(SITE)
#pretty print the json results
print json.dumps(objects, sort_keys=True, indent=4)

#This part is to grab the path of the Virtual Network name and RT from contrail

print "Name: ", objects['virtual-network']['name'], " RT: ", objects['virtual-network']['route_target_list']['route_target'][0]


Script in action:
——————
$ python contrail.py 
{
    "virtual-network": {
        "fq_name": [
            "default-domain", 
            "demo", 
            "Network1"
        ], 
        "href": "http://172.16.1.4:8082/virtual-network/380c0f4e-34b6-4901-98b2-61a8ed7afd7d", 
        "id_perms": {
            "created": "2014-09-19T19:19:11.650288", 
            "description": null, 
            "enable": true, 
            "last_modified": "2014-09-27T05:22:30.453524", 
            "permissions": {
                "group": "cloud-admin-group", 
                "group_access": 7, 
                "other_access": 7, 
                "owner": "cloud-admin", 
                "owner_access": 7
            }, 
            "uuid": {
                "uuid_lslong": 11002964217786203517, 
                "uuid_mslong": 4038619794410719489
            }
        }, 
        "instance_ip_back_refs": [
            {
                "attr": null, 
                "href": "http://172.16.1.4:8082/instance-ip/17b19dc0-b177-4df7-955b-57b8a87caf28", 
                "to": [
                    "17b19dc0-b177-4df7-955b-57b8a87caf28"
                ], 
                "uuid": "17b19dc0-b177-4df7-955b-57b8a87caf28"
            }, 
            {
                "attr": null, 
                "href": "http://172.16.1.4:8082/instance-ip/d525ddce-3542-4986-b8d1-56f3831d8678", 
                "to": [
                    "d525ddce-3542-4986-b8d1-56f3831d8678"
                ], 
                "uuid": "d525ddce-3542-4986-b8d1-56f3831d8678"
            }
        ], 
        "is_shared": false, 
        "name": "Network1", 
        "network_ipam_refs": [
            {
                "attr": {
                    "ipam_subnets": [
                        {
                            "default_gateway": "100.1.1.254", 
                            "subnet": {
                                "gw": "100.1.1.254", 
                                "ip_prefix": "100.1.1.0", 
                                "ip_prefix_len": 24
                            }
                        }
                    ]
                }, 
                "href": "http://172.16.1.4:8082/network-ipam/1f24fa35-b7bf-4d0f-8185-746f58e234c9", 
                "to": [
                    "default-domain", 
                    "demo", 
                    "Network1-ipam"
                ], 
                "uuid": "1f24fa35-b7bf-4d0f-8185-746f58e234c9"
            }
        ], 
        "network_policy_refs": [
            {
                "attr": {
                    "sequence": {
                        "major": 0, 
                        "minor": 0
                    }, 
                    "timer": null
                }, 
                "href": "http://172.16.1.4:8082/network-policy/0a1c1776-a323-4c00-959a-aada50b91be8", 
                "to": [
                    "default-domain", 
                    "demo", 
                    "net1<->net2"
                ], 
                "uuid": "0a1c1776-a323-4c00-959a-aada50b91be8"
            }
        ], 
        "parent_href": "http://172.16.1.4:8082/project/3af66afe-3284-40cc-8b04-85c69af512c7", 
        "parent_type": "project", 
        "parent_uuid": "3af66afe-3284-40cc-8b04-85c69af512c7", 
        "route_target_list": {
            "route_target": [
                "target:64512:100"
            ]
        }, 
        "router_external": false, 
        "routing_instances": [
            {
                "href": "http://172.16.1.4:8082/routing-instance/06165761-3b55-417d-acf0-0ad27a9010d0", 
                "to": [
                    "default-domain", 
                    "demo", 
                    "Network1", 
                    "Network1"
                ], 
                "uuid": "06165761-3b55-417d-acf0-0ad27a9010d0"
            }
        ], 
        "uuid": "380c0f4e-34b6-4901-98b2-61a8ed7afd7d", 
        "virtual_machine_interface_back_refs": [
            {
                "attr": null, 
                "href": "http://172.16.1.4:8082/virtual-machine-interface/2aefccdc-bf34-4d3b-bd37-41f716274883", 
                "to": [
                    "e432ad9a-8f6e-4f7c-813b-18ada76bfd64", 
                    "2aefccdc-bf34-4d3b-bd37-41f716274883"
                ], 
                "uuid": "2aefccdc-bf34-4d3b-bd37-41f716274883"
            }, 
            {
                "attr": null, 
                "href": "http://172.16.1.4:8082/virtual-machine-interface/f20bb145-49cd-43bf-a6e5-9d9c50794244", 
                "to": [
                    "2e775d6f-8043-41df-9295-d1b8d8f705a2", 
                    "f20bb145-49cd-43bf-a6e5-9d9c50794244"
                ], 
                "uuid": "f20bb145-49cd-43bf-a6e5-9d9c50794244"
            }
        ], 
        "virtual_network_properties": {
            "extend_to_external_routers": null, 
            "forwarding_mode": "l2_l3", 
            "network_id": 4, 
            "vxlan_network_identifier": null
        }
    }
}

Name:  Network1  RT:  target:64512:100


As you can see I was able to pull the "name" of the virtual network and the route-target of the virtual network. Later I can then create a script template to build the VRF on the Router Gateway like a MX.  That's one step closer to network automation.

Thursday, July 10, 2014

OP script - look up a vendor mac oui directly from a juniper switch using slax and cURL

I found a couple of cool things in Juniper scripting.

There is the ability to retrieve data over an internet connection using curl. From Juniper - cURL is a tool that uses the libcurl library and permits data transfers using a number of protocols, including FTP, FTPS, HTTP, HTTPS, SCP, and SMTP.

BTW I'm using cURL on an EX switch that is running 12.3R3.4. I didn't see this on 11.4 code.

This got me thinking. Whenever I'm troubleshooting a switch, usually due to some errant device in the network, I have to look up the IEEE Mac OUI address. This entails copying each mac address in the mac table of the script. Then I can go to a website like www.macvendors.com and paste the MACOUI to retrieve the vendor of the mac. This could be a pain if you had to parse through hundreds of mac addresses.

Luckily, macvendors.com has an API. 

So what if we used the power of curl and scripting to do this for you. Instead of having to open up a separate web browser, have the switch fetch the info over the internet and do it for you.

See the difference below.

{master:0}[edit]
user@SWITCH# run show ethernet-switching table                  
Ethernet-switching table: 9 entries, 6 learned, 0 persistent entries
  VLAN             MAC address       Type         Age Interfaces
  v250              *                 Flood          - All-members
  v150              *                 Flood          - All-members
  v150              00:00:05:00:00:00 Learn          0 ge-0/0/47.0
  v150              00:21:59:c7:09:41 Learn          0 ge-0/0/47.0
  v150              00:24:dc:d3:1a:10 Learn          0 ge-0/0/45.0
  v100              *                 Flood          - All-members
  v100              00:00:03:00:00:00 Learn          0 ge-0/0/45.0
  v100              00:24:dc:d3:1a:10 Learn          0 ge-0/0/45.0
  v100              a8:d0:e5:5a:59:08 Learn          0 ge-0/0/45.0

Now as an OP script:

{master:0}[edit]
user@SWITCH# run op mac-resolve                   
Vlan           Mac address Interfaces       Age Vendor 
v150  00:00:05:00:00:00 ge-0/0/47.0 0 XEROX CORPORATION
v150  00:21:59:c7:09:41 ge-0/0/47.0 0 Juniper Networks
v150  00:24:dc:d3:1a:10 ge-0/0/45.0 0 Juniper Networks
v100  00:00:03:00:00:00 ge-0/0/45.0 0 XEROX CORPORATION
v100  00:24:dc:d3:1a:10 ge-0/0/45.0 0 Juniper Networks
v100  a8:d0:e5:5a:59:08 ge-0/0/45.0 0 Juniper Networks


Now I can imagine if you really wanted to do some kind of accounting, you could create a Web page of equipment and users in your network. You could create scripts to populate this database. For example, DHCP users would have both their user logins and mac addresses. Then you could build onto this script by extracting the user login and show the user who is connected to the interface above.





The source code
-----------------

version 1.1;
ns curl extension = "http://xml.libslax.org/curl";

ns junos= "http://xml.juniper.net/junos/*/junos";

ns xnm= "http://xml.juniper.net/xnm/1.1/xnm";

ns jcs= "http://xml.juniper.net/junos/commit-scripts/1.0";

import "../import/junos.xsl";


match / {
    <op-script-results> {
        var $mac-info =  <command> "show ethernet-switching table";
        var $mac-table = jcs:invoke($mac-info);
        <output> "Vlan\tMac address\t\tInterfaces\tAge\tVendor ";
        for-each($mac-table//mac-table-entry) {
          if (current()/mac-address != "*") {
        var $test = current()/mac-address;
        var $str = substring ($test,1,8);
          var $url = "http://api.macvendors.com/" _ $str;
        var $options := {
        <url> $url;
        <method> "get";
        }
        var $curl = curl:open();
        var $results = curl:perform($curl,$options);
        var $int = current()/mac-interfaces-list;
        var $int2 = translate ($int, "\t\n\r", "");
        <output> current()/mac-vlan _"\t" _ current()/mac-address _"\t" _ $int2 
_ "\t"_ current()/mac-age _ "\t"_ $results/raw-data;
        expr curl:close($curl);
            }
        }
    }

}