def test_record_count(self): rrs = ResourceRecordSets(self.conn, self.zone.id) hosts = 101 for hostid in range(hosts): rec = "test" + str(hostid) + ".%s" % self.base_domain created = rrs.add_change("CREATE", rec, "A") ip = '192.168.0.' + str(hostid) created.add_value(ip) # Max 100 changes per commit if (hostid + 1) % 100 == 0: rrs.commit() rrs = ResourceRecordSets(self.conn, self.zone.id) rrs.commit() all_records = self.conn.get_all_rrsets(self.zone.id) # First time around was always fine i = 0 for rset in all_records: i += 1 # Second time was a failure i = 0 for rset in all_records: i += 1 # Cleanup indivual records rrs = ResourceRecordSets(self.conn, self.zone.id) for hostid in range(hosts): rec = "test" + str(hostid) + ".%s" % self.base_domain deleted = rrs.add_change("DELETE", rec, "A") ip = '192.168.0.' + str(hostid) deleted.add_value(ip) # Max 100 changes per commit if (hostid + 1) % 100 == 0: rrs.commit() rrs = ResourceRecordSets(self.conn, self.zone.id) rrs.commit() # 2nd count should match the number of hosts plus NS/SOA records records = hosts + 2 self.assertEqual(i, records)
def _add_del(conn, hosted_zone_id, change, name, type, identifier, weight, values, ttl, comment): from boto.route53.record import ResourceRecordSets changes = ResourceRecordSets(conn, hosted_zone_id, comment) change = changes.add_change(change, name, type, ttl, identifier=identifier, weight=weight) for value in values.split(','): change.add_value(value) print changes.commit()
def _addRR(self, rectype, host, ip, ttl=300): if self.zoneid is not None: if self.changes is None: self.changes = ResourceRecordSets(self.ar53, self.zoneid) change = self.changes.add_change("CREATE", host, rectype, ttl) change.add_value(ip) else: print "Zone not set, or does not exist in DNS"
def configure_domain(): conn = boto.connect_route53() changes = ResourceRecordSets(conn, env.hosted_zone_id) for url in env.urls: change = changes.add_change("CREATE", url, "CNAME") change.add_value(env.public_host) changes.commit()
def test_incomplete_add_alias_failure(self): base_record = dict(name="alias.%s." % self.base_domain, type="A", alias_dns_name="target.%s" % self.base_domain, alias_hosted_zone_id=self.zone.id, identifier="boto:TestRoute53AliasResourceRecordSets") rrs = ResourceRecordSets(self.conn, self.zone.id) rrs.add_change(action="UPSERT", **base_record) try: self.assertRaises(DNSServerError, rrs.commit) except: # if the call somehow goes through, delete our unexpected new record before failing test rrs = ResourceRecordSets(self.conn, self.zone.id) rrs.add_change(action="DELETE", **base_record) rrs.commit() raise
def add_record(self, resource_type, name, value, ttl=60, comment=""): """Add a new record to a zone""" changes = ResourceRecordSets(route53, self.id, comment) change = changes.add_change("CREATE", name, resource_type, ttl) if type(value) in [list, tuple, set]: for record in value: change.add_value(record) else: change.add_value(value) status = Status(changes.commit()['ChangeResourceRecordSetsResponse']['ChangeInfo'])
def add_record(self, resource_type, name, value, ttl=60, identifier=None, comment=""): """ Add a new record to this Zone. See _new_record for parameter documentation. Returns a Status object. """ changes = ResourceRecordSets(self.route53connection, self.id, comment) self._new_record(changes, resource_type, name, value, ttl, identifier, comment) return Status(self.route53connection, self._commit(changes))
def main(): # Get your public IP from the hypervisor # If you wish to use this on a non-AWS server, use http://ip.42.pl/raw instead current_ip = urllib2.urlopen('http://169.254.169.254/latest/meta-data/public-ipv4').read() # Avoid to hit the Route53 API if is not necessary. # so compare first to a DNS server if the IP changed resolved_ip = resolve_name_ip(DOMAIN_NAME) if resolved_ip == current_ip: logger.debug('DNS response (%s) and public IP (%s) are the same, nothing to do' % (resolved_ip, current_ip)) return conn = Route53Connection() try: zone = conn.get_hosted_zone(HOSTED_ZONE) except DNSServerError: logger.error('%s Zone Not Found' % HOSTED_ZONE) sys.exit(1) response = conn.get_all_rrsets(HOSTED_ZONE, 'A', DOMAIN_NAME, maxitems=1)[0] if current_ip not in response.resource_records: logger.info('Found new IP: %s' % current_ip) # Delete the old record, and create a new one. # This code is from route53.py script, the change record command changes = ResourceRecordSets(conn, HOSTED_ZONE, '') change1 = changes.add_change("DELETE", DOMAIN_NAME, 'A', response.ttl) for old_value in response.resource_records: change1.add_value(old_value) change2 = changes.add_change("CREATE", DOMAIN_NAME, 'A', response.ttl) change2.add_value(current_ip) try: commit = changes.commit() logger.debug('%s' % commit) except: logger.error("Changes can't be made: %s" % commit) sys.exit(1) else: change = conn.get_change(get_change_id(commit['ChangeResourceRecordSetsResponse'])) logger.debug('%s' % change) while get_change_status(change['GetChangeResponse']) == 'PENDING': time.sleep(2) change = conn.get_change(get_change_id(change['GetChangeResponse'])) logger.debug('%s' % change) if get_change_status(change['GetChangeResponse']) == 'INSYNC': logger.info('Change %s A de %s -> %s' % (DOMAIN_NAME, response.resource_records[0], current_ip)) else: logger.warning('Unknow status for the change: %s' % change) logger.debug('%s' % change)
def delete_record(self, resource_type, name, value, ttl=None, comment=""): """Delete a record from a zone""" ttl = ttl or default_ttl changes = ResourceRecordSets(route53, self.id, comment) change = changes.add_change("DELETE", name, resource_type, ttl) if type(value) in [list, tuple, set]: for record in value: change.add_value(record) else: change.add_value(value) status = Status(changes.commit()['ChangeResourceRecordSetsResponse']['ChangeInfo'])
def _add_del_alias(conn, hosted_zone_id, change, name, type, identifier, weight, alias_hosted_zone_id, alias_dns_name, comment): from boto.route53.record import ResourceRecordSets changes = ResourceRecordSets(conn, hosted_zone_id, comment) change = changes.add_change(change, name, type, identifier=identifier, weight=weight) change.set_alias(alias_hosted_zone_id, alias_dns_name) print changes.commit()
def update_dns_cname_record(conn, zone_id, cname_record, cname_value): # zone_id = 'Z2IBYTQ6W9V2HA' # cname_record = 'sol1-salt1.devopslogic.com' result = None try: changes = ResourceRecordSets(conn, zone_id) change = changes.add_change("UPSERT", cname_record, "CNAME", 60) change.add_value(cname_value) result = changes.commit() except Exception, e: print "Exception: %s" % e
def cname(route53conn, zone, domain_name, alt_name, ttl=60, remove=False): from boto.route53.record import ResourceRecordSets zone_id = get_zone_id(zone) changes = ResourceRecordSets(route53conn, zone_id) change = changes.add_change("DELETE" if remove else "CREATE", name=domain_name, type="CNAME", ttl=ttl) if alt_name: change.add_value(alt_name) changes.commit()
def acquire_master_cname(self, force=False): """ Use Route53 to update the master_cname record to point to this instance. If the CNAME already exists and force is False, an exception will be raised. Setting force to True will cause this function to 'take' the DNS record. """ try: answers = dns.resolver.query(self.master_cname, 'CNAME') except dns.resolver.NXDOMAIN: master_cname_exists = False self.logger.info('%s does not exist, so creating it' % self.master_cname) else: master_cname_exists = True old_cname_value = answers.rrset.items[0].to_text() if old_cname_value == '%s.' % self.metadata['public-hostname']: self.logger.info( '%s already points to this instance - not doing anything' % self.master_cname) return self.logger.info('%s already exists, so updating it' % self.master_cname) if master_cname_exists == True and force == False: self.logger.critical( 'CNAME %s exists and force is false - exiting' % self.master_cname) raise Exception( 'CNAME %s exists and force is False - not taking the CNAME' % self.master_cname) # if we get here, either the CNAME does not exist or Force is true, so we should take the CNAME route53_conn = self._get_route53_conn() changes = ResourceRecordSets(route53_conn, settings.ROUTE53_ZONE_ID) if master_cname_exists: self.logger.info('Deleting existing record for %s' % self.master_cname) del_record = changes.add_change('DELETE', self.master_cname, 'CNAME', ttl=settings.MASTER_CNAME_TTL) del_record.add_value(old_cname_value) self.logger.info('Creating record for %s' % self.master_cname) add_record = changes.add_change('CREATE', self.master_cname, 'CNAME', ttl=settings.MASTER_CNAME_TTL) add_record.add_value(self.metadata['public-hostname']) changes.commit() self.logger.info('Finished updating DNS records')
def delete_record(self, name): changes = ResourceRecordSets(self.route53, self.zone_id) value = None sets = self.route53.get_all_rrsets(self.zone_id, None) for rset in sets: if rset.name == name + ".": value = rset.resource_records[0] if value != None: change = changes.add_change("DELETE", name + ".", "CNAME", 60) change.add_value(value) changes.commit()
def r53_change_record(name, values, aws_access_key_id, aws_secret_access_key, proxy=None, proxy_port=None, type="A", ttl="600", comment=""): # note: if the network is unreachable this function will retry, # blocking up to one minute before the last retry (not configurable?) conn = boto.connect_route53(aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, proxy=proxy, proxy_port=proxy_port) res = conn.get_all_hosted_zones() domain_name = re.sub('^[^\.]*\.', '', name) if name[0] == '.': name = name[1:] hosted_zone_id = None for zoneinfo in res['ListHostedZonesResponse']['HostedZones']: zonename = zoneinfo['Name'] _zone_id = zoneinfo['Id'] _zone_id = re.sub('/hostedzone/', '', _zone_id) if zonename[-1] == '.': zonename = zonename[:-1] #print domain_name, zonename if domain_name == zonename: hosted_zone_id = _zone_id break if not hosted_zone_id: raise RuntimeError, 'domain_name ' + repr( domain_name) + ' not found in hosted zones' changes = ResourceRecordSets(conn, hosted_zone_id, comment) response = conn.get_all_rrsets(hosted_zone_id, type, name, maxitems=1) if response: rrset = response[0] change1 = changes.add_change("DELETE", name, type, rrset.ttl) for old_value in rrset.resource_records: change1.add_value(old_value) change2 = changes.add_change("CREATE", name, type, ttl) for new_value in values.split(','): change2.add_value(new_value) return changes.commit()
def _changeAlias(self, change, name, recordType, aliasHostedZoneId, aliasDNSName, identifier, weight, comment): logging.info('%s alias %s:%s in zone %s', change, name, recordType, self.domain) changes = ResourceRecordSets(self.connection, self.id, comment) change = changes.add_change(change, name, recordType, identifier=identifier, weight=weight) change.set_alias(aliasHostedZoneId, aliasDNSName) changes.commit()
def update_record(self, name, value): changes = ResourceRecordSets(self.route53, self.zone_id) sets = self.route53.get_all_rrsets(self.zone_id, None) for rset in sets: if rset.name == name + ".": previous_value = rset.resource_records[0] change = changes.add_change("DELETE", name + ".", "CNAME", 60) change.add_value(previous_value) change = changes.add_change("CREATE", name + ".", "CNAME", 60) change.add_value(value) changes.commit()
def test_rrset_with_multiple_values(): conn = boto.connect_route53("the_key", "the_secret") zone = conn.create_hosted_zone("testdns.aws.com") zoneid = zone["CreateHostedZoneResponse"]["HostedZone"]["Id"].split("/")[-1] changes = ResourceRecordSets(conn, zoneid) change = changes.add_change("CREATE", "foo.bar.testdns.aws.com", "A") change.add_value("1.2.3.4") change.add_value("5.6.7.8") changes.commit() rrsets = conn.get_all_rrsets(zoneid) rrsets.should.have.length_of(1) set(rrsets[0].resource_records).should.equal(set(["1.2.3.4", "5.6.7.8"]))
def test_rrset(): conn = boto.connect_route53('the_key', 'the_secret') conn.get_all_rrsets.when.called_with("abcd", type="A").\ should.throw(boto.route53.exception.DNSServerError, "404 Not Found") zone = conn.create_hosted_zone("testdns.aws.com") zoneid = zone["CreateHostedZoneResponse"]["HostedZone"]["Id"] changes = ResourceRecordSets(conn, zoneid) change = changes.add_change("CREATE", "foo.bar.testdns.aws.com", "A") change.add_value("1.2.3.4") changes.commit() rrsets = conn.get_all_rrsets(zoneid, type="A") rrsets.should.have.length_of(1) rrsets[0].resource_records[0].should.equal('1.2.3.4') rrsets = conn.get_all_rrsets(zoneid, type="CNAME") rrsets.should.have.length_of(0) changes = ResourceRecordSets(conn, zoneid) changes.add_change("DELETE", "foo.bar.testdns.aws.com", "A") change = changes.add_change("CREATE", "foo.bar.testdns.aws.com", "A") change.add_value("5.6.7.8") changes.commit() rrsets = conn.get_all_rrsets(zoneid, type="A") rrsets.should.have.length_of(1) rrsets[0].resource_records[0].should.equal('5.6.7.8') changes = ResourceRecordSets(conn, zoneid) changes.add_change("DELETE", "foo.bar.testdns.aws.com", "A") changes.commit() rrsets = conn.get_all_rrsets(zoneid) rrsets.should.have.length_of(0)
def add_alias_record(self, resource_type, name, alias_hosted_zone_id, alias_dns_name, identifier=None, comment=""): """ Add a new alias record to this Zone. See _new_alias for parameter documentation. Returns a Status object. """ changes = ResourceRecordSets(self.route53connection, self.id, comment) self._new_alias_record(changes, resource_type, name, identifier, alias_hosted_zone_id, alias_dns_name, comment) return Status(self.route53connection, self._commit(changes))
def _changeRecord(self, change, name, recordType, values, ttl): logging.info('%s record %s:%s in zone %s', change, name, recordType, self.domain) if type(values) is not types.ListType: values = [values] changes = ResourceRecordSets(self.connection, self.id) change = changes.add_change(change, name, recordType, ttl) for value in values: change.add_value(value) changes.commit()
def get_all_rrsets(self, hosted_zone_id, type=None, name=None, maxitems=None): """ Retrieve the Resource Record Sets defined for this Hosted Zone. Returns the raw XML data returned by the Route53 call. :type hosted_zone_id: str :param hosted_zone_id: The unique identifier for the Hosted Zone :type type: str :param type: The type of resource record set to begin the record listing from. Valid choices are: * A * AAAA * CNAME * MX * NS * PTR * SOA * SPF * SRV * TXT :type name: str :param name: The first name in the lexicographic ordering of domain names to be retrieved :type maxitems: int :param maxitems: The maximum number of records """ from boto.route53.record import ResourceRecordSets params = {'type': type, 'name': name, 'maxitems': maxitems} uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id) response = self.make_request('GET', uri, params=params) body = response.read() boto.log.debug(body) if response.status >= 300: raise exception.DNSServerError(response.status, response.reason, body) rs = ResourceRecordSets(connection=self, hosted_zone_id=hosted_zone_id) h = handler.XmlHandler(rs, self) xml.sax.parseString(body, h) return rs
def r53_change_record(name, values, aws_access_key_id, aws_secret_access_key, proxy=None, proxy_port=None, type="A", ttl="600", comment=""): conn = boto.connect_route53(aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, proxy=proxy, proxy_port=proxy_port) res = conn.get_all_hosted_zones() domain_name = re.sub('^[^\.]*\.', '', name) if name[0] == '.': name = name[1:] hosted_zone_id = None for zoneinfo in res['ListHostedZonesResponse']['HostedZones']: zonename = zoneinfo['Name'] _zone_id = zoneinfo['Id'] _zone_id = re.sub('/hostedzone/', '', _zone_id) if zonename[-1] == '.': zonename = zonename[:-1] logging.debug("%s %s" % (domain_name, zonename)) if domain_name == zonename: hosted_zone_id = _zone_id break if not hosted_zone_id: raise NotInHostedZonesError(name) changes = ResourceRecordSets(conn, hosted_zone_id, comment) response = conn.get_all_rrsets(hosted_zone_id, type, name, maxitems=1) if response: rrset = response[0] change1 = changes.add_change("DELETE", name, type, rrset.ttl) for old_value in rrset.resource_records: change1.add_value(old_value) change2 = changes.add_change("CREATE", name, type, ttl) for new_value in values.split(','): change2.add_value(new_value) return changes.commit()
def test_alias_rrset(): conn = boto.connect_route53('the_key', 'the_secret') zone = conn.create_hosted_zone("testdns.aws.com") zoneid = zone["CreateHostedZoneResponse"]["HostedZone"]["Id"].split("/")[-1] changes = ResourceRecordSets(conn, zoneid) changes.add_change("CREATE", "foo.alias.testdns.aws.com", "A", alias_hosted_zone_id="Z3DG6IL3SJCGPX", alias_dns_name="foo.testdns.aws.com") changes.add_change("CREATE", "bar.alias.testdns.aws.com", "CNAME", alias_hosted_zone_id="Z3DG6IL3SJCGPX", alias_dns_name="bar.testdns.aws.com") changes.commit() rrsets = conn.get_all_rrsets(zoneid, type="A") rrsets.should.have.length_of(1) rrsets[0].resource_records[0].should.equal('foo.testdns.aws.com') rrsets = conn.get_all_rrsets(zoneid, type="CNAME") rrsets.should.have.length_of(1) rrsets[0].resource_records[0].should.equal('bar.testdns.aws.com')
def start_rr_transaction(self): """ Creates a new Route53 ResourceRecordSets object that is used internally like a transaction of sorts. You may add or delete many resource records using a single set by calling the `add_record` and `delete_record` methods. Finish the transaction with `finish_rr_transaction` NOTE: Calling this method again before finishing will not finish an existing transaction or delete it. To cancel an existing transaction use the `cancel_rr_transaction`. """ if self._rr_txn is None: # Return a new ResourceRecordSets "transaction" self._rr_txn = ResourceRecordSets(self.conn, self.zone_id)
def update_record(self, resource_type, name, old_value, new_value, old_ttl, new_ttl=None, comment=""): new_ttl = new_ttl or default_ttl changes = ResourceRecordSets(route53, self.id, comment) change = changes.add_change("DELETE", name, resource_type, old_ttl) if type(old_value) in [list, tuple, set]: for record in old_value: change.add_value(record) else: change.add_value(old_value) change = changes.add_change('CREATE', name, resource_type, new_ttl) if type(new_value) in [list, tuple, set]: for record in new_value: change.add_value(record) else: change.add_value(new_value) status = Status(changes.commit()['ChangeResourceRecordSetsResponse']['ChangeInfo'])
def delete_record(self, record, comment=""): """ Delete one or more records from this Zone. Returns a Status object. :param record: A ResourceRecord (e.g. returned by find_records) or list, tuple, or set of ResourceRecords. :type comment: str :param comment: A comment that will be stored with the change. """ changes = ResourceRecordSets(self.route53connection, self.id, comment) if type(record) in [list, tuple, set]: for r in record: changes.add_change_record("DELETE", r) else: changes.add_change_record("DELETE", record) return Status(self.route53connection, self._commit(changes))
def update_record(self, old_record, new_value, new_ttl=None, new_identifier=None, comment=""): """ Update an existing record in this Zone. Returns a Status object. :type old_record: ResourceRecord :param old_record: A ResourceRecord (e.g. returned by find_records) See _new_record for additional parameter documentation. """ new_ttl = new_ttl or default_ttl record = copy.copy(old_record) changes = ResourceRecordSets(self.route53connection, self.id, comment) changes.add_change_record("DELETE", record) self._new_record(changes, record.type, record.name, new_value, new_ttl, new_identifier, comment) return Status(self.route53connection, self._commit(changes))
def remove_from_slave_cname_pool(self): """ Remove this instance from the pool of slave hostnames, usually after a promotion. """ route53_conn = self._get_route53_conn() changes = ResourceRecordSets(route53_conn, settings.ROUTE53_ZONE_ID) self.logger.info('Removing %s from CNAME pool for %s' % (self.metadata['instance-id'], self.slave_cname)) del_record = changes.add_change( 'DELETE', self.slave_cname, 'CNAME', ttl=settings.SLAVE_CNAME_TTL, weight='10', identifier=self.metadata['instance-id']) del_record.add_value(self.metadata['public-hostname']) changes.commit()
def _add_dns_record(self, hosted_zone, record_name, record_type, record_value, record_ttl=300, record_comment=''): from boto.route53.record import ResourceRecordSets conn = boto.connect_route53() zone = conn.get_zone(hosted_zone) changes = ResourceRecordSets(conn, zone.id, record_comment) change = changes.add_change('CREATE', '%s.%s' % (record_name, hosted_zone), record_type, record_ttl) change.add_value(record_value) # if not self.config['dry_run']: changes.commit()