master
1#!/usr/bin/python
2
3import ConfigParser
4import requests
5import optparse
6import sys
7
8
9# This is a command-line script to import and export DNS records for a
10# single domain into or out of a hover account. Run it like so:
11# ./bulkhover.py -u USERNAME -p PASSWORD (import|export) DOMAIN DNS_FILE
12# or create a config file like this:
13#
14# [hover]
15# username=USERNAME
16# password=PASSWORD
17#
18# and run it like this:
19# ./bulkhover.py -c PATH_TO_CONF (import|export) DOMAIN DNS_FILE
20#
21# The DNS file should have one record per line, in the format:
22# {name} {type} {content}
23#
24# For example:
25#
26# www A 127.0.0.1
27# @ MX 10 example.com
28#
29# You can even copy the entire contents of one domain to another, like so:
30# ./bulkhover.py -c CONF export example.com - | ./bulkhover.py -c CONF -f import other.com -
31
32
33class HoverException(Exception):
34 pass
35
36
37class HoverAPI(object):
38 def __init__(self, username, password):
39 params = {"username": username, "password": password}
40 r = requests.post("https://www.hover.com/api/login", params=params)
41 if not r.ok or "hoverauth" not in r.cookies:
42 raise HoverException(r)
43 self.cookies = {"hoverauth": r.cookies["hoverauth"]}
44 def call(self, method, resource, data=None):
45 url = "https://www.hover.com/api/{0}".format(resource)
46 r = requests.request(method, url, data=data, cookies=self.cookies)
47 if not r.ok:
48 raise HoverException(r)
49 if r.content:
50 body = r.json()
51 if "succeeded" not in body or body["succeeded"] is not True:
52 raise HoverException(body)
53 return body
54
55
56def import_dns(username, password, domain, filename, flush=False):
57 try:
58 client = HoverAPI(username, password)
59 except HoverException as e:
60 raise HoverException("Authentication failed")
61 if flush:
62 records = client.call("get", "domains/{0}/dns".format(domain))["domains"][0]["entries"]
63 for record in records:
64 client.call("delete", "dns/{0}".format(record["id"]))
65 print "Deleted {name} {type} {content}".format(**record)
66
67 domain_id = client.call("get", "domains/{0}".format(domain))["domain"]["id"]
68
69 if filename == "-": filename = "/dev/stdin"
70 with open(filename, "r") as f:
71 for line in f:
72 parts = line.strip().split(" ", 2)
73 record = {"name": parts[0], "type": parts[1], "content": parts[2]}
74 client.call("post", "domains/{0}/dns".format(domain), record)
75 print "Created {name} {type} {content}".format(**record)
76
77def export_dns(username, password, domain, filename):
78 try:
79 client = HoverAPI(username, password)
80 except HoverException as e:
81 raise HoverException("Authentication failed")
82 records = client.call("get", "domains/{0}/dns".format(domain))["domains"][0]["entries"]
83
84 if filename == "-": filename = "/dev/stdout"
85 with open(filename, "w") as f:
86 for record in records:
87 f.write("{name} {type} {content}\n".format(**record))
88
89
90def main():
91 usage = "usage: %prog (-c CONF|-u USERNAME -p PASSWORD) (import|export) DOMAIN DNS_FILE"
92 description = "Import or export DNS records for a single domain in a hover account."
93 parser = optparse.OptionParser(usage=usage, description=description)
94 parser.add_option("-c", "--conf", default=None, help="The conf file that contains your username and password")
95 parser.add_option("-u", "--username", default=None, help="Your hover.com username")
96 parser.add_option("-p", "--password", default=None, help="Your hover.com password")
97 parser.add_option("-f", "--flush", default=False, action="store_true", help="Flush all DNS records associated with the domain before importing")
98 (options, args) = parser.parse_args()
99
100 if len(args) < 3:
101 parser.error("You must specify an operation, a domain, and a file")
102
103 operation, domain, filename = args
104
105 if operation not in ("import", "export"):
106 parser.error("Invalid operation: {0} - Valid operations are import and export".format(operation))
107
108 def get_conf(filename):
109 config = ConfigParser.ConfigParser()
110 config.read(filename)
111 items = dict(config.items("hover"))
112 return items["username"], items["password"]
113
114 if options.conf is None:
115 if not all((options.username, options.password)):
116 parser.error("You must specifiy either a conf file, or a username and password")
117 else:
118 username, password = options.username, options.password
119 else:
120 username, password = get_conf(options.conf)
121
122 if operation == "import":
123 import_dns(username, password, domain, filename, options.flush)
124 elif operation == "export":
125 export_dns(username, password, domain, filename)
126
127
128if __name__ == "__main__":
129 try:
130 main()
131 except HoverException as e:
132 print "Failed while importing DNS: {0}".format(e)
133 sys.exit(1)
134 sys.exit(0)