Internet Performance Delivered right to your inbox

Managing Your DNS Integration Is As Easy As A-P-I

After bouncing back and forth between a number of programming languages, several operating systems, more cloud services than The Weather Channel and DoS’ing my own network (not my finest moment, but let’s just say before severe load testing, make sure you are really on a completely isolated network), people ask me quite often how I keep things straight.

I quickly respond with “I don’t” but then point out that with a simple-to-use API, switching from one environment to a completely different one really isn’t that hard. As a way to illustrate this, I thought a Getting Started primer for some of the more popular languages would be a nice blog piece people may be able to use.

And it fits perfectly into Integration Month here at Dyn!

Here is a getting started guide to your favorite language (sorry BASIC users). In order to keep it straight forward and easy to understand, just the most basic code to add an A record has been shown on this page as an example. For more real world uses (including error checking and examples ranging from load balancer interaction to failover control), each section has a link to github repositories and other helpful locations to help you get your API on.

If you want to discuss more regarding APIs, go to our community site where myself (kgray) or other users would love to join in the discussion or help out when needed!

Pick Your Language!

Ruby
Python
C# (.Net)
Perl
Java

Ruby


The Ruby world is a fun one to play in since there are so many innovative things being done with one of the most popular current languages. Extensions done to many of the industry standard configuration management frameworks are written in Ruby such as the Dynect extensions written for Chef and Puppet. Ruby on Rails speeds up development and can take advantage of the Dyn APIs — much as we did ourselves for our integration with Heroku which makes a seamless portal to Dynect in its current form and an even tighter coupling to come.

Adding an A record to Dynect takes only a very few lines of code, including the session login (once logged in you don’t need to re-log in to add subsequent records):

#!/usr/bin/env ruby
require 'rubygems'
require 'net/https'
require 'uri'
require 'json'

# Set up our HTTP object with the required host and path
url = URI.parse('https://api2.dynect.net/REST/Session/')
headers = { "Content-Type" => 'application/json' }
http = Net::HTTP.new(url.host, url.port)
http.set_debug_output $stderr
http.use_ssl = true

# Login and get an authentication token that will be used for all subsequent requests.
session_data = { :customer_name => “customer_name”, :user_name => “user_name, :password =>”pwd” }

resp, data = http.post(url.path, session_data.to_json, headers)
result = JSON.parse(data)

auth_token = ''
if result['status'] == 'success'
	auth_token = result['data']['token']
else
	# the messages returned from a failed command are a list
	result['msgs'][0].each{|key, value| print key, " : ", value, "\n"}
end

# New headers to use from here on with the auth-token set
headers = { "Content-Type" => 'application/json', 'Auth-Token' => auth_token }

# Create the A record
url = URI.parse("https://api2.dynect.net/REST/ARecord/myzone.com/myfqdn.myzone.com /")
record_data = { :rdata => { :address => "192.168.1.2" }, :ttl => "30" }
resp, data = http.post(url.path, record_data.to_json, headers)

# Publish the changes
url = URI.parse("https://api2.dynect.net/REST/Zone/myzone.com/")
publish_data = { "publish" => "true" }
resp, data = http.put(url.path, publish_data.to_json, headers)

That is all it takes to login to Dynect to add an A record for a specific fqdn and zone and then publish the zone live! Now doing it this way is very straight-forward and is how I tend to do it, but for an even easier experience, a few of our users have built their own helper libraries with example use case. One of these is Michael Conigliaro who created a ruby gem which can not only be imported but can be run from the command line. If you take advantage of this gem you can streamline your own Ruby code down to this:

#!/usr/bin/env ruby

require 'dynect4r'

