From c021a0d1a9bb57a5c8a5a56427ed6ed29aaea325 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Mon, 31 Mar 2014 21:31:27 -0500 Subject: [PATCH] Fix #77 - Initialized gcloud dns. Cleaned up dns. Addressed comments on dns. Added function to create changes. Changed project_id to project and addressed comments. Made some changes. Streamlined dns to reflect first draft of design and updated doc strings. Initialized more doc strings. Edited some doc strings. Changed project_name to project. Updated docstrings and fixed some errors. Fixed a bug involving changes. --- gcloud/connection.py | 84 ++++++++++++ gcloud/dns/__init__.py | 92 +++++++++++++ gcloud/dns/change.py | 21 +++ gcloud/dns/connection.py | 145 ++++++++++++++++++++ gcloud/dns/demo/__init__.py | 19 +++ gcloud/dns/demo/__main__.py | 5 + gcloud/dns/demo/demo.p12 | Bin 0 -> 1732 bytes gcloud/dns/demo/demo.py | 26 ++++ gcloud/dns/exceptions.py | 6 + gcloud/dns/record.py | 65 +++++++++ gcloud/dns/zone.py | 260 ++++++++++++++++++++++++++++++++++++ gcloud/exceptions.py | 18 +++ 12 files changed, 741 insertions(+) create mode 100644 gcloud/dns/__init__.py create mode 100644 gcloud/dns/change.py create mode 100644 gcloud/dns/connection.py create mode 100644 gcloud/dns/demo/__init__.py create mode 100644 gcloud/dns/demo/__main__.py create mode 100644 gcloud/dns/demo/demo.p12 create mode 100644 gcloud/dns/demo/demo.py create mode 100644 gcloud/dns/exceptions.py create mode 100644 gcloud/dns/record.py create mode 100644 gcloud/dns/zone.py create mode 100644 gcloud/exceptions.py diff --git a/gcloud/connection.py b/gcloud/connection.py index 35855e89b445..fb9773441f0f 100644 --- a/gcloud/connection.py +++ b/gcloud/connection.py @@ -1,4 +1,8 @@ import httplib2 +import json +import urllib + +from gcloud import exceptions class Connection(object): @@ -42,3 +46,83 @@ def http(self): self._http = self._credentials.authorize(self._http) return self._http + +class JsonConnection(Connection): + + API_BASE_URL = 'https://www.googleapis.com' + """The base of the API call URL.""" + + _EMPTY = object() + """A pointer to represent an empty value for default arguments.""" + + def __init__(self, project=None, *args, **kwargs): + + super(JsonConnection, self).__init__(*args, **kwargs) + + self.project = project + + def build_api_url(self, path, query_params=None, api_base_url=None, + api_version=None): + + url = self.API_URL_TEMPLATE.format( + api_base_url=(api_base_url or self.API_BASE_URL), + api_version=(api_version or self.API_VERSION), + path=path) + + query_params = query_params or {} + query_params.update({'project': self.project}) + url += '?' + urllib.urlencode(query_params) + + return url + + def make_request(self, method, url, data=None, content_type=None, + headers=None): + + headers = headers or {} + headers['Accept-Encoding'] = 'gzip' + + if data: + content_length = len(str(data)) + else: + content_length = 0 + + headers['Content-Length'] = content_length + + if content_type: + headers['Content-Type'] = content_type + + return self.http.request(uri=url, method=method, headers=headers, + body=data) + + def api_request(self, method, path=None, query_params=None, + data=None, content_type=None, + api_base_url=None, api_version=None, + expect_json=True): + + url = self.build_api_url(path=path, query_params=query_params, + api_base_url=api_base_url, + api_version=api_version) + + # Making the executive decision that any dictionary + # data will be sent properly as JSON. + if data and isinstance(data, dict): + data = json.dumps(data) + content_type = 'application/json' + + response, content = self.make_request( + method=method, url=url, data=data, content_type=content_type) + + # TODO: Add better error handling. + if response.status == 404: + raise exceptions.NotFoundError(response, content) + elif not 200 <= response.status < 300: + raise exceptions.ConnectionError(response, content) + + if content and expect_json: + # TODO: Better checking on this header for JSON. + content_type = response.get('content-type', '') + if not content_type.startswith('application/json'): + raise TypeError('Expected JSON, got %s' % content_type) + return json.loads(content) + + return content diff --git a/gcloud/dns/__init__.py b/gcloud/dns/__init__.py new file mode 100644 index 000000000000..83b11cfd4022 --- /dev/null +++ b/gcloud/dns/__init__.py @@ -0,0 +1,92 @@ +"""Shortcut methods for getting set up with Google Cloud DNS. + +You'll typically use these to get started with the API: + +>>> import gcloud.dns +>>> zone = gcloud.dns.get_zone('zone-name-here', + 'long-email@googleapis.com', + '/path/to/private.key') + +The main concepts with this API are: + +- :class:`gcloud.dns.connection.Connection` + which represents a connection between your machine + and the Cloud DNS API. + +- :class:`gcloud.dns.zone.Zone` + which represents a particular zone. +""" + + +__version__ = '0.1' + +# TODO: Allow specific scopes and authorization levels. +SCOPE = ('https://www.googleapis.com/auth/cloud-platform', + 'https://www.googleapis.com/auth/ndev.clouddns.readonly', + 'https://www.googleapis.com/auth/ndev.clouddns.readwrite') +"""The scope required for authenticating as a Cloud DNS consumer.""" + + +def get_connection(project, client_email, private_key_path): + """Shortcut method to establish a connection to Cloud DNS. + + Use this if you are going to access several zones + with the same set of credentials: + + >>> from gcloud import dns + >>> connection = dns.get_connection(project, email, key_path) + >>> zone1 = connection.get_zone('zone1') + >>> zone2 = connection.get_zone('zone2') + + :type project: string + :param project: The name of the project to connect to. + + :type client_email: string + :param client_email: The e-mail attached to the service account. + + :type private_key_path: string + :param private_key_path: The path to a private key file (this file was + given to you when you created the service + account). + + :rtype: :class:`gcloud.dns.connection.Connection` + :returns: A connection defined with the proper credentials. + """ + + from gcloud.credentials import Credentials + from gcloud.dns.connection import Connection + + credentials = Credentials.get_for_service_account( + client_email, private_key_path, scope=SCOPE) + return Connection(project=project, credentials=credentials) + + +def get_zone(zone, project, client_email, private_key_path): + """Shortcut method to establish a connection to a particular zone. + + You'll generally use this as the first call to working with the API: + + >>> from gcloud import dns + >>> zone = dns.get_zone(zone, project, email, key_path) + + :type zone: string + :param zone: The id of the zone you want to use. + This is akin to a disk name on a file system. + + :type project: string + :param project: The name of the project to connect to. + + :type client_email: string + :param client_email: The e-mail attached to the service account. + + :type private_key_path: string + :param private_key_path: The path to a private key file (this file was + given to you when you created the service + account). + + :rtype: :class:`gcloud.dns.zone.Zone` + :returns: A zone with a connection using the provided credentials. + """ + + connection = get_connection(project, client_email, private_key_path) + return connection.get_zone(zone) diff --git a/gcloud/dns/change.py b/gcloud/dns/change.py new file mode 100644 index 000000000000..243ce3b965a1 --- /dev/null +++ b/gcloud/dns/change.py @@ -0,0 +1,21 @@ +class Change(object): + """A class representing a Change on Cloud DNS. + + :type additions: list + :param name: A list of records slated to be added to a zone. + + :type deletions: list + :param data: A list of records slated to be deleted to a zone. + """ + + def __init__(self, additions=None, deletions=None): + self.additions = additions + self.deletions = deletions + + def to_dict(self): + """Format the change into a dict compatible with Cloud DNS. + + :rtype: dict + :returns: A Cloud DNS dict representation of a change. + """ + return {'additions': self.additions, 'deletions': self.deletions} diff --git a/gcloud/dns/connection.py b/gcloud/dns/connection.py new file mode 100644 index 000000000000..11bc608262f5 --- /dev/null +++ b/gcloud/dns/connection.py @@ -0,0 +1,145 @@ +from gcloud import connection +from gcloud.dns.record import Record +from gcloud.dns.zone import Zone + + +class Connection(connection.JsonConnection): + """A connection to Google Cloud DNS via the JSON REST API. + + See :class:`gcloud.connection.JsonConnection` for a full list of parameters. + :class:`Connection` differs only in needing a project name + (which you specify when creating a project in the Cloud Console). + """ + + API_VERSION = 'v1beta1' + """The version of the API, used in building the API call's URL.""" + + API_URL_TEMPLATE = ('{api_base_url}/dns/{api_version}/projects/{path}') + """A template used to craft the URL pointing toward a particular API call.""" + + _EMPTY = object() + """A pointer to represent an empty value for default arguments.""" + + def __init__(self, project=None, *args, **kwargs): + """ + :type project: string + :param project: The project name to connect to. + """ + + super(Connection, self).__init__(*args, **kwargs) + + self.project = project + + def new_zone(self, zone): + """Factory method for creating a new (unsaved) zone object. + + :type zone: string or :class:`gcloud.dns.zone.Zone` + :param zone: A name of a zone or an existing Zone object. + """ + + if isinstance(zone, Zone): + return zone + + # Support Python 2 and 3. + try: + string_type = basestring + except NameError: + string_type = str + + if isinstance(zone, string_type): + return Zone(connection=self, name=zone) + + def create_zone(self, zone, dns_name, description): + """Create a new zone. + + :type zone: string or :class:`gcloud.dns.zone.Zone` + :param zone: The zone name (or zone object) to create. + + :rtype: :class:`gcloud.dns.zone.Zone` + :returns: The newly created zone. + """ + + zone = self.new_zone(zone) + response = self.api_request(method='POST', path=zone.path, + data={'name': zone.name, 'dnsName': dns_name, + 'description': description}) + return Zone.from_dict(response, connection=self) + + def delete_zone(self, zone, force=False): + """Delete a zone. + + You can use this method to delete a zone by name, + or to delete a zone object:: + + >>> from gcloud import dns + >>> connection = dns.get_connection(project, email, key_path) + >>> connection.delete_zone('my-zone') + True + + You can also delete pass in the zone object:: + + >>> zone = connection.get_zone('other-zone') + >>> connection.delete_zone(zone) + True + + :type zone: string or :class:`gcloud.dns.zone.Zone` + :param zone: The zone name (or zone object) to create. + + :type force: bool + :param full: If True, deletes the zones's recordss then deletes it. + + :rtype: bool + :returns: True if the zone was deleted. + """ + + zone = self.new_zone(zone) + + if force: + rrsets = self.get_records(zone) + for rrset in rrsets['rrsets']: + record = Record.from_dict(rrset) + if record.type != 'NS' and record.type != 'SOA': + zone.remove_record(record) + zone.save() + + self.api_request(method='DELETE', path=zone.path + zone.name) + return True + + def get_zone(self, zone): + """Get a zone by name. + + :type zone: string + :param zone: The name of the zone to get. + + :rtype: :class:`gcloud.dns.zone.Zone` + :returns: The zone matching the name provided. + """ + + zone = self.new_zone(zone) + response = self.api_request(method='GET', path=zone.path) + return Zone.from_dict(response['managedZones'][0], connection=self) + + def get_records(self, zone): + """Get a list of resource records on a zone. + + :type zone: string or :class:`gcloud.dns.zone.Zone` + :param zone: The zone name (or zone object) to get records from. + """ + + zone = self.new_zone(zone) + return self.api_request(method='GET', path=zone.path + zone.name + + '/rrsets') + + def save_change(self, zone, change): + """Save a set of changes to a zone. + + :type zone: string or :class:`gcloud.dns.zone.Zone` + :param zone: The zone name (or zone object) to save to. + + :type change: dict + :param dict: A dict with the addition and deletion lists of records. + """ + + zone = self.new_zone(zone) + return self.api_request(method='POST', path=zone.path + zone.name + + '/changes', data=change) diff --git a/gcloud/dns/demo/__init__.py b/gcloud/dns/demo/__init__.py new file mode 100644 index 000000000000..6b050674353b --- /dev/null +++ b/gcloud/dns/demo/__init__.py @@ -0,0 +1,19 @@ +import os +from gcloud import dns + + +__all__ = ['get_connection', 'get_zone' 'CLIENT_EMAIL', 'PRIVATE_KEY_PATH', + 'PROJECT'] + + +CLIENT_EMAIL = '524635209885-rda26ks46309o10e0nc8rb7d33rn0hlm@developer.gserviceaccount.com' +PRIVATE_KEY_PATH = os.path.join(os.path.dirname(__file__), 'demo.p12') +PROJECT = 'gceremote' + + +def get_connection(): + return dns.get_connection(PROJECT, CLIENT_EMAIL, PRIVATE_KEY_PATH) + + +def get_zone(zone): + return dns.get_zone(zone, PROJECT, CLIENT_EMAIL, PRIVATE_KEY_PATH) diff --git a/gcloud/dns/demo/__main__.py b/gcloud/dns/demo/__main__.py new file mode 100644 index 000000000000..8074eae216b9 --- /dev/null +++ b/gcloud/dns/demo/__main__.py @@ -0,0 +1,5 @@ +from gcloud import demo +from gcloud import dns + + +demo.DemoRunner.from_module(dns).run() diff --git a/gcloud/dns/demo/demo.p12 b/gcloud/dns/demo/demo.p12 new file mode 100644 index 0000000000000000000000000000000000000000..0f5966647b38373e55d50a4d15b8a0ae2be6e6e0 GIT binary patch literal 1732 zcmY+Dc{tRGAIHCQ$|w!Skyc}jF;k94v*Rck$2y~5p#1nN94*C zn@Emdax2C(59x+EX&)}6y z8gr#)(HqXB$D5_C?k6;3kGtE*=m1D2{V-!=Xu+D_0;z~((egIK^#VJ3`d$2Hp@hcD zYRlB|ey!QnbOK%zn82DZR zcl~-u+gMp)I|0SpvKHfZ0zfqsqc&4QN>9fQV_)Cm5vx4h4*gqE^ww6X$;7(9s(7;# zrl4X&@0!))2mh+TY=&Yw94Wn2ykAbkDqCi-9lIh5@!G%~nn}&LyRDj>3y{g91 z_Yt0=4Jm1eA6{4x47Wueq3ND3^fJBtQJ%+e?+lJGou8k^;(~8b)Y?|w<+4~~kW^JK z`-)@^xH+(~xxFnawKvpP7Frb*U)Z{5LL(^S6TILk#E!B#5$D<~IA!joZ%f4f{o&Fm zeWgPvAP5Ks{DCmwA`k-j0`y%)5HbIh6!lS1c^7|%uev_T*oaIvHYAbBM5A3GNPm~4 z;H7Mc=B}j<1_8U{z&{D-Ux2{B0#az`@JMv?vPk`TQguahD5LT9`lYXc#InJ!`inE= z4Y%*J-bhbP9sat2wQvjfM+lB#o-0aL}ZyjcE$e#x^pL_lxb zBklDdW+4M1hqR$5<=JkR)>>+?&^TI-&gLuVOVSi%b*^Lj?i4>lC&5PRk9nzyP()<| zEFj|w&NlcB>cUTE=Z9mm`m4zCdpT2ZGgSWP`K0A%Sx+!-5A|*x-N&~|)1T>!cGB^q z&83Ye(rp&G-ziB|t&jNZgs+x2@CNzr%P^}wMFXbB*v!;Xx1xaP(Xp=d@mEnxOo#4J z_oN#}H))=q%El7eBj}n027jgK?zK72hp3~1+%ofDvc#9aOOWbWUnzR@n+6)OuzA9w z2`N$#OL!!7y2%HSv;9*F9SE9l?ECy?R`+97Ucm=Ps!rU0%$bYL0`JcfdxA%kzg*WL z#B|7W_+V7w`VR=rG676T1yAW>%SxEj)u!IxmI*mg%iYGs^vsx+tqV-y)nMhoZ2S+@*1|c-5@XWe!N2w?b;DY2Nzz{#4TQ#+%p{_o z+qfJ&uD`Z=2{!#`*wdzQVQ9K*cK8ZfHdo!p!t#uv$Iz|7NWx>=6W^4cp@^%m;gnad zzVJ{xba>9ZE-qYX9_fU}qJB18H8M?ZE$rqfAHIm+Mq|Er79wN(4PI(AKkGJ_??XHY z;w*nT=Cl+hFI{8T&9K2J$sixZC$Zl8%t_SpBE*yVOPz8t0ebDfs>pPg@AY8ftxr8b z)<5%eR`v6a${8*%3hSl~RV>VoQR(xF;})f`aHpsLy}dMEZpqrD{lwE^MqJZEQ+qTn z)kwv?PwCOU!~#4@gm(7%M}C1f@f=c=Ca0G3b+jt_T=@M$BTA1%J7mD^;fs5z*~UwV zbmB=OhA0h(Y0E;usxSbGI;i|!#mxKI&7nIYsr`}aBi|RBFrcWS`d%XEws}>|8OG?) OhMFv#RPFc`kv{<`xeb~C literal 0 HcmV?d00001 diff --git a/gcloud/dns/demo/demo.py b/gcloud/dns/demo/demo.py new file mode 100644 index 000000000000..e6eca9d89621 --- /dev/null +++ b/gcloud/dns/demo/demo.py @@ -0,0 +1,26 @@ +# Welcome to the gCloud DNS Demo! (hit enter) + +# We're going to walk through some of the basics..., +# Don't worry though. You don't need to do anything, just keep hitting enter... + +# Let's start by importing the demo module and getting a connection: +from gcloud.dns import demo +connection = demo.get_connection() + +# Lets create a zone. +zone = connection.create_zone('zone', 'zone.com.', 'My zone.') + +# Lets see what records the zone has... +print connection.get_records('zone') + +# Lets add a A record to the zone. +zone.add_a('zone.com.', ['1.1.1.1'], 9000) + +# Lets commit the changes of the zone with... +zone.save() + +# Lets see what records the zone has... +print connection.get_records('zone') + +# Finally lets clean up and delete our test zone. +zone.delete(force=True) diff --git a/gcloud/dns/exceptions.py b/gcloud/dns/exceptions.py new file mode 100644 index 000000000000..dff61f3cf740 --- /dev/null +++ b/gcloud/dns/exceptions.py @@ -0,0 +1,6 @@ +from gcloud.exceptions import Error +# TODO: Make these super useful. + + +class DNSError(Error): + pass diff --git a/gcloud/dns/record.py b/gcloud/dns/record.py new file mode 100644 index 000000000000..857bcb62f5aa --- /dev/null +++ b/gcloud/dns/record.py @@ -0,0 +1,65 @@ +class Record(object): + """A class representing a Resource Record Set on Cloud DNS. + + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + + :type type: string + :param string: The type of DNS record. + """ + + def __init__(self, name=None, data=[], ttl=None, type=None): + self.name = name + self.data = data + self.ttl = ttl + self.type = type + + @classmethod + def from_dict(cls, record_dict): + """Construct a new record from a dictionary of data from Cloud DNS. + + :type record_dict: dict + :param record_dict: The dictionary of data to construct a record from. + + :rtype: :class:`Record` + :returns: A record constructed from the data provided. + """ + + return cls(name=record_dict['name'], data=record_dict['rrdatas'], + ttl=record_dict['ttl'], type=record_dict['type']) + + def __str__(self): + """Format the record when printed. + + :rtype: string + :returns: A formated record string. + """ + + record = ('{name} {ttl} IN {type} {data}') + return record.format(name=self.name, ttl=self.ttl, type=self.type, + data=self.data) + + def add_data(self, data): + """Add to the list of resource record data for the record. + + :type data: string + :param data: The textual representation of a resourse record. + """ + + self.data.append(data) + + def to_dict(self): + """Format the record into a dict compatible with Cloud DNS. + + :rtype: dict + :returns: A Cloud DNS dict representation of a record. + """ + + return {'name': self.name, 'rrdatas': self.data, 'ttl': self.ttl, + 'type': self.type} diff --git a/gcloud/dns/zone.py b/gcloud/dns/zone.py new file mode 100644 index 000000000000..bb9924da81a8 --- /dev/null +++ b/gcloud/dns/zone.py @@ -0,0 +1,260 @@ +from gcloud.dns.change import Change +from gcloud.dns.record import Record + + +class Zone(object): + """A class representing a Managed Zone on Cloud DNS. + + :type connection: :class:`gcloud.dns.connection.Connection` + :param connection: The connection to use when sending requests. + + :type creation_time: string + :param connection_time: Time that this zone was created on the server. + + :type description: string + :param data: A description of the zone. + + :type dns_name: string + :param data: The DNS name of the zone. + + :type id: unsigned long + :param data: Unique identifier defined by the server. + + :type kind: string + :param data: Identifies what kind of resource. + + :type name_servers: list + :param name_servers: List of virtual name servers of the zone. + """ + + def __init__(self, connection=None, creation_time=None, + description=None, dns_name=None, id=None, kind=None, name=None, + name_servers=None): + self.additions = [] + self.connection = connection + self.creation_time = creation_time + self.deletions = [] + self.description = description + self.dns_name = dns_name + self.id = id + self.kind = kind + self.name = name + self.name_servers = name_servers + + @classmethod + def from_dict(cls, zone_dict, connection=None): + """Construct a new zone from a dictionary of data from Cloud DNS. + + :type zone_dict: dict + :param zone_dict: The dictionary of data to construct a record from. + + :rtype: :class:`Zone` + :returns: A zone constructed from the data provided. + """ + + return cls(connection=connection, + creation_time=zone_dict['creationTime'], + description=zone_dict['description'], + dns_name=zone_dict['dnsName'], id=zone_dict['id'], + kind=zone_dict['kind'], name=zone_dict['name'], + name_servers=zone_dict['nameServers']) + + @property + def path(self): + """The URL path to this zone.""" + + if not self.connection.project: + raise ValueError('Cannot determine path without project name.') + + return self.connection.project + '/managedZones/' + + def delete(self, force=False): + """Delete this zone. + + The zone **must** be empty in order to delete it. + + If you want to delete a non-empty zone you can pass + in a force parameter set to true. + This will iterate through the zones's records and delete the related + records, before deleting the zone. + + :type force: bool + :param full: If True, deletes the zones's records then deletes it. + """ + + return self.connection.delete_zone(self.name, force=force) + + def save(self): + """Commit all the additions and deletions of records on this zone. + """ + + change = Change(additions=self.additions, deletions=self.deletions) + self.connection.save_change(self.name, change.to_dict()) + self.additions = [] + self.deletions = [] + return True + + def add_record(self, record): + """Add a record to the dict of records to be added to the zone. + + :type record: dict or :class:`Record` + :param record: A dict representation of a record to be added. + """ + + if isinstance(record, Record): + record = record.to_dict() + + if isinstance(record, dict): + self.additions.append(record) + + # Throw type error here. + + def remove_record(self, record): + """Add a record to the dict of records to be deleted to the zone. + + :type record: dict or :class:`Record` + :param record: A dict representation of a record to be deleted. + """ + + if isinstance(record, Record): + record = record.to_dict() + + if isinstance(record, dict): + self.deletions.append(record) + + # Throw type error here. + + def add_a(self, name, data, ttl): + """ Shortcut method to add a A record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'A') + self.add_record(record) + + def add_aaaa(self, name, data, ttl): + """ Shortcut method to add a AAAA record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'AAAA') + self.add_record(record) + + def add_cname(self, name, data, ttl): + """ Shortcut method to add a CNAME record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'CNAME') + self.add_record(record) + + def add_mx(self, name, data, ttl): + """ Shortcut method to add a MX record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'MX') + self.add_record(record) + + def add_ns(self, name, data, ttl): + """ Shortcut method to add a NS record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'NS') + self.add_record(record) + + def add_ptr(self, name, data, ttl): + """ Shortcut method to add a PTR record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'PTR') + self.add_record(record) + + def add_soa(self, name, data, ttl): + """ Shortcut method to add a SOA record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'SOA') + self.add_record(record) + + def add_spf(self, name, data, ttl): + """ Shortcut method to add a SRV record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'SRV') + self.add_record(record) + + def add_txt(self, name, data, ttl): + """ Shortcut method to add a TXT record to be added to the zone. + :type name: string + :param name: The name of the record, for example 'www.example.com.'. + + :type data: list + :param data: A list of the textual representation of Resource Records. + + :type ttl: int + :param ttl: The record's time to live. + """ + + record = Record(name, data, ttl, 'TXT') + self.add_record(record) diff --git a/gcloud/exceptions.py b/gcloud/exceptions.py new file mode 100644 index 000000000000..b05d5586307b --- /dev/null +++ b/gcloud/exceptions.py @@ -0,0 +1,18 @@ +# TODO: Make these super useful. + + +class Error(Exception): + pass + + +class ConnectionError(Error): + + def __init__(self, response, content): + message = str(response) + content + super(ConnectionError, self).__init__(message) + + +class NotFoundError(Error): + + def __init__(self, response, content): + self.message = 'GET %s returned a 404.' % (response.url)