Skip to main content Skip to search
Carl Levine
Posted by
Carl Levine on
November 21, 2016

Setting Up + Working With The NS1 Python SDK

If you’re looking for a powerful way to integrate the intelligence and flexibility of the NS1 platform into your Python application or stack, this technical document will help you get off the ground. 

Before you begin, you’ll need to ensure that you’re running Python 2.7 or later in your environment, along with PIP. 

The base NS1 Python module includes a Python SDK for accessing the NS1 DNS platform and includes both a simple NS1 REST API wrapper, as well as a higher level interface for managing zones, records, data feeds, and more.

Installation

From the command line, execute the following command:


$ pip install nsone

That’s it, it’s installed.

Setting Up For Initial Use

Log into your NS1 account via the NS1 portal; on the Settings + Users page, create an API key.

API Key Configuration Screen - V2

When creating an API key, remember that you are able to set various permission levels to ensure that the application is only able to modify certain parts of your account. For more information on this, please reference the KB article around API keys.

Once your API key has been created, you are now able to authenticate using your application.

Importing The Base Package + Configuring To Use Your API Key

This file can be viewed and forked from the Github repository, here. You are able to specify multiple authentication schemes if your application requires it, but by default, the package is set up for one key.


from nsone import NSONE, Config

# you can either build your own config, or let NSONE build a default one for

# you. the latter is easier and works like this:

# NSONE will use config in ~/.nsone by default

nsone = NSONE()

# to specify an apikey here instead, use:

nsone = NSONE(apiKey=’YOURAPIKEYGOESHERE’)

# to load an alternate configuration file:

nsone = NSONE(configFile='/etc/nsone/api.json')

# to load a specific keyID inside of your config file (see config format

# in docs), use this. this only makes sense for config file loads, not

# apiKey loads:

nsone = NSONE(keyID='all-access')

# if you have special needs, build your own Config object and pass it to

# NSONE:

config = Config()

config.createFromAPIKey('qACMD09OJXBxT7XOwv9v')

config['verbosity'] = 5

config['transport'] = 'twisted'

nsone = NSONE(config=config)

#  you can get the current config object NSONE is using via

config = nsone.config

# change config variables

config['verbosity'] = 5

# write out new config files

config.write('/tmp/newconfig.json')

# the config file format supports different apiKeys (see docs) using keyID

# get the current keyID

print(config.getCurrentKeyID())

# use a different keyID

config.useKeyID('read-access')

Basic Zone Management

Every great DNS adventure wouldn't be complete without the ability to add, modify and remove zones from your account. These basic examples should give you the head start you'll need to initiate zone changes wherever necessary in your application. The following example can be viewed and forked from the Github repository, here.


from nsone import NSONE

# NSONE will use config in ~/.nsone by default

nsone = NSONE()

# to specify an apikey here instead, use:

# nsone = NSONE(apiKey='YOURAPIKEYGOESHERE')

# to load an alternate configuration file:

# nsone = NSONE(configFile='/etc/nsone/api.json’)

######################

# LOAD / CREATE ZONE #

######################

# to load an existing zone, get a Zone object back

