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.
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.