~ubuntu-branches/ubuntu/trusty/python-boto/trusty

« back to all changes in this revision

Viewing changes to boto/route53/zone.py

  • Committer: Package Import Robot
  • Author(s): Eric Evans
  • Date: 2013-05-10 23:38:14 UTC
  • mfrom: (1.1.10) (14.1.2 experimental)
  • Revision ID: package-import@ubuntu.com-20130510233814-701dvlop7xfh88i7
Tags: 2.9.2-1
New upstream release (Closes: #700743).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2011 Blue Pines Technologies LLC, Brad Carleton
 
2
# www.bluepines.org
 
3
# Copyright (c) 2012 42 Lines Inc., Jim Browne
 
4
# All rights reserved.
 
5
#
 
6
# Permission is hereby granted, free of charge, to any person obtaining a
 
7
# copy of this software and associated documentation files (the
 
8
# "Software"), to deal in the Software without restriction, including
 
9
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
10
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
11
# persons to whom the Software is furnished to do so, subject to the fol-
 
12
# lowing conditions:
 
13
#
 
14
# The above copyright notice and this permission notice shall be included
 
15
# in all copies or substantial portions of the Software.
 
16
#
 
17
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
18
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
19
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
20
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
21
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
22
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
23
# IN THE SOFTWARE.
 
24
 
 
25
default_ttl = 60
 
26
 
 
27
import copy
 
28
from boto.exception import TooManyRecordsException
 
29
from boto.route53.record import ResourceRecordSets
 
30
from boto.route53.status import Status
 
31
 
 
32
 
 
33
class Zone(object):
 
34
    """
 
35
    A Route53 Zone.
 
36
 
 
37
    :ivar Route53Connection route53connection
 
38
    :ivar str Id: The ID of the hosted zone.
 
39
    """
 
40
    def __init__(self, route53connection, zone_dict):
 
41
        self.route53connection = route53connection
 
42
        for key in zone_dict:
 
43
            if key == 'Id':
 
44
                self.id = zone_dict['Id'].replace('/hostedzone/', '')
 
45
            else:
 
46
                self.__setattr__(key.lower(), zone_dict[key])
 
47
 
 
48
    def __repr__(self):
 
49
        return '<Zone:%s>' % self.name
 
50
 
 
51
    def _commit(self, changes):
 
52
        """
 
53
        Commit a set of changes and return the ChangeInfo portion of
 
54
        the response.
 
55
 
 
56
        :type changes: ResourceRecordSets
 
57
        :param changes: changes to be committed
 
58
        """
 
59
        response = changes.commit()
 
60
        return response['ChangeResourceRecordSetsResponse']['ChangeInfo']
 
61
 
 
62
    def _new_record(self, changes, resource_type, name, value, ttl, identifier,
 
63
                   comment=""):
 
64
        """
 
65
        Add a CREATE change record to an existing ResourceRecordSets
 
66
 
 
67
        :type changes: ResourceRecordSets
 
68
        :param changes: change set to append to
 
69
 
 
70
        :type name: str
 
71
        :param name: The name of the resource record you want to
 
72
            perform the action on.
 
73
 
 
74
        :type resource_type: str
 
75
        :param resource_type: The DNS record type
 
76
 
 
77
        :param value: Appropriate value for resource_type
 
78
 
 
79
        :type ttl: int
 
80
        :param ttl: The resource record cache time to live (TTL), in seconds.
 
81
 
 
82
        :type identifier: tuple
 
83
        :param identifier: A tuple for setting WRR or LBR attributes.  Valid
 
84
           forms are:
 
85
 
 
86
           * (str, int): WRR record [e.g. ('foo',10)]
 
87
           * (str, str): LBR record [e.g. ('foo','us-east-1')
 
88
 
 
89
        :type comment: str
 
90
        :param comment: A comment that will be stored with the change.
 
91
        """
 
92
        weight = None
 
93
        region = None
 
94
        if identifier is not None:
 
95
            try:
 
96
                int(identifier[1])
 
97
                weight = identifier[1]
 
98
                identifier = identifier[0]
 
99
            except:
 
100
                region = identifier[1]
 
101
                identifier = identifier[0]
 
102
        change = changes.add_change("CREATE", name, resource_type, ttl,
 
103
                                    identifier=identifier, weight=weight,
 
104
                                    region=region)
 
105
        if type(value) in [list, tuple, set]:
 
106
            for record in value:
 
107
                change.add_value(record)
 
108
        else:
 
109
            change.add_value(value)
 
110
 
 
111
    def add_record(self, resource_type, name, value, ttl=60, identifier=None,
 
112
                   comment=""):
 
113
        """
 
114
        Add a new record to this Zone.  See _new_record for parameter
 
115
        documentation.  Returns a Status object.
 
116
        """
 
117
        changes = ResourceRecordSets(self.route53connection, self.id, comment)
 
118
        self._new_record(changes, resource_type, name, value, ttl, identifier,
 
119
                         comment)
 
120
        return Status(self.route53connection, self._commit(changes))
 
121
 
 
122
    def update_record(self, old_record, new_value, new_ttl=None,
 
123
                      new_identifier=None, comment=""):
 
124
        """
 
125
        Update an existing record in this Zone.  Returns a Status object.
 
126
 
 
127
        :type old_record: ResourceRecord
 
128
        :param old_record: A ResourceRecord (e.g. returned by find_records)
 
129
 
 
130
        See _new_record for additional parameter documentation.
 
131
        """
 
132
        new_ttl = new_ttl or default_ttl
 
133
        record = copy.copy(old_record)
 
134
        changes = ResourceRecordSets(self.route53connection, self.id, comment)
 
135
        changes.add_change_record("DELETE", record)
 
136
        self._new_record(changes, record.type, record.name,
 
137
                         new_value, new_ttl, new_identifier, comment)
 
138
        return Status(self.route53connection, self._commit(changes))
 
139
 
 
140
    def delete_record(self, record, comment=""):
 
141
        """
 
142
        Delete one or more records from this Zone.  Returns a Status object.
 
143
 
 
144
        :param record: A ResourceRecord (e.g. returned by
 
145
           find_records) or list, tuple, or set of ResourceRecords.
 
146
 
 
147
        :type comment: str
 
148
        :param comment: A comment that will be stored with the change.
 
149
        """
 
150
        changes = ResourceRecordSets(self.route53connection, self.id, comment)
 
151
        if type(record) in [list, tuple, set]:
 
152
            for r in record:
 
153
                changes.add_change_record("DELETE", r)
 
154
        else:
 
155
            changes.add_change_record("DELETE", record)
 
156
        return Status(self.route53connection, self._commit(changes))
 
157
 
 
158
    def add_cname(self, name, value, ttl=None, identifier=None, comment=""):
 
159
        """
 
160
        Add a new CNAME record to this Zone.  See _new_record for
 
161
        parameter documentation.  Returns a Status object.
 
162
        """
 
163
        ttl = ttl or default_ttl
 
164
        name = self.route53connection._make_qualified(name)
 
165
        value = self.route53connection._make_qualified(value)
 
166
        return self.add_record(resource_type='CNAME',
 
167
                               name=name,
 
168
                               value=value,
 
169
                               ttl=ttl,
 
170
                               identifier=identifier,
 
171
                               comment=comment)
 
172
 
 
173
    def add_a(self, name, value, ttl=None, identifier=None, comment=""):
 
174
        """
 
175
        Add a new A record to this Zone.  See _new_record for
 
176
        parameter documentation.  Returns a Status object.
 
177
        """
 
178
        ttl = ttl or default_ttl
 
179
        name = self.route53connection._make_qualified(name)
 
180
        return self.add_record(resource_type='A',
 
181
                               name=name,
 
182
                               value=value,
 
183
                               ttl=ttl,
 
184
                               identifier=identifier,
 
185
                               comment=comment)
 
186
 
 
187
    def add_mx(self, name, records, ttl=None, identifier=None, comment=""):
 
188
        """
 
189
        Add a new MX record to this Zone.  See _new_record for
 
190
        parameter documentation.  Returns a Status object.
 
191
        """
 
192
        ttl = ttl or default_ttl
 
193
        records = self.route53connection._make_qualified(records)
 
194
        return self.add_record(resource_type='MX',
 
195
                               name=name,
 
196
                               value=records,
 
197
                               ttl=ttl,
 
198
                               identifier=identifier,
 
199
                               comment=comment)
 
200
 
 
201
    def find_records(self, name, type, desired=1, all=False, identifier=None):
 
202
        """
 
203
        Search this Zone for records that match given parameters.
 
204
        Returns None if no results, a ResourceRecord if one result, or
 
205
        a ResourceRecordSets if more than one result.
 
206
 
 
207
        :type name: str
 
208
        :param name: The name of the records should match this parameter
 
209
 
 
210
        :type type: str
 
211
        :param type: The type of the records should match this parameter
 
212
 
 
213
        :type desired: int
 
214
        :param desired: The number of desired results.  If the number of
 
215
           matching records in the Zone exceeds the value of this parameter,
 
216
           throw TooManyRecordsException
 
217
 
 
218
        :type all: Boolean
 
219
        :param all: If true return all records that match name, type, and
 
220
          identifier parameters
 
221
 
 
222
        :type identifier: Tuple
 
223
        :param identifier: A tuple specifying WRR or LBR attributes.  Valid
 
224
           forms are:
 
225
 
 
226
           * (str, int): WRR record [e.g. ('foo',10)]
 
227
           * (str, str): LBR record [e.g. ('foo','us-east-1')
 
228
 
 
229
        """
 
230
        name = self.route53connection._make_qualified(name)
 
231
        returned = self.route53connection.get_all_rrsets(self.id, name=name,
 
232
                                                         type=type)
 
233
 
 
234
        # name/type for get_all_rrsets sets the starting record; they
 
235
        # are not a filter
 
236
        results = [r for r in returned if r.name == name and r.type == type]
 
237
 
 
238
        weight = None
 
239
        region = None
 
240
        if identifier is not None:
 
241
            try:
 
242
                int(identifier[1])
 
243
                weight = identifier[1]
 
244
            except:
 
245
                region = identifier[1]
 
246
 
 
247
        if weight is not None:
 
248
            results = [r for r in results if (r.weight == weight and
 
249
                                              r.identifier == identifier[0])]
 
250
        if region is not None:
 
251
            results = [r for r in results if (r.region == region and
 
252
                                              r.identifier == identifier[0])]
 
253
 
 
254
        if ((not all) and (len(results) > desired)):
 
255
            message = "Search: name %s type %s" % (name, type)
 
256
            message += "\nFound: "
 
257
            message += ", ".join(["%s %s %s" % (r.name, r.type, r.to_print())
 
258
                                  for r in results])
 
259
            raise TooManyRecordsException(message)
 
260
        elif len(results) > 1:
 
261
            return results
 
262
        elif len(results) == 1:
 
263
            return results[0]
 
264
        else:
 
265
            return None
 
266
 
 
267
    def get_cname(self, name, all=False):
 
268
        """
 
269
        Search this Zone for CNAME records that match name.
 
270
 
 
271
        Returns a ResourceRecord.
 
272
 
 
273
        If there is more than one match return all as a
 
274
        ResourceRecordSets if all is True, otherwise throws
 
275
        TooManyRecordsException.
 
276
        """
 
277
        return self.find_records(name, 'CNAME', all=all)
 
278
 
 
279
    def get_a(self, name, all=False):
 
280
        """
 
281
        Search this Zone for A records that match name.
 
282
 
 
283
        Returns a ResourceRecord.
 
284
 
 
285
        If there is more than one match return all as a
 
286
        ResourceRecordSets if all is True, otherwise throws
 
287
        TooManyRecordsException.
 
288
        """
 
289
        return self.find_records(name, 'A', all=all)
 
290
 
 
291
    def get_mx(self, name, all=False):
 
292
        """
 
293
        Search this Zone for MX records that match name.
 
294
 
 
295
        Returns a ResourceRecord.
 
296
 
 
297
        If there is more than one match return all as a
 
298
        ResourceRecordSets if all is True, otherwise throws
 
299
        TooManyRecordsException.
 
300
        """
 
301
        return self.find_records(name, 'MX', all=all)
 
302
 
 
303
    def update_cname(self, name, value, ttl=None, identifier=None, comment=""):
 
304
        """
 
305
        Update the given CNAME record in this Zone to a new value, ttl,
 
306
        and identifier.  Returns a Status object.
 
307
 
 
308
        Will throw TooManyRecordsException is name, value does not match
 
309
        a single record.
 
310
        """
 
311
        name = self.route53connection._make_qualified(name)
 
312
        value = self.route53connection._make_qualified(value)
 
313
        old_record = self.get_cname(name)
 
314
        ttl = ttl or old_record.ttl
 
315
        return self.update_record(old_record,
 
316
                                  new_value=value,
 
317
                                  new_ttl=ttl,
 
318
                                  new_identifier=identifier,
 
319
                                  comment=comment)
 
320
 
 
321
    def update_a(self, name, value, ttl=None, identifier=None, comment=""):
 
322
        """
 
323
        Update the given A record in this Zone to a new value, ttl,
 
324
        and identifier.  Returns a Status object.
 
325
 
 
326
        Will throw TooManyRecordsException is name, value does not match
 
327
        a single record.
 
328
        """
 
329
        name = self.route53connection._make_qualified(name)
 
330
        old_record = self.get_a(name)
 
331
        ttl = ttl or old_record.ttl
 
332
        return self.update_record(old_record,
 
333
                                  new_value=value,
 
334
                                  new_ttl=ttl,
 
335
                                  new_identifier=identifier,
 
336
                                  comment=comment)
 
337
 
 
338
    def update_mx(self, name, value, ttl=None, identifier=None, comment=""):
 
339
        """
 
340
        Update the given MX record in this Zone to a new value, ttl,
 
341
        and identifier.  Returns a Status object.
 
342
 
 
343
        Will throw TooManyRecordsException is name, value does not match
 
344
        a single record.
 
345
        """
 
346
        name = self.route53connection._make_qualified(name)
 
347
        value = self.route53connection._make_qualified(value)
 
348
        old_record = self.get_mx(name)
 
349
        ttl = ttl or old_record.ttl
 
350
        return self.update_record(old_record,
 
351
                                  new_value=value,
 
352
                                  new_ttl=ttl,
 
353
                                  new_identifier=identifier,
 
354
                                  comment=comment)
 
355
 
 
356
    def delete_cname(self, name, identifier=None, all=False):
 
357
        """
 
358
        Delete a CNAME record matching name and identifier from
 
359
        this Zone.  Returns a Status object.
 
360
 
 
361
        If there is more than one match delete all matching records if
 
362
        all is True, otherwise throws TooManyRecordsException.
 
363
        """
 
364
        name = self.route53connection._make_qualified(name)
 
365
        record = self.find_records(name, 'CNAME', identifier=identifier,
 
366
                                   all=all)
 
367
        return self.delete_record(record)
 
368
 
 
369
    def delete_a(self, name, identifier=None, all=False):
 
370
        """
 
371
        Delete an A record matching name and identifier from this
 
372
        Zone.  Returns a Status object.
 
373
 
 
374
        If there is more than one match delete all matching records if
 
375
        all is True, otherwise throws TooManyRecordsException.
 
376
        """
 
377
        name = self.route53connection._make_qualified(name)
 
378
        record = self.find_records(name, 'A', identifier=identifier,
 
379
                                   all=all)
 
380
        return self.delete_record(record)
 
381
 
 
382
    def delete_mx(self, name, identifier=None, all=False):
 
383
        """
 
384
        Delete an MX record matching name and identifier from this
 
385
        Zone.  Returns a Status object.
 
386
 
 
387
        If there is more than one match delete all matching records if
 
388
        all is True, otherwise throws TooManyRecordsException.
 
389
        """
 
390
        name = self.route53connection._make_qualified(name)
 
391
        record = self.find_records(name, 'MX', identifier=identifier,
 
392
                                   all=all)
 
393
        return self.delete_record(record)
 
394
 
 
395
    def get_records(self):
 
396
        """
 
397
        Return a ResourceRecordsSets for all of the records in this zone.
 
398
        """
 
399
        return self.route53connection.get_all_rrsets(self.id)
 
400
 
 
401
    def delete(self):
 
402
        """
 
403
        Request that this zone be deleted by Amazon.
 
404
        """
 
405
        self.route53connection.delete_hosted_zone(self.id)
 
406
 
 
407
    def get_nameservers(self):
 
408
        """ Get the list of nameservers for this zone."""
 
409
        ns = self.find_records(self.name, 'NS')
 
410
        if ns is not None:
 
411
            ns = ns.resource_records
 
412
        return ns