dyn_client = Dynect::Client.new(:customer_name => 'customer_name', :user_name => 'user_name', :password => 'password')
dyn_client.rest_call(:post, [Dynect::rtype_to_resource('A'), 'myzone.com', 'myfqdn.myzone.com], { :rdata => { :address => "192.168.1.2" }, :ttl => "30" })
dyn_client.rest_call(:put, [ 'Zone', 'myzone.com'], { 'publish' => 'true' })

Another great resource is the library Adam Jacob created which does a great job in handling sessions in Dynect. One of the fun projects I put together were the scripts for RightScale which would automate entering your newly minted RightScale server into your Dynect system (Managed DNS or Load Balancing or Global Server Load Balancing).

Python

Another widely used object oriented scripting language found on most Linux/Unix based systems is Python. A great deal of API development for Dynect has been done using the Python language and aside from my “home court” of Windows development, Python is my language of choice. One of the most interesting uses of the Dynect API written in Python is the Dynect integration with Amazon’s Route 53. Here, Python is used to fill in the missing ability of Route 53 to receive notifies when it is put to use as a less robust secondary DNS server to Dynect in those cases where policy dictates multiple DNS providers be used for over protection from problems. You can see that code posted for free on our Github repo. Getting started with Python is as simple as the following:

#!/usr/bin/python

import sys
import json
import httplib2

http = httplib2.Http()
http.force_exception_to_status_code = True

# First do the login and save the session token
token = ""
loginParams = {}
loginParams["customer_name"] = customername
loginParams["user_name"] = username
loginParams["password"] = password

response, content = http.request('https://api2.dynect.net/REST/Session/', 'POST', json.JSONEncoder().encode(loginParams), headers={'Content-type': 'application/json'})
result = json.loads(content)

if result["status"] == "success":
	token = result["data"]["token"]
	return True
else:
	return False

# Now that we have the session and token, add the A record for our zone and fqdn
recordParams = {}
recordParams["rdata"] = {}
recordParams["rdata"]["address"] = '192.168.1.2'
recordParams["ttl"] = 30

response, content = http.request('https://api2.dynect.net/REST/ARecord/myzone.com/myfqdn.myzone.com/', 'POST', json.JSONEncoder().encode(recordParams), headers={'Content-type': 'application/json', 'Auth-Token':  token})

result = json.loads(content)

# Finally, if adding the A record was a success, let's publish the zone
if result["status"] == "success":
	publishParams = {}
	publishParams["publish"] = 1

	response, content = http.request('https://api2.dynect.net/REST/Zone/myzone.com/', 'PUT', json.JSONEncoder().encode(publishParams), headers={'Content-type': 'application/json', 'Auth-Token':  token})
	result = json.loads(content)
	if result["status"] == "success":
		return True
	else:
		return False
else:

	return False

Much of this functionality has been wrapped into a library in Python meaning that instead of the above code, if you want to use the Python library for Dynect, you can boil the code down to the following:

#!/usr/bin/python

import sys
import json
import httplib2

import dynect
from dynect import  *

# connect to dynect and add an A record
dyn = Dynect('customer_name', 'username', 'password')
dyn.add_a_record('myzone.com', 'myfqdn.myzone.com', '192.168.1.2', 30)

To see more Python code accessing Dynect, check out Automating failover based on a Gomez alert and using Route 53 as a secondary.

C# (.Net)

Dynect is a cloud service offering a REST and SOAP interface — perfect for any platform including Windows and .Net. To make Visual Studio development simpler, the brilliant individuals over in engineering have exposed a WSDL to make using the SOAP interface a snap. As such, the easiest and most straightforward way of integrating into .Net is to import this WSDL and let Visual Studio create the web services based on it. I found a pretty good article on how to do that. Once you have added the WSDL to your project, you will have a WebReference to it and you can now use it in a manner such as this:

using System;
using System.Collections.Generic;
using System.Text;

namespace UpdateDynect
{
    class Program
    {

        static void Main(string[] args)
        {

            // create the object from the imported wsdl which should appear in your project as a web reference
            net.dynect.api2.Dynect dynectWsdl = null;

            // init the request
            net.dynect.api2.SessionLoginRequestType request = new CSharpDllTest.net.dynect.api2.SessionLoginRequestType();

            // setup the session parameters
            request.customer_name = "customerName";
            request.user_name = "userName";
            request.password = "password";
            request.fault_incompat = 1;
            request.fault_incompatSpecified = true;

            // do the login
            net.dynect.api2.SessionLoginResponseType response = dynectWsdl.SessionLogin(request);

            // get the data back including the token
            net.dynect.api2.SessionLoginData sessionData = response.data;

            // make sure we got back a valid session data object
            if (sessionData == null)
                return;

            // init the Create A Record Request object
            net.dynect.api2.CreateARecordRequestType aRequest = new CSharpDllTest.net.dynect.api2.CreateARecordRequestType();

            // setup the A record parameters
            aRequest.token = sessionData.token;
            aRequest.fault_incompat = 1;
            aRequest.fault_incompatSpecified = true;
            aRequest.zone = "myzone.com";
            aRequest.fqdn = "myfqdn.myzone.com";

            // init the Rdata for the A record
            aRequest.rdata = new CSharpDllTest.net.dynect.api2.RDataA();

            // now fill in the rdata
            aRequest.rdata.address = "192.168.1.2";
            aRequest.ttl = 30;

            // Create the record
            net.dynect.api2.CreateARecordResponseType aResponse = dynectWsdl.CreateARecord(aRequest);

            // get the response
            net.dynect.api2.ARecordData retVal = aResponse.data;

            // init the publish zone object
            net.dynect.api2.PublishZoneRequestType pubRequest = new CSharpDllTest.net.dynect.api2.PublishZoneRequestType();

            // setup the publish parameters
            pubRequest.token = sessionData.token;
            pubRequest.fault_incompat = 1;
            pubRequest.fault_incompatSpecified = true;
            pubRequest.zone = "myzone.com";

            // Now publish the zone
            net.dynect.api2.PublishZoneResponseType pubResponse = dynectWsdl.PublishZone(pubRequest);

            // get the response
            net.dynect.api2.ZoneData pubRetVal = pubResponse.data;
	}
    }
}

Now, this is relatively intuitive but there is some repetitive setup that can’t be avoided. However, it can be encapsulated. In this vein, you can use the DynectWrapper library ( get the full Visual Studio 2008 project and source ) to shorten this down to a very concise:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DynSoapWrapper.DynClasses;

namespace DynectTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create a dynect wrapper object with the proper credentials
            DynectWrapper wrapper = new DynectWrapper("customer_name", "user_name", "password");

            // Add an A record
            wrapper.CreateARecord("myzone.com", "myfqdn.myzone.com", "192.168.1.2", DynectWrapper.TTLSeconds.THIRTY);

            // publish the zone
            wrapper.PublishZone("myzone.com");

        }
    }
}