test_zone = nsone.loadZone('test.com’)

# or create a new zone, get a Zone object back

# you can specify options like retry, refresh, expiry, nx_ttl, etc

zone = nsone.createZone('example.com', nx_ttl=3600)

print(zone)

# once you have a Zone, you can access all the zone information via the

# data property

print(zone.data['dns_servers’])

###############

# ADD RECORDS #

###############

# in general, use add_XXXX to add a new record to a zone, where XXXX represents

# the record type. all of these take optional named parameters like

# ttl, use_csubnet, feed, networks, meta, regions, filters

# all of these return Record objects

# add an A record with a single static answer

rec = zone.add_A('orchid', '2.2.2.2')

print(rec)<p></p><pre>

<pre># add an A record with two static answers

zone.add_A('honey', ['1.2.3.4', ‘5.6.7.8’])

# add a cname

zone.add_CNAME('pot', 'honey.example.com’)

# add an MX with two answers, priority 5 and 10

zone.add_MX('example.com', [[5, "mail1.example.com"],

                            [10, "mail2.example.com”]])

# add a AAAA, specify ttl of 300 seconds

zone.add_AAAA('honey6', '2607:f8b0:4006:806::1010', ttl=300)

# add an A record using full answer format to specify 2 answers with meta data.

# ensure edns-client-subnet is in use, and add two filters: geotarget_country,

# and select_first_n, which has a filter config option N set to 1

zone.add_A('bumble',

           [{'answer': ['1.1.1.1'],

             'meta': {

                 'up': False,

                 'country': ['US']

                 }

             },

            {'answer': ['9.9.9.9'],

             'meta': {

                 'up': True,

                 'country': ['FR']

                 }

             }],

           use_csubnet=True,

           filters=[{'geotarget_country': {}},

                    {'select_first_n': {'N': 1}}])

# zone usage

print(zone.qps())

###########################

# LOAD and UPDATE RECORDS #

###########################

# if you don't have a Record object yet, you can load it in one of two ways:

# 1) directly from the top level NSONE object by specifying name and type

rec = nsone.loadRecord('honey.example.com', 'A')

print(rec)

# 2) if you have a Zone object already, you can load it from that

rec = zone.loadRecord('honey', 'A')

print(rec)

# you can access all the record information via the data property

print(rec.data['answers’])

# add answer(s) to existing answer list

rec.addAnswers('4.4.4.4')

rec.addAnswers(['3.4.5.6', '4.5.6.8'])

print(rec.data['answers'])<p></p>

# update the full answer list

rec.update(answers=['6.6.6.6', '7.7.7.7'])

print(rec.data['answers’])

# set filters, ttl

rec.update(filters=[{'geotarget_country': {}},

                    {'select_first_n': {'N': 1}}],

           ttl=10)

# update record level (as opposed to zone or answer level) meta data

rec.update(meta={'up': False})

# update answer level meta data directly. note this is better done through

# a data feed (see examples/data.py), which allows changing the meta data

# values individually, without having to set the answer block

rec = zone.loadRecord('bumble', 'A')

print(rec.data['answers'])

rec.update(answers=[{'answer': ['1.1.1.1'],

                     'meta': {

                         'up': True,

                         'country': ['US']

                         }

                     },

                    {'answer': ['9.9.9.9'],

                     'meta': {

                         'up': False,

                         'country': ['FR']

                         }

                     }])

print(rec.data['answers’])

# record usage

print(rec.qps())

##########

# DELETE #

##########

# delete a single record

rec.delete()

# delete a whole zone, including all records, data feeds, etc. this is

# immediate and irreversible, so be careful!

zone.delete()

Leveling Up: Connecting Data Feeds

Data feeds are the magic that make the NS1 platform so powerful. Connecting to and leveraging data feeds allow you to steer traffic based on the data and metrics that are most important to you, and more importantly your users and customers. The following example can be viewed and forked from the Github repository, here.


from nsone import NSONE

# NSONE will use config in ~/.nsone by default

nsone = NSONE()

# to specify an apikey here instead, use:

# from nsone import Config

# config = Config()

# config.createFromAPIKey('qACMD09OJXBxT7XOuRs8')

# nsone = NSONE(config=config)

# create a zone to play in

zone = nsone.createZone('testzone.com')

# create an NSONE API data source

sourceAPI = nsone.datasource()

s = sourceAPI.create('my api source', 'nsone_v1')

sourceID = s['id']

# create feeds which will drive the meta data for each answer

# we'll use the id of these feeds when we connect the feeds to the

# answer meta below

feedAPI = nsone.datafeed()

feed1 = feedAPI.create(sourceID,

                       'feed to server1',

                       config={'label': 'server1'})

feed2 = feedAPI.create(sourceID,

                       'feed to server2',

                       config={'label': 'server2'})

# create a record to connect to this source, with two answers

# specify the up filter so we can send traffic to only those nodes

# which are known to be up. we'll start with just the second answer up.

# each 'up' meta value is a feed pointer, pointing to the feeds we

# created above

record = zone.add_A('record',

                    [{'answer': ['1.1.1.1'],

                      'meta': {

                          'up': {'feed': feed1['id']}

                          }

                      },

                     {'answer': ['9.9.9.9'],

                      'meta': {

                          'up': {'feed': feed2['id']}

                          }

                      }],

                    filters=[{'up': {}}])

# now publish an update via feed to the records. here we push to both

# feeds at once, but you can push to one or the other individually as well

sourceAPI.publish(sourceID, {

    'server1': {

        'up': True

    },

    'server2': {

        'up': False

    }})

# NSONE will instantly notify DNS servers at the edges, causing traffic to be

# sent to server1, and ceasing traffic to server2

# Disconnect feed1 from datasource.

feedAPI.delete(sourceID, feed1['id'])

The Possibilities Are ENDLESS!

With the NS1 Python SDK, all of the features that make the platform great are at your fingertips behind a familiar interface in Python. As with all of our SDKs, we keep things nice and open source - if you feel that there’s something we missed, feel free to submit an issue or pull request and we’ll work with you.