Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/boto/route53/connection.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
comparison
equal
deleted
inserted
replaced
4:79f47841a781 | 5:9b1c78e6ba9c |
---|---|
1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ | |
2 # Copyright (c) 2010, Eucalyptus Systems, Inc. | |
3 # Copyright (c) 2011 Blue Pines Technologies LLC, Brad Carleton | |
4 # www.bluepines.org | |
5 # Copyright (c) 2012 42 Lines Inc., Jim Browne | |
6 # | |
7 # Permission is hereby granted, free of charge, to any person obtaining a | |
8 # copy of this software and associated documentation files (the | |
9 # "Software"), to deal in the Software without restriction, including | |
10 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
11 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
12 # persons to whom the Software is furnished to do so, subject to the fol- | |
13 # lowing conditions: | |
14 # | |
15 # The above copyright notice and this permission notice shall be included | |
16 # in all copies or substantial portions of the Software. | |
17 # | |
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
19 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
20 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
21 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
22 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
24 # IN THE SOFTWARE. | |
25 # | |
26 | |
27 from boto.route53 import exception | |
28 import random | |
29 import uuid | |
30 import xml.sax | |
31 | |
32 import boto | |
33 from boto.connection import AWSAuthConnection | |
34 from boto import handler | |
35 import boto.jsonresponse | |
36 from boto.route53.record import ResourceRecordSets | |
37 from boto.route53.zone import Zone | |
38 from boto.compat import six, urllib | |
39 | |
40 | |
41 HZXML = """<?xml version="1.0" encoding="UTF-8"?> | |
42 <CreateHostedZoneRequest xmlns="%(xmlns)s"> | |
43 <Name>%(name)s</Name> | |
44 <CallerReference>%(caller_ref)s</CallerReference> | |
45 <HostedZoneConfig> | |
46 <Comment>%(comment)s</Comment> | |
47 </HostedZoneConfig> | |
48 </CreateHostedZoneRequest>""" | |
49 | |
50 HZPXML = """<?xml version="1.0" encoding="UTF-8"?> | |
51 <CreateHostedZoneRequest xmlns="%(xmlns)s"> | |
52 <Name>%(name)s</Name> | |
53 <VPC> | |
54 <VPCId>%(vpc_id)s</VPCId> | |
55 <VPCRegion>%(vpc_region)s</VPCRegion> | |
56 </VPC> | |
57 <CallerReference>%(caller_ref)s</CallerReference> | |
58 <HostedZoneConfig> | |
59 <Comment>%(comment)s</Comment> | |
60 </HostedZoneConfig> | |
61 </CreateHostedZoneRequest>""" | |
62 | |
63 # boto.set_stream_logger('dns') | |
64 | |
65 | |
66 class Route53Connection(AWSAuthConnection): | |
67 DefaultHost = 'route53.amazonaws.com' | |
68 """The default Route53 API endpoint to connect to.""" | |
69 | |
70 Version = '2013-04-01' | |
71 """Route53 API version.""" | |
72 | |
73 XMLNameSpace = 'https://route53.amazonaws.com/doc/2013-04-01/' | |
74 """XML schema for this Route53 API version.""" | |
75 | |
76 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, | |
77 port=None, proxy=None, proxy_port=None, | |
78 host=DefaultHost, debug=0, security_token=None, | |
79 validate_certs=True, https_connection_factory=None, | |
80 profile_name=None): | |
81 super(Route53Connection, self).__init__( | |
82 host, | |
83 aws_access_key_id, aws_secret_access_key, | |
84 True, port, proxy, proxy_port, debug=debug, | |
85 security_token=security_token, | |
86 validate_certs=validate_certs, | |
87 https_connection_factory=https_connection_factory, | |
88 profile_name=profile_name) | |
89 | |
90 def _required_auth_capability(self): | |
91 return ['route53'] | |
92 | |
93 def make_request(self, action, path, headers=None, data='', params=None): | |
94 if params: | |
95 pairs = [] | |
96 for key, val in six.iteritems(params): | |
97 if val is None: | |
98 continue | |
99 pairs.append(key + '=' + urllib.parse.quote(str(val))) | |
100 path += '?' + '&'.join(pairs) | |
101 return super(Route53Connection, self).make_request( | |
102 action, path, headers, data, | |
103 retry_handler=self._retry_handler) | |
104 | |
105 # Hosted Zones | |
106 | |
107 def get_all_hosted_zones(self, start_marker=None, zone_list=None): | |
108 """ | |
109 Returns a Python data structure with information about all | |
110 Hosted Zones defined for the AWS account. | |
111 | |
112 :param int start_marker: start marker to pass when fetching additional | |
113 results after a truncated list | |
114 :param list zone_list: a HostedZones list to prepend to results | |
115 """ | |
116 params = {} | |
117 if start_marker: | |
118 params = {'marker': start_marker} | |
119 response = self.make_request('GET', '/%s/hostedzone' % self.Version, | |
120 params=params) | |
121 body = response.read() | |
122 boto.log.debug(body) | |
123 if response.status >= 300: | |
124 raise exception.DNSServerError(response.status, | |
125 response.reason, | |
126 body) | |
127 e = boto.jsonresponse.Element(list_marker='HostedZones', | |
128 item_marker=('HostedZone',)) | |
129 h = boto.jsonresponse.XmlHandler(e, None) | |
130 h.parse(body) | |
131 if zone_list: | |
132 e['ListHostedZonesResponse']['HostedZones'].extend(zone_list) | |
133 while 'NextMarker' in e['ListHostedZonesResponse']: | |
134 next_marker = e['ListHostedZonesResponse']['NextMarker'] | |
135 zone_list = e['ListHostedZonesResponse']['HostedZones'] | |
136 e = self.get_all_hosted_zones(next_marker, zone_list) | |
137 return e | |
138 | |
139 def get_hosted_zone(self, hosted_zone_id): | |
140 """ | |
141 Get detailed information about a particular Hosted Zone. | |
142 | |
143 :type hosted_zone_id: str | |
144 :param hosted_zone_id: The unique identifier for the Hosted Zone | |
145 | |
146 """ | |
147 uri = '/%s/hostedzone/%s' % (self.Version, hosted_zone_id) | |
148 response = self.make_request('GET', uri) | |
149 body = response.read() | |
150 boto.log.debug(body) | |
151 if response.status >= 300: | |
152 raise exception.DNSServerError(response.status, | |
153 response.reason, | |
154 body) | |
155 e = boto.jsonresponse.Element(list_marker='NameServers', | |
156 item_marker=('NameServer',)) | |
157 h = boto.jsonresponse.XmlHandler(e, None) | |
158 h.parse(body) | |
159 return e | |
160 | |
161 def get_hosted_zone_by_name(self, hosted_zone_name): | |
162 """ | |
163 Get detailed information about a particular Hosted Zone. | |
164 | |
165 :type hosted_zone_name: str | |
166 :param hosted_zone_name: The fully qualified domain name for the Hosted | |
167 Zone | |
168 | |
169 """ | |
170 if hosted_zone_name[-1] != '.': | |
171 hosted_zone_name += '.' | |
172 all_hosted_zones = self.get_all_hosted_zones() | |
173 for zone in all_hosted_zones['ListHostedZonesResponse']['HostedZones']: | |
174 # check that they gave us the FQDN for their zone | |
175 if zone['Name'] == hosted_zone_name: | |
176 return self.get_hosted_zone(zone['Id'].split('/')[-1]) | |
177 | |
178 def create_hosted_zone(self, domain_name, caller_ref=None, comment='', | |
179 private_zone=False, vpc_id=None, vpc_region=None): | |
180 """ | |
181 Create a new Hosted Zone. Returns a Python data structure with | |
182 information about the newly created Hosted Zone. | |
183 | |
184 :type domain_name: str | |
185 :param domain_name: The name of the domain. This should be a | |
186 fully-specified domain, and should end with a final period | |
187 as the last label indication. If you omit the final period, | |
188 Amazon Route 53 assumes the domain is relative to the root. | |
189 This is the name you have registered with your DNS registrar. | |
190 It is also the name you will delegate from your registrar to | |
191 the Amazon Route 53 delegation servers returned in | |
192 response to this request.A list of strings with the image | |
193 IDs wanted. | |
194 | |
195 :type caller_ref: str | |
196 :param caller_ref: A unique string that identifies the request | |
197 and that allows failed CreateHostedZone requests to be retried | |
198 without the risk of executing the operation twice. If you don't | |
199 provide a value for this, boto will generate a Type 4 UUID and | |
200 use that. | |
201 | |
202 :type comment: str | |
203 :param comment: Any comments you want to include about the hosted | |
204 zone. | |
205 | |
206 :type private_zone: bool | |
207 :param private_zone: Set True if creating a private hosted zone. | |
208 | |
209 :type vpc_id: str | |
210 :param vpc_id: When creating a private hosted zone, the VPC Id to | |
211 associate to is required. | |
212 | |
213 :type vpc_region: str | |
214 :param vpc_region: When creating a private hosted zone, the region | |
215 of the associated VPC is required. | |
216 | |
217 """ | |
218 if caller_ref is None: | |
219 caller_ref = str(uuid.uuid4()) | |
220 if private_zone: | |
221 params = {'name': domain_name, | |
222 'caller_ref': caller_ref, | |
223 'comment': comment, | |
224 'vpc_id': vpc_id, | |
225 'vpc_region': vpc_region, | |
226 'xmlns': self.XMLNameSpace} | |
227 xml_body = HZPXML % params | |
228 else: | |
229 params = {'name': domain_name, | |
230 'caller_ref': caller_ref, | |
231 'comment': comment, | |
232 'xmlns': self.XMLNameSpace} | |
233 xml_body = HZXML % params | |
234 uri = '/%s/hostedzone' % self.Version | |
235 response = self.make_request('POST', uri, | |
236 {'Content-Type': 'text/xml'}, xml_body) | |
237 body = response.read() | |
238 boto.log.debug(body) | |
239 if response.status == 201: | |
240 e = boto.jsonresponse.Element(list_marker='NameServers', | |
241 item_marker=('NameServer',)) | |
242 h = boto.jsonresponse.XmlHandler(e, None) | |
243 h.parse(body) | |
244 return e | |
245 else: | |
246 raise exception.DNSServerError(response.status, | |
247 response.reason, | |
248 body) | |
249 | |
250 def delete_hosted_zone(self, hosted_zone_id): | |
251 """ | |
252 Delete the hosted zone specified by the given id. | |
253 | |
254 :type hosted_zone_id: str | |
255 :param hosted_zone_id: The hosted zone's id | |
256 | |
257 """ | |
258 uri = '/%s/hostedzone/%s' % (self.Version, hosted_zone_id) | |
259 response = self.make_request('DELETE', uri) | |
260 body = response.read() | |
261 boto.log.debug(body) | |
262 if response.status not in (200, 204): | |
263 raise exception.DNSServerError(response.status, | |
264 response.reason, | |
265 body) | |
266 e = boto.jsonresponse.Element() | |
267 h = boto.jsonresponse.XmlHandler(e, None) | |
268 h.parse(body) | |
269 return e | |
270 | |
271 # Health checks | |
272 | |
273 POSTHCXMLBody = """<CreateHealthCheckRequest xmlns="%(xmlns)s"> | |
274 <CallerReference>%(caller_ref)s</CallerReference> | |
275 %(health_check)s | |
276 </CreateHealthCheckRequest>""" | |
277 | |
278 def create_health_check(self, health_check, caller_ref=None): | |
279 """ | |
280 Create a new Health Check | |
281 | |
282 :type health_check: HealthCheck | |
283 :param health_check: HealthCheck object | |
284 | |
285 :type caller_ref: str | |
286 :param caller_ref: A unique string that identifies the request | |
287 and that allows failed CreateHealthCheckRequest requests to be retried | |
288 without the risk of executing the operation twice. If you don't | |
289 provide a value for this, boto will generate a Type 4 UUID and | |
290 use that. | |
291 | |
292 """ | |
293 if caller_ref is None: | |
294 caller_ref = str(uuid.uuid4()) | |
295 uri = '/%s/healthcheck' % self.Version | |
296 params = {'xmlns': self.XMLNameSpace, | |
297 'caller_ref': caller_ref, | |
298 'health_check': health_check.to_xml() | |
299 } | |
300 xml_body = self.POSTHCXMLBody % params | |
301 response = self.make_request('POST', uri, {'Content-Type': 'text/xml'}, xml_body) | |
302 body = response.read() | |
303 boto.log.debug(body) | |
304 if response.status == 201: | |
305 e = boto.jsonresponse.Element() | |
306 h = boto.jsonresponse.XmlHandler(e, None) | |
307 h.parse(body) | |
308 return e | |
309 else: | |
310 raise exception.DNSServerError(response.status, response.reason, body) | |
311 | |
312 def get_list_health_checks(self, maxitems=None, marker=None): | |
313 """ | |
314 Return a list of health checks | |
315 | |
316 :type maxitems: int | |
317 :param maxitems: Maximum number of items to return | |
318 | |
319 :type marker: str | |
320 :param marker: marker to get next set of items to list | |
321 | |
322 """ | |
323 | |
324 params = {} | |
325 if maxitems is not None: | |
326 params['maxitems'] = maxitems | |
327 if marker is not None: | |
328 params['marker'] = marker | |
329 | |
330 uri = '/%s/healthcheck' % (self.Version, ) | |
331 response = self.make_request('GET', uri, params=params) | |
332 body = response.read() | |
333 boto.log.debug(body) | |
334 if response.status >= 300: | |
335 raise exception.DNSServerError(response.status, | |
336 response.reason, | |
337 body) | |
338 e = boto.jsonresponse.Element(list_marker='HealthChecks', | |
339 item_marker=('HealthCheck',)) | |
340 h = boto.jsonresponse.XmlHandler(e, None) | |
341 h.parse(body) | |
342 return e | |
343 | |
344 def get_checker_ip_ranges(self): | |
345 """ | |
346 Return a list of Route53 healthcheck IP ranges | |
347 """ | |
348 uri = '/%s/checkeripranges' % self.Version | |
349 response = self.make_request('GET', uri) | |
350 body = response.read() | |
351 boto.log.debug(body) | |
352 if response.status >= 300: | |
353 raise exception.DNSServerError(response.status, | |
354 response.reason, | |
355 body) | |
356 e = boto.jsonresponse.Element(list_marker='CheckerIpRanges', item_marker=('member',)) | |
357 h = boto.jsonresponse.XmlHandler(e, None) | |
358 h.parse(body) | |
359 return e | |
360 | |
361 def delete_health_check(self, health_check_id): | |
362 """ | |
363 Delete a health check | |
364 | |
365 :type health_check_id: str | |
366 :param health_check_id: ID of the health check to delete | |
367 | |
368 """ | |
369 uri = '/%s/healthcheck/%s' % (self.Version, health_check_id) | |
370 response = self.make_request('DELETE', uri) | |
371 body = response.read() | |
372 boto.log.debug(body) | |
373 if response.status not in (200, 204): | |
374 raise exception.DNSServerError(response.status, | |
375 response.reason, | |
376 body) | |
377 e = boto.jsonresponse.Element() | |
378 h = boto.jsonresponse.XmlHandler(e, None) | |
379 h.parse(body) | |
380 return e | |
381 | |
382 # Resource Record Sets | |
383 | |
384 def get_all_rrsets(self, hosted_zone_id, type=None, | |
385 name=None, identifier=None, maxitems=None): | |
386 """ | |
387 Retrieve the Resource Record Sets defined for this Hosted Zone. | |
388 Returns the raw XML data returned by the Route53 call. | |
389 | |
390 :type hosted_zone_id: str | |
391 :param hosted_zone_id: The unique identifier for the Hosted Zone | |
392 | |
393 :type type: str | |
394 :param type: The type of resource record set to begin the record | |
395 listing from. Valid choices are: | |
396 | |
397 * A | |
398 * AAAA | |
399 * CNAME | |
400 * MX | |
401 * NS | |
402 * PTR | |
403 * SOA | |
404 * SPF | |
405 * SRV | |
406 * TXT | |
407 | |
408 Valid values for weighted resource record sets: | |
409 | |
410 * A | |
411 * AAAA | |
412 * CNAME | |
413 * TXT | |
414 | |
415 Valid values for Zone Apex Aliases: | |
416 | |
417 * A | |
418 * AAAA | |
419 | |
420 :type name: str | |
421 :param name: The first name in the lexicographic ordering of domain | |
422 names to be retrieved | |
423 | |
424 :type identifier: str | |
425 :param identifier: In a hosted zone that includes weighted resource | |
426 record sets (multiple resource record sets with the same DNS | |
427 name and type that are differentiated only by SetIdentifier), | |
428 if results were truncated for a given DNS name and type, | |
429 the value of SetIdentifier for the next resource record | |
430 set that has the current DNS name and type | |
431 | |
432 :type maxitems: int | |
433 :param maxitems: The maximum number of records | |
434 | |
435 """ | |
436 params = {'type': type, 'name': name, | |
437 'identifier': identifier, 'maxitems': maxitems} | |
438 uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id) | |
439 response = self.make_request('GET', uri, params=params) | |
440 body = response.read() | |
441 boto.log.debug(body) | |
442 if response.status >= 300: | |
443 raise exception.DNSServerError(response.status, | |
444 response.reason, | |
445 body) | |
446 rs = ResourceRecordSets(connection=self, hosted_zone_id=hosted_zone_id) | |
447 h = handler.XmlHandler(rs, self) | |
448 xml.sax.parseString(body, h) | |
449 return rs | |
450 | |
451 def change_rrsets(self, hosted_zone_id, xml_body): | |
452 """ | |
453 Create or change the authoritative DNS information for this | |
454 Hosted Zone. | |
455 Returns a Python data structure with information about the set of | |
456 changes, including the Change ID. | |
457 | |
458 :type hosted_zone_id: str | |
459 :param hosted_zone_id: The unique identifier for the Hosted Zone | |
460 | |
461 :type xml_body: str | |
462 :param xml_body: The list of changes to be made, defined in the | |
463 XML schema defined by the Route53 service. | |
464 | |
465 """ | |
466 uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id) | |
467 response = self.make_request('POST', uri, | |
468 {'Content-Type': 'text/xml'}, | |
469 xml_body) | |
470 body = response.read() | |
471 boto.log.debug(body) | |
472 if response.status >= 300: | |
473 raise exception.DNSServerError(response.status, | |
474 response.reason, | |
475 body) | |
476 e = boto.jsonresponse.Element() | |
477 h = boto.jsonresponse.XmlHandler(e, None) | |
478 h.parse(body) | |
479 return e | |
480 | |
481 def get_change(self, change_id): | |
482 """ | |
483 Get information about a proposed set of changes, as submitted | |
484 by the change_rrsets method. | |
485 Returns a Python data structure with status information about the | |
486 changes. | |
487 | |
488 :type change_id: str | |
489 :param change_id: The unique identifier for the set of changes. | |
490 This ID is returned in the response to the change_rrsets method. | |
491 | |
492 """ | |
493 uri = '/%s/change/%s' % (self.Version, change_id) | |
494 response = self.make_request('GET', uri) | |
495 body = response.read() | |
496 boto.log.debug(body) | |
497 if response.status >= 300: | |
498 raise exception.DNSServerError(response.status, | |
499 response.reason, | |
500 body) | |
501 e = boto.jsonresponse.Element() | |
502 h = boto.jsonresponse.XmlHandler(e, None) | |
503 h.parse(body) | |
504 return e | |
505 | |
506 def create_zone(self, name, private_zone=False, | |
507 vpc_id=None, vpc_region=None): | |
508 """ | |
509 Create a new Hosted Zone. Returns a Zone object for the newly | |
510 created Hosted Zone. | |
511 | |
512 :type name: str | |
513 :param name: The name of the domain. This should be a | |
514 fully-specified domain, and should end with a final period | |
515 as the last label indication. If you omit the final period, | |
516 Amazon Route 53 assumes the domain is relative to the root. | |
517 This is the name you have registered with your DNS registrar. | |
518 It is also the name you will delegate from your registrar to | |
519 the Amazon Route 53 delegation servers returned in | |
520 response to this request. | |
521 | |
522 :type private_zone: bool | |
523 :param private_zone: Set True if creating a private hosted zone. | |
524 | |
525 :type vpc_id: str | |
526 :param vpc_id: When creating a private hosted zone, the VPC Id to | |
527 associate to is required. | |
528 | |
529 :type vpc_region: str | |
530 :param vpc_region: When creating a private hosted zone, the region | |
531 of the associated VPC is required. | |
532 """ | |
533 zone = self.create_hosted_zone(name, private_zone=private_zone, | |
534 vpc_id=vpc_id, vpc_region=vpc_region) | |
535 return Zone(self, zone['CreateHostedZoneResponse']['HostedZone']) | |
536 | |
537 def get_zone(self, name): | |
538 """ | |
539 Returns a Zone object for the specified Hosted Zone. | |
540 | |
541 :param name: The name of the domain. This should be a | |
542 fully-specified domain, and should end with a final period | |
543 as the last label indication. | |
544 """ | |
545 name = self._make_qualified(name) | |
546 for zone in self.get_zones(): | |
547 if name == zone.name: | |
548 return zone | |
549 | |
550 def get_zones(self): | |
551 """ | |
552 Returns a list of Zone objects, one for each of the Hosted | |
553 Zones defined for the AWS account. | |
554 | |
555 :rtype: list | |
556 :returns: A list of Zone objects. | |
557 | |
558 """ | |
559 zones = self.get_all_hosted_zones() | |
560 return [Zone(self, zone) for zone in | |
561 zones['ListHostedZonesResponse']['HostedZones']] | |
562 | |
563 def _make_qualified(self, value): | |
564 """ | |
565 Ensure passed domain names end in a period (.) character. | |
566 This will usually make a domain fully qualified. | |
567 """ | |
568 if type(value) in [list, tuple, set]: | |
569 new_list = [] | |
570 for record in value: | |
571 if record and not record[-1] == '.': | |
572 new_list.append("%s." % record) | |
573 else: | |
574 new_list.append(record) | |
575 return new_list | |
576 else: | |
577 value = value.strip() | |
578 if value and not value[-1] == '.': | |
579 value = "%s." % value | |
580 return value | |
581 | |
582 def _retry_handler(self, response, i, next_sleep): | |
583 status = None | |
584 boto.log.debug("Saw HTTP status: %s" % response.status) | |
585 | |
586 if response.status == 400: | |
587 body = response.read() | |
588 | |
589 # We need to parse the error first | |
590 err = exception.DNSServerError( | |
591 response.status, | |
592 response.reason, | |
593 body) | |
594 if err.error_code: | |
595 # This is a case where we need to ignore a 400 error, as | |
596 # Route53 returns this. See | |
597 # http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html | |
598 if not err.error_code in ( | |
599 'PriorRequestNotComplete', | |
600 'Throttling', | |
601 'ServiceUnavailable', | |
602 'RequestExpired'): | |
603 return status | |
604 msg = "%s, retry attempt %s" % ( | |
605 err.error_code, | |
606 i | |
607 ) | |
608 next_sleep = min(random.random() * (2 ** i), | |
609 boto.config.get('Boto', 'max_retry_delay', 60)) | |
610 i += 1 | |
611 status = (msg, i, next_sleep) | |
612 | |
613 return status |