Several .Net based projects in the field are currently using this for simple automated management of their DNS.

Perl

Perl is a very heavily-used language with a good history of providing easy DNS access for all needs. Dynect access via Perl is no different. Here is a basic A record adding script for Perl using the JSON:XS library and the LWP:USerAgent

#!/usr/bin/perl

use JSON::XS;
use LWP::UserAgent;

# First lets setup the HTTP request for the session
my $session_uri = 'https://api2.dynect.net/REST/Session';
my $session_json = '{"customer_name": "customername", "user_name": "username", "password": "password"}';
my $session_req = HTTP::Request->new( 'POST', $session_uri );
$session_req->header( 'Content-Type' => 'application/json' );
$session_req->content( $session_json );

# Now create a LWP agent and make the request
my $session_lwp = LWP::UserAgent->new;
$session_res = $session_lwp->request( $session_req );

# decode the return json
$session_output = decode_json($session_res->content);

# if we did not succeed, print that and return
if($session_output->{'status'} ne 'success')
{
        print "Error on return from session connect";
        exit(0);
}

# Next lets setup the HTTP request for the Record Add
my $record_uri = 'https://api2.dynect.net/REST/ARecord/myzone.net/myfqdn.myzone.net/';
my $record_json = '{"rdata" : { "address" : "192.168.1.2" }, "ttl" : 30 }';
my $record_req = HTTP::Request->new( 'POST', $record_uri );
$record_req->header( 'Content-Type' => 'application/json', 'Auth-Token' => $session_output->{'data'}->{'token'} );
$record_req->content( $record_json );

# Now create a LWP agent and make the request
my $record_lwp = LWP::UserAgent->new;
$record_res = $record_lwp->request( $record_req );

# decode the return json
$record_output = decode_json($record_res->content);

# if we did not succeed, print that and return
if($record_output->{'status'} ne 'success')
{
        print "Error on return from adding A record";
        exit(0);
}

# Finally lets setup the HTTP request for the publish
my $publish_uri = 'https://api2.dynect.net/REST/Zone/mybrandnewzone.net/';
my $publish_json = '{"publish" : "true" }';
my $publish_req = HTTP::Request->new( 'PUT', $publish_uri );
$publish_req->header( 'Content-Type' => 'application/json', 'Auth-Token' => $session_output->{'data'}->{'token'} );
$publish_req->content( $publish_json );

# Now create a LWP agent and make the request
my $publish_lwp = LWP::UserAgent->new;
$publish_res = $publish_lwp->request( $publish_req );

# decode the return json
$publish_output = decode_json($publish_res->content);

# if we did not succeed, print that and return
if($record_output->{'status'} ne 'success')
{
        print "Error on return from publishing";
        exit(0);
}

print "Success!";
exit(0);

Once again, there are developers out there who have created libraries to make working with Dynect via Perl easier. One such library is the Net-Dynect-REST library by James Bromberger. Using this library to create a Dynect A record is stripped down to the following:

use Net::Dynect::REST::ARecord;
use Net::Dynect::REST::Zone;
use Net::Dynect::REST

$dynect = Net::Dynect::REST->new(user_name => 'username', customer_name => 'customername', password => 'password');

$zone = Net::Dynect::REST::Zone->new(connection => $dynect);

