Skip to content

Instantly share code, notes, and snippets.

@robheittman
Created December 2, 2014 07:05
Show Gist options
  • Save robheittman/675860dfe122e9a089bc to your computer and use it in GitHub Desktop.
Save robheittman/675860dfe122e9a089bc to your computer and use it in GitHub Desktop.
Fixed Python zone migration script
import dns.zone
from dns.zone import NoSOA
from dns.exception import DNSException
from dns.rdataclass import *
from dns.rdatatype import *
from dns import rdatatype
import httplib2
import json
import sys
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
# Update SERVICE_ACCOUNT_EMAIL with the email address of the service account for
# the client id created in the developer console.
SERVICE_ACCOUNT_EMAIL = 'set-me@developer.gserviceaccount.com'
# Update SERVICE_ACCOUNT_PKCS12_FILE_PATH with the file path to the private key
# file downloaded from the developer console.
SERVICE_ACCOUNT_PKCS12_FILE_PATH = 'set-me.p12'
# Scopes for access to data.
SCOPES = ['https://www.googleapis.com/auth/ndev.clouddns.readwrite']
# See https://developers.google.com/dns/what-is-cloud-dns#supported_record_types
SUPPORTED_RECORD_TYPES = [A, AAAA, CNAME, MX, PTR, SPF, SRV, TXT, DS]
def parseZone():
jsonOutput = {}
additions = []
zone_file = '%s.zone' % domain
try:
zone = dns.zone.from_file(zone_file, domain)
for name, node in zone.nodes.items():
rdatasets = node.rdatasets
for rdataset in rdatasets:
api_name = qualifyName(name)
if rdataset.rdtype in SUPPORTED_RECORD_TYPES:
addition = {
'name' : api_name,
'ttl' : str(rdataset.ttl),
'type' : rdatatype.to_text(rdataset.rdtype),
'kind' : 'dns#resourceRecordSet'
}
elif rdataset.rdtype in [SOA,NS]:
# Skip the SOA and NS records in this example. The
# SOA record is generated as part of the managed zone
# the NS records here aren't applicable because this
# zone is using Google Cloud DNS rather than providing
# its own name servers
# In your situation, you might want to keep the NS
# records, if so add them to the SUPPORTED_RECORD_TYPES
continue
# Array of records for this name/type combination
rrdatas = []
for rdata in rdataset:
if rdataset.rdtype == MX:
rrdatas.append('%s %s' % (rdata.preference, qualifyName(rdata.exchange)))
if rdataset.rdtype == CNAME:
rrdatas.append(qualifyName(rdata.target))
if rdataset.rdtype == A:
rrdatas.append(rdata.address)
if rdataset.rdtype == TXT:
rrdatas.append(rdata.strings[0])
if rdataset.rdtype == SRV:
rrdatas.append(str(rdata.priority) + " " + str(rdata.weight) + " " + str(rdata.port) + " " + qualifyName(rdata.target))
addition.update({'rrdatas' : rrdatas})
additions.append(addition)
jsonOutput.update({'additions' : additions })
return jsonOutput
except DNSException, e:
if e.__class__ is NoSOA:
print ('Check that your SOA line starts with a qualified domain and is in the form of: \n')
print (' example.com. IN SOA ns1.example.com. hostmaster.example.com. (')
print e.__class__, e
def qualifyName(dnsName):
dnsName = str(dnsName)
if dnsName.endswith("."):
return dnsName
if domain not in dnsName and dnsName != '@':
return dnsName + '.' + domain + '.'
else:
# Catches the @ symbol case too.
return domain + '.'
def authenticate():
"""Build and return a DNS service object authorized with the
service accounts that act on behalf of the given user.
Returns:
DNS service object.
"""
f = open(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'rb')
key = f.read()
f.close()
# Setting the sub field with USER_EMAIL allows you to make API calls using the
# special keyword 'me' in place of a user id for that user.
credentials = SignedJwtAssertionCredentials(
SERVICE_ACCOUNT_EMAIL,
key,
scope=SCOPES)
http = httplib2.Http()
http = credentials.authorize(http)
# Create and return the DNS service object
return build('dns', 'v1beta1', http=http)
def createZone(service):
body = {
'name' : domain.replace('.', '-'),
'dnsName' : domain + '.',
'description' : 'A generated zone for ' + domain
}
args = { 'project' : project, 'body' : body }
try:
response = service.managedZones().create(**args).execute()
return response['name']
except:
if 'already exists' in str(sys.exc_info()):
print('Zone already exists, moving on.')
return domain.replace('.','-')
else:
print str(sys.exc_info())
exit()
def createRecords(service, zone_name, records):
args = {
'project' : project,
'managedZone' : zone_name,
'body' : records
}
try:
print json.dumps(args,
sort_keys=True,
indent=4,
separators=(',', ': '))
response = service.changes().create(**args).execute()
print 'Record set created, result object: '
print json.dumps(response,
sort_keys=True,
indent=4,
separators=(',', ': '))
except:
print str(sys.exc_info())
exit()
if __name__ == '__main__':
if len(sys.argv) > 2:
project = sys.argv[1]
domain = sys.argv[2]
service = authenticate()
records = parseZone()
zone_name = createZone(service)
createRecords(service, zone_name, records)
else:
print ('Missing arguments. Provide both the Developer Console project ID'
'and domain name. ')
print ('Usage: migrateZone.py console-project-name example.com')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment