-
Notifications
You must be signed in to change notification settings - Fork 13
1. Introduction
The design goals of this open source library are as follows:
- Encapsulate Junos Space API into a Python class named
Space. It should be possible to access all Junos Space web services, collections, and resources hierarchically as members of this class. - Maintain a logical mapping between the way Junos Space API is structured and the containment hierarchy of the Python objects provided by this library. This allows end users to refer to Junos Space API documentation to understand the API and easily map to objects in this library.
- Map resources and collections in Junos Space API to Python objects and operate on them in a natural way.
- Map HTTP methods (GET, PUT, POST, DELETE) to methods on the Python objects representing resources and collections.
- Avoid writing of XML/JSON parsing and formatting code as much as possible for the users of this library.
- Organize code in such a way that most of the heavy lifting is done in a few core classes, allowing contributors to easily add/enhance support for web services, collections, and resources by supplying descriptions as YAML files.
Let's look at an example to understand what this means. The following snippet imports the rest module from the package jnpr.space and creates an instance of the class Space. As you can see, we need to supply the URL of the Space server, a valid userid, and its password.
from jnpr.space import rest
my_space = rest.Space(url='https://10.1.1.1', user='super', passwd='juniper123')The Space object encapsulates the URI /api/space and provides access to all web-services contained under this URI. Let's say you want to access the tag-management service and get the list of all tags. This can be done by the following line of code.
tags_list = my_space.tag_management.tags.get()This line shows that:
- The Space object contains an attribute named tag_management. This object provides access to the Tag Management service at /api/space/tag-management. (Notice that hyphens are replaced by underscores in the naming of attributes.)
- This object in turn contains an attribute named tags. This represents the collection at /api/space/tag-management/tags.
- You need to perform an HTTP GET on this collection object to get the tag resources. This can be done by invoking the get() method on the collection object.
The get() method returns a list of Python objects, each object representing a tag resource. You can access the attributes of the tag resource as if they were direct attributes of the tag Python object. For example, the following lines of code print out the name and type of each tag resource. Notice that there is no XML/JSON parsing that you need to do.
tags_list = my_space.tag_management.tags.get()
for t in tags_list:
print t.name, t.typeFor more details on how to access XML sub-elements and attributes from resources, please see the wiki page here.
In case you do not want to get all tags, but instead get tags based on a filter, that can easily be done by supplying a filter_ argument to the get() call. The filter_ argument can be a dictionary with each name-value pair indicating a filter condition (equality), or it can be a string formatted as a valid filter criteria as per Junos Space API filtering syntax. For example, let's say you want to get the public tag named MyTag - the following line of code will suffice:
tags_list = my_space.tag_management.tags.get(filter_={'name':'MyTag',
'type':'public'})Notice the use of a dictionary to express the filtering criteria. The above call can also be expressed as follows. In this example, we supply a string as the value of filter_ and this string is formatted as a valid filtering criteria obeying Junos Space API filtering syntax.
tags_list = my_space.tag_management.tags.get(filter_="(name eq 'MyTag') \
and (type eq 'public')")When performing gets on a collection of resources, you can also opt to get resources page-by-page, by supplying the start and limit values for paging inside a dictionary. These correspond to the start and limit conditions of the Junos Space paging criteria as described in the documentation. The following line of code gets the first 10 public tags from Junos Space:
tags_list = my_space.tag_management.tags.get(filter_="type eq 'public'",
paging={'start': 0,
'limit': 10})Now let's say you want to create a new tag. The following lines of code will do the trick. Essentially, you instantiate an empty Resource object and assign attribute values to it. You can use the factory.make_resource() method to instantiate a Resource. As you can see, you need to supply the type_name of the resource and the rest_end_point to this method. Then you invoke the post() method on the tags collection, supplying the new resource as an argument. This internally invokes HTTP POST on /api/space/tag-management/tags to create a new tag with the given name and type. The request body for the POST method is automatically created by formatting the object in XML format.
from jnpr.space import factory
new_tag = factory.make_resource(type_name='tag_management.tag',
rest_end_point=my_space)
new_tag.name = 'NewTag'
new_tag.type = 'private'
new_tag = my_space.tag_management.tags.post(new_tag)You can modify a resource easily by setting new values for its attributes and invoking the put() method on it. Below is an example that modifies the name and type of the new_tag object that we created in the previous example. The put() method invocation results in an HTTP PUT on /api/space/tag-management/tags/{id} - i.e. the href of the new_tag object. The request body for the PUT method is automatically created by formatting the object in XML format.
new_tag.name = 'NewTagName'
new_tag.type = 'public'
new_tag.put()Deleting a resource is quite easy - you just invoke the delete() method on the object representing the resource. For example, the following line will delete the new tag that was created in the previous example. Note that the Python object (new_tag) continues to exist even after a successful delete() call, however the corresponding tag resource will be deleted from Space.
new_tag.delete()In Junos Space API, each tag resource contains a collection named targets. Each element in this collection represents a target resource on which this tag has been applied. This library allows you to work with such collections naturally and perform Read (GET), Create (POST), and Delete (DELETE) operations on them quite easily.
For example, let's say you want to print the href and type of all resources on which a specific tag (referred to by the variable my_tag below) has been applied. You can do this as shown below:
targets_list = my_tag.targets.get()
for tgt in targets_list:
print tgt.href, tgt.typeLet's say you want to assign my_tag to a device whose URI is available in the variable device_href. You can do it as shown below. Essentially, you create a new tag_management.target resource and post it to the targets collection under my_tag
tgt = factory.make_resource(type_name='tag_management.target',
rest_end_point=my_space)
tgt.href = device_href
my_tag.targets.post(tgt)You can also assign a tag to multiple resources at the same time. For example, the following code snippet assigns my_tag to 2 devices (inside the list called dlist). As you can see, we supply a list of 2 tag_management.target resources to the post() method. Each tag_management.target object is initialized using the attributes argument (this is a special argument defined by this library).
my_tag.targets.post([
factory.make_resource(type_name='tag_management.target',
attributes={'href': dlist[0].href}),
factory.make_resource(type_name='tag_management.target',
attributes={'href': dlist[1].href})])You can remove a tag assignment by deleting the corresponding target resource. For example, the following code snippet removes my_tag from all targets to which it has been previously assigned.
targets_list = my_tag.targets.get()
for tgt in targets_list:
tgt.delete()Now let's look at a complete example that uses Tag Management and Device Management services of Junos Space API. In this example, we tag each device based on the snmp/location that is configured on it.
from jnpr.space import rest, factory
def main():
# Create a Space REST end point
my_space = rest.Space(url='http://localhost:8080',
user='super', passwd='juniper123')
# Get all devices
devices_list = my_space.device_management.devices.get()
for d in devices_list:
# Get the snmp/location configured on each device
c = d.configurations.expanded.post(xpaths=['configuration/snmp/location'])
if c.configuration.location:
# Tag the device with the configured location
tag_device(my_space, d, c.configuration.location)
def tag_device(spc, device, tag_name):
try:
# Check if a tag exists already with the given name
tag = spc.tag_management.tags.get(filter_={'name': tag_name})[0]
except:
# Create a new public tag with the given name
tag = factory.make_resource('tag_management.tag', spc)
tag.name, tag.type = tag_name, 'public'
tag = spc.tag_management.tags.post(tag)
"""
Create a new target for this tag, pointing to the given device.
In other words, assign this tag to this device
"""
target = factory.make_resource('tag_management.target', spc)
target.href = device.href
tag.targets.post(target)
if __name__ == "__main__":
main()