$record = Net::Dynect::REST::ARecord->new(connection => $dynect, zone => 'myzone.net', fqdn => 'myfqdn.myzone.net', rdata => Net::Dynect::REST::RData->new(data => {address => '192.168.1.2'}) );

$record->save;

$zone->publish;

If you are interested in the steps to build this library, check them out!

Java

A much employed cross-platform language which has innate ease and support for networking calls is our old friend Java. Strongly typed and object oriented, java is the basis for many client side applications which can employ the Dynect API. Using only the org.json package in order to make json handling easier (beautifully put together by Douglas Crockford and available on github ) the following example shows exactly how to publish an A record in Dynect:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import org.json.JSONObject;

class Test {

	public static void main(String[] args) {
		try	{

			URL url = null;
			url = new URL("https://api2.dynect.net/REST/Session");

			HttpURLConnection connection = (HttpURLConnection)url.openConnection();
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setAllowUserInteraction(false);
			connection.setRequestProperty("Content-Type", "application/json");

			JSONObject jso = new JSONObject();
				jso.put("user_name", "user_name");
				jso.put("customer_name", "customer_name");
				jso.put("password", "password");

			    OutputStreamWriter out = new OutputStreamWriter(
						connection.getOutputStream());
			    out.write(jso.toString());
			    out.close();

			if (connection.getResponseCode() != 200) {
				throw new IOException(connection.getResponseMessage());
			}

			// Buffer the result into a string
			BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			StringBuilder sb = new StringBuilder();
			String line;

			while ((line = rd.readLine()) != null) {
				sb.append(line);
			}
			rd.close();

			JSONObject retJson = null;
			retJson = new JSONObject(sb.toString());

				String token = retJson.getJSONObject("data").getString("token");

				url = new URL("https://api2.dynect.net/REST/ARecord/myzone.com/myzone.com");

			connection = (HttpURLConnection)url.openConnection();
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setAllowUserInteraction(false);
			connection.setRequestProperty("Content-Type", "application/json");
			connection.setRequestProperty("Auth-Token", token);

			jso = new JSONObject();
				jso.put("fqdn", "myzone.com");
				jso.put("zone", "myfqdn.myzone.com");
				jso.put("ttl", 30);
				JSONObject rdata = new JSONObject();
				rdata.put("address", args[2]);
				jso.put("rdata", rdata);

			    out = new OutputStreamWriter(
						connection.getOutputStream());
			    out.write(jso.toString());
			    out.close();

			if (connection.getResponseCode() != 200) {
				throw new IOException(connection.getResponseMessage());
			}

			// Buffer the result into a string
			rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			sb = new StringBuilder();
			line = "";

			while ((line = rd.readLine()) != null) {
				sb.append(line);
			}
			rd.close();

			url = new URL("https://api2.dynect.net/REST/Zone/myzone.com");

			connection = (HttpURLConnection)url.openConnection();
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setAllowUserInteraction(false);
			connection.setRequestProperty("Content-Type", "application/json");
			connection.setRequestProperty("Auth-Token", token);

			jso = new JSONObject();
				jso.put("publish", "true");

			    out = new OutputStreamWriter(
						connection.getOutputStream());
			    out.write(jso.toString());
			    out.close();

			if (connection.getResponseCode() != 200) {
				throw new IOException(connection.getResponseMessage());
			}

			// Buffer the result into a string
			rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			sb = new StringBuilder();
			line = "";

			while ((line = rd.readLine()) != null) {
				sb.append(line);
			}
			rd.close();

		}
		catch(Exception e) {
				e.printStackTrace();
		}

	}

}

As with many of the other languages, some repetitive setup has to be done and can be encapsulated away. A package that does just that is available on github and with using this package, adding an A record is simplified to the very basics of:

import dynect.DynectRecords;

class Test {
	public static void main(String[] args) {
		DynectRecords dyn = new DynectRecords("user_name", "customer-name", "password");
		dyn.AddARecord("mybrandnewzone.net", "mybrandnewzone.net", "22.33.44.5", 30);
		dyn.PublishZone("mybrandnewzone.net");
	}

}

Good luck and be sure to let us know if you have questions!

Kevin Gray is a technical integrator for Dyn Inc., an IaaS (Infrastructure-as-a-Service) company that features a full suite of DNS and email delivery services. Follow at Twitter: @tuftsmoose and @dyninc.


Share Now

Whois: Kevin Gray

Kevin Gray is a Sales Engineer for Dyn , the world leader in Internet Performance Solutions that delivers traffic management, message management, and performance assurance. Follow on Twitter: @tuftsmoose and @Dyn.