Example #1
0
def test_explicit_rollback_and_commit(zone):
    with zone.writer() as txn:
        assert not txn.changed()
        txn.delete(dns.name.from_text('bar.foo', None))
        txn.rollback()
    assert zone.get_node('bar.foo') is not None
    with zone.writer() as txn:
        assert not txn.changed()
        txn.delete(dns.name.from_text('bar.foo', None))
        txn.commit()
    assert zone.get_node('bar.foo') is None
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.delete(dns.name.from_text('bar.foo', None))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.add('bar.foo', 300, dns.rdata.from_text('in', 'txt', 'hi'))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.replace('bar.foo', 300, dns.rdata.from_text('in', 'txt', 'hi'))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.reader() as txn:
            txn.rollback()
            txn.get('bar.foo', 'in', 'mx')
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.delete_exact('bar.foo')
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.name_exists('bar.foo')
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.update_serial()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.changed()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.rollback()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.commit()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            for rdataset in txn:
                print(rdataset)
Example #2
0
def test_explicit_rollback_and_commit(zone):
    with zone.writer() as txn:
        assert not txn.changed()
        txn.delete(dns.name.from_text("bar.foo", None))
        txn.rollback()
    assert zone.get_node("bar.foo") is not None
    with zone.writer() as txn:
        assert not txn.changed()
        txn.delete(dns.name.from_text("bar.foo", None))
        txn.commit()
    assert zone.get_node("bar.foo") is None
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.delete(dns.name.from_text("bar.foo", None))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.add("bar.foo", 300, dns.rdata.from_text("in", "txt", "hi"))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.replace("bar.foo", 300, dns.rdata.from_text("in", "txt", "hi"))
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.reader() as txn:
            txn.rollback()
            txn.get("bar.foo", "in", "mx")
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.delete_exact("bar.foo")
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.name_exists("bar.foo")
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.update_serial()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.changed()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.rollback()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            txn.commit()
    with pytest.raises(dns.transaction.AlreadyEnded):
        with zone.writer() as txn:
            txn.rollback()
            for rdataset in txn:
                pass
Example #3
0
def convert_zone(domain, zone):
    new_zone = dns.zone.Zone(origin=(domain.rstrip('.') + '.'))
    for name in zone:
        new_name = adjust_node_name(zone.origin, domain, str(name))
        node = zone.get_node(name)
        for rdataset in node.rdatasets:
            new_rdataset = new_zone.find_rdataset(new_name, rdtype=rdataset.rdtype, create=True)
            for rdata in rdataset:
                new_rdata = dns.rdata.from_text(1, rdata.rdtype, rdata.to_text())

                if rdataset.rdtype == dns.rdatatype.CNAME:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(adjust_node_name(zone.origin, domain, str(new_rdata.target.derelativize(zone.origin))), origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.MX:
                    if not new_rdata.exchange.is_absolute():
                        new_rdata.exchange = dns.name.from_text(adjust_node_name(zone.origin, domain, str(new_rdata.exchange.derelativize(zone.origin))), origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.NS:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(adjust_node_name(zone.origin, domain, str(new_rdata.target.derelativize(zone.origin))), origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.SRV:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(adjust_node_name(zone.origin, domain, str(new_rdata.target.derelativize(zone.origin))), origin=new_zone.origin)

                new_rdataset.add(new_rdata, ttl=rdataset.ttl)
    return new_zone
Example #4
0
def cmd_rrcreate(args):
    zone = _get_records(args)
    name = dns.name.from_text(args.rr, zone.origin)
    rdataset = _create_rdataset(args.type, args.ttl, args.values)

    rdataset_old = None
    node = zone.get_node(args.rr)
    if node:
        for rds in node.rdatasets:
            if args.type == dns.rdatatype.to_text(rds.rdtype):
                rdataset_old = rds
                break

    f = BindToR53Formatter()
    if args.replace and rdataset_old:
        parts = f.replace_record(zone, name, rdataset_old, rdataset)
    else:
        parts = f.create_record(zone, name, rdataset)
    for xml in parts:
        ret = r53.change_rrsets(args.zone, xml)
        if args.wait:
            wait_for_sync(ret)
        else:
            print 'Success'
            pprint(ret.ChangeResourceRecordSetsResponse)
Example #5
0
def cmd_rrdelete(args):
    zone = _get_records(args)
    name = dns.name.from_text(args.rr, zone.origin)

    node = zone.get_node(args.rr)
    if node:
        if len(node.rdatasets) > 1 and not args.type:
            rtypes = [ dns.rdatatype.to_text(rds.rdtype) for rds in node.rdatasets ]
            print 'Ambigious record - several resource types for record %r found: %s' % (args.rr, ', '.join(rtypes))
        else:
            rdataset = None
            for rds in node.rdatasets:
                if args.type == dns.rdatatype.to_text(rds.rdtype) or not args.type:
                    rdataset = rds
                    break

            if not rdataset:
                print 'Record not found: %s, type: %s' % (args.rr, args.type)
                return

            print 'Deleting %s %s...' % (args.rr, dns.rdatatype.to_text(rds.rdtype))

            f = BindToR53Formatter()
            for xml in f.delete_record(zone, name, rdataset):
                ret = r53.change_rrsets(args.zone, xml)
                if args.wait:
                    wait_for_sync(ret)
                else:
                    print 'Success'
                    pprint(ret.ChangeResourceRecordSetsResponse)
    else:
        print 'Record not found: %s' % args.rr
Example #6
0
def cmd_rrdelete(args, r53):
    zone = _get_records(args, r53)
    name = dns.name.from_text(args.rr, zone.origin)

    node = zone.get_node(args.rr)
    if node:
        if len(node.rdatasets) > 1 and not args.type:
            rtypes = [dns.rdatatype.to_text(rds.rdtype) for rds in node.rdatasets]
            logging.warning(
                'Ambigious record - several resource types for record %r found: %s' % (
                    args.rr, ', '.join(rtypes)))
        else:
            rdataset = None
            for rds in node.rdatasets:
                if args.type == dns.rdatatype.to_text(rds.rdtype) or not args.type:
                    rdataset = rds
                    break

            if not rdataset:
                logging.warning('Record not found: %s, type: %s' % (args.rr, args.type))
                return

            logging.info('Deleting %s %s...' % (args.rr, dns.rdatatype.to_text(rds.rdtype)))

            f = BindToR53Formatter()
            for xml in f.delete_record(zone, name, rdataset):
                ret = r53.change_rrsets(args.zone, xml)
                if args.wait:
                    wait_for_sync(ret, r53)
                else:
                    logging.info('Success')
                    pprint(ret.ChangeResourceRecordSetsResponse)
    else:
        logging.warning('Record not found: %s' % args.rr)
Example #7
0
def cmd_rrcreate(args):
    zone = _get_records(args)
    name = dns.name.from_text(args.rr, zone.origin)
    rdataset = _create_rdataset(args.type, args.ttl, args.values)

    rdataset_old = None
    node = zone.get_node(args.rr)
    if node:
        for rds in node.rdatasets:
            if args.type == dns.rdatatype.to_text(rds.rdtype):
                rdataset_old = rds
                break

    f = BindToR53Formatter()
    if args.replace and rdataset_old:
        parts = f.replace_record(zone, name, rdataset_old, rdataset)
    else:
        parts = f.create_record(zone, name, rdataset)
    for xml in parts:
        ret = r53.change_rrsets(args.zone, xml)
        if args.wait:
            wait_for_sync(ret)
        else:
            print 'Success'
            pprint(ret.ChangeResourceRecordSetsResponse)
Example #8
0
def cmd_rrcreate(args, r53):
    zone = _get_records(args, r53)
    name = dns.name.from_text(args.rr, zone.origin)
    rdataset = _create_rdataset(args.type, args.ttl, args.values, args.weight, args.identifier, args.region, args.failover)

    rdataset_old = None
    node = zone.get_node(args.rr)
    if node:
        for rds in node.rdatasets:
            if args.type == dns.rdatatype.to_text(rds.rdtype):
                # find the rds in the requested region only
                if args.region is not None:
                    for rdtype in rds.items:
                        if hasattr(rdtype, 'region') and rdtype.region == args.region:
                            rdataset_old = rds
                            break
                else:
                    rdataset_old = rds
                    break

    f = BindToR53Formatter()
    if args.replace and rdataset_old:
        parts = f.replace_record(zone, name, rdataset_old, rdataset)
    else:
        parts = f.create_record(zone, name, rdataset)
    for xml in parts:
        if args.dump:
            logging.debug(xml)
        ret = retry(r53.change_rrsets, args.zone, xml)
        if args.wait:
            wait_for_sync(ret, r53)
        else:
            logging.info('Success')
            pprint(ret.ChangeResourceRecordSetsResponse)
Example #9
0
    def addRecord(self, domain, host, ip, klass, type, ttl):
        host = "%s." % host
        records = [x for x in self.zones if x.domain == domain]
        if not records:
            raise RuntimeError("Invalid domain")

        record = records[0]
        file = record.file
        zone = dns.zone.from_file(file,
                                  os.path.basename(file),
                                  relativize=False)
        node = zone.get_node(host, create=True)

        if type == "A":
            t = dns.rdatatype.A

        if klass == "IN":
            k = dns.rdataclass.IN

        ds = node.get_rdataset(t, k, covers=dns.rdatatype.NONE, create=True)
        ds.ttl = ttl
        if type == "A" and klass == "IN":
            item = A(k, t, ip)
            ds.items.append(item)

        # update version
        for k, v in zone.nodes.iteritems():
            for ds in v.rdatasets:
                if ds.rdtype == dns.rdatatype.SOA:
                    for item in ds.items:
                        item.serial += 1

        zone.to_file(file, relativize=False)
        self.restart()
Example #10
0
def cmd_rrcreate(args, r53):
    zone = _get_records(args, r53)
    name = dns.name.from_text(args.rr, zone.origin)
    rdataset = _create_rdataset(args.type, args.ttl, args.values, args.weight, args.identifier, args.region, args.failover)

    rdataset_old = None
    node = zone.get_node(args.rr)
    if node:
        for rds in node.rdatasets:
            if args.type == dns.rdatatype.to_text(rds.rdtype):
                # find the rds in the requested region only
                if args.region is not None:
                    for rdtype in rds.items:
                        if hasattr(rdtype, 'region') and rdtype.region == args.region:
                            rdataset_old = rds
                            break
                else:
                    rdataset_old = rds
                    break

    f = BindToR53Formatter()
    if args.replace and rdataset_old:
        parts = f.replace_record(zone, name, rdataset_old, rdataset)
    else:
        parts = f.create_record(zone, name, rdataset)
    for xml in parts:
        if args.dump:
            logging.debug(xml)
        ret = retry(r53.change_rrsets, args.zone, xml)
        if args.wait:
            wait_for_sync(ret, r53)
        else:
            logging.info('Success')
            pprint(ret.ChangeResourceRecordSetsResponse)
Example #11
0
def cmd_rrdelete(args):
    zone = _get_records(args)
    name = dns.name.from_text(args.rr, zone.origin)

    node = zone.get_node(args.rr)
    if node:
        if len(node.rdatasets) > 1 and not args.type:
            rtypes = [ dns.rdatatype.to_text(rds.rdtype) for rds in node.rdatasets ]
            print 'Ambigious record - several resource types for record %s found: %s' % (args.rr, ', '.join(rtypes))
        else:
            rdataset = None
            for rds in node.rdatasets:
                if args.type == dns.rdatatype.to_text(rds.rdtype) or not args.type:
                    rdataset = rds
                    break
                    
            if not rdataset:
                print 'Record not found: %s, type: %s' % (args.rr, args.type)
                return
                
            print 'Deleting %s %s...' % (args.rr, dns.rdatatype.to_text(rds.rdtype))
            
            xml = BindToR53Formatter().delete_record(zone, name, rdataset)
            ret = r53.change_rrsets(args.zone, xml)
            print 'Success'
            pprint(ret.ChangeResourceRecordSetsResponse)
    else:
        print 'Record not found: %s' % args.rr
Example #12
0
    def addRecord(self, domain, host, ip, klass, type, ttl):
        host = "%s." % host
        records  = [x for x in self.zones if x.domain == domain]
        if not records:
            raise RuntimeError("Invalid domain")
        
        record = records[0]
        file = record.file
        zone = dns.zone.from_file(file, os.path.basename(file),relativize=False)
        node = zone.get_node(host, create=True)
        
        if type == "A":
            t = dns.rdatatype.A
        
        if klass == "IN":
            k = dns.rdataclass.IN

        ds = node.get_rdataset(t, k, covers=dns.rdatatype.NONE, create=True)
        ds.ttl = ttl
        if type == "A" and klass == "IN":
            item = A(k, t, ip)
            ds.items.append(item)
        
        # update version
        for k, v in zone.nodes.iteritems():
            for ds in v.rdatasets:
                if ds.rdtype == dns.rdatatype.SOA:
                    for item in ds.items:
                        item.serial += 1
        
        zone.to_file(file, relativize=False)
        self.restart()
Example #13
0
def test_zone_rollback(zone):
    a99 = dns.name.from_text("a99.example.")
    try:
        with zone.writer() as txn:
            rds = dns.rdataset.from_text("in", "a", 300, "10.0.0.99")
            txn.add(a99, rds)
            assert txn.name_exists(a99)
            raise ExpectedException
    except ExpectedException:
        pass
    assert not zone.get_node(a99)
Example #14
0
def test_zone_rollback(zone):
    try:
        with zone.writer() as txn:
            a99 = dns.name.from_text('a99.example.')
            rds = dns.rdataset.from_text('in', 'a', 300, '10.0.0.99')
            txn.add(a99, rds)
            assert txn.name_exists(a99)
            raise ExpectedException
    except ExpectedException:
        pass
    assert not zone.get_node(a99)
Example #15
0
def convert_zone(domain, zone):
    new_zone = dns.zone.Zone(origin=(domain.rstrip('.') + '.'))
    for name in zone:
        new_name = adjust_node_name(zone.origin, domain, str(name))
        node = zone.get_node(name)
        for rdataset in node.rdatasets:
            new_rdataset = new_zone.find_rdataset(new_name,
                                                  rdtype=rdataset.rdtype,
                                                  create=True)
            for rdata in rdataset:
                new_rdata = dns.rdata.from_text(1, rdata.rdtype,
                                                rdata.to_text())

                if rdataset.rdtype == dns.rdatatype.CNAME:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(
                            adjust_node_name(
                                zone.origin, domain,
                                str(new_rdata.target.derelativize(
                                    zone.origin))),
                            origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.MX:
                    if not new_rdata.exchange.is_absolute():
                        new_rdata.exchange = dns.name.from_text(
                            adjust_node_name(
                                zone.origin, domain,
                                str(
                                    new_rdata.exchange.derelativize(
                                        zone.origin))),
                            origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.NS:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(
                            adjust_node_name(
                                zone.origin, domain,
                                str(new_rdata.target.derelativize(
                                    zone.origin))),
                            origin=new_zone.origin)
                elif rdataset.rdtype == dns.rdatatype.SRV:
                    if not new_rdata.target.is_absolute():
                        new_rdata.target = dns.name.from_text(
                            adjust_node_name(
                                zone.origin, domain,
                                str(new_rdata.target.derelativize(
                                    zone.origin))),
                            origin=new_zone.origin)

                new_rdataset.add(new_rdata, ttl=rdataset.ttl)
    return new_zone
Example #16
0
def reply_from_zone(zone, query, allnames):
    name = query.question[0].name
    rtype = query.question[0].rdtype
    rclass = query.question[0].rdclass

    if not name.is_subdomain(zone.origin):
        return gen_refused(query)

    # qname is below delegation -> referral to the delegation
    t = name
    while len(t.labels) > len(zone.origin.labels):
        r = zone.get_rrset(t, dns.rdatatype.NS)
        if r:
            return gen_referral(zone, query, r)
        t = t.parent()

    # find exact match to qname & qtype
    r = zone.get_rrset(name, rtype)
    if r:
        return gen_answer(zone, query, (r, ))

    # try CNAME
    r = zone.get_rrset(name, dns.rdatatype.CNAME)
    if r:
        return gen_answer(zone, query, (r, ))
    # try ANY
    if rtype == dns.rdatatype.ANY:
        r = zone.get_node(name)
        rrsets = []
        if r:
            rrsets = []
            for rdataset in r.rdatasets:
                rrsets.append(
                    dns.rrset.from_rdata_list(name, rdataset.ttl, rdataset))
            return gen_answer(zone, query, rrsets)

    # TODO: try DNAME, wildcard,...

    # NODATA
    # 1. rrset not found for qname
    #   (we have A record only for qname but qtype is AAAA)
    # 2. empty non-terminal

    if name in allnames:
        return gen_nxdomain_nodata(zone, query, nxdomain=False)

    # generate NXDOMAIN
    return gen_nxdomain_nodata(zone, query, nxdomain=True)
Example #17
0
def cmd_rrdelete(args, r53):
    zone = _get_records(args, r53)
    name = dns.name.from_text(args.rr, zone.origin)

    node = zone.get_node(args.rr)
    if node:
        if len(node.rdatasets) > 1 and not args.type:
            rtypes = [
                dns.rdatatype.to_text(rds.rdtype) for rds in node.rdatasets
            ]
            logging.warning(
                'Ambigious record - several resource types for record %r found: %s'
                % (args.rr, ', '.join(rtypes)))
        else:
            rdataset = None
            for rds in node.rdatasets:
                if args.type == dns.rdatatype.to_text(
                        rds.rdtype) or not args.type:
                    if args.identifier is not None:
                        for rdtype in rds.items:
                            if hasattr(
                                    rdtype, 'identifier'
                            ) and rdtype.identifier == args.identifier:
                                rdataset = rds
                                break
                    else:
                        rdataset = rds
                        break

            if not rdataset:
                logging.warning('Record not found: %s, type: %s' %
                                (args.rr, args.type))
                return

            logging.info('Deleting %s %s...' %
                         (args.rr, dns.rdatatype.to_text(rds.rdtype)))

            f = BindToR53Formatter()
            for xml in f.delete_record(zone, name, rdataset):
                ret = r53.change_rrsets(args.zone, xml)
                if args.wait:
                    wait_for_sync(ret, r53)
                else:
                    logging.info('Success')
                    pprint(ret.ChangeResourceRecordSetsResponse)
    else:
        logging.warning('Record not found: %s' % args.rr)
Example #18
0
def cmd_instances(args, r53):
    logging.info('Getting DNS records')
    zone = _get_records(args, r53)
    if args.off:
        filters = {}
    else:
        filters = {'instance-state-name': 'running'}

    if args.credentials:
        connections = _read_aws_cfg(args.credentials)
    else:
        connections = [boto.ec2.connect_to_region(region) for region in args.regions.split(',')]

    def get_instances():
        for conn in connections:
            for r in conn.get_all_instances(filters=filters):
                for i in r.instances:
                    yield i

    suffix = '.' + zone.origin.to_text().strip('.')
    creates = []
    deletes = []
    instances = get_instances()
    # limit to instances with a Name tag
    instances = (i for i in instances if i.tags.get('Name'))
    if args.match:
        instances = (i for i in instances if re.search(args.match, i.tags['Name']))
    logging.info('Getting EC2 instances')
    instances_by_name = {}
    for inst in instances:
        name = inst.tags.get('Name')
        if not name:
            continue

        # strip domain suffix if present
        if name.endswith(suffix):
            name = name[0:-len(suffix)]
        name = dns.name.from_text(name, zone.origin)

        if name not in instances_by_name or inst.state == 'running':
            # on duplicate named instances, running takes priority
            instances_by_name[name] = inst

    if args.write_a_record:
        rtype = dns.rdatatype.A
    else:
        rtype = dns.rdatatype.CNAME

    for name, inst in instances_by_name.iteritems():
        node = zone.get_node(name)
        if node and node.rdatasets and node.rdatasets[0].rdtype != rtype:
            # don't replace/update existing manually created records
            logging.warning("Not overwriting record for %s as it appears to have been manually created" % name)
            continue

        newvalue = None
        if inst.state == 'running':
            if inst.public_dns_name and not args.internal:
                newvalue = inst.ip_address if args.write_a_record else inst.public_dns_name
            else:
                newvalue = inst.private_ip_address if args.write_a_record else inst.private_dns_name
        elif args.off == 'delete':
            newvalue = None
        elif args.off and name not in creates:
            newvalue = args.off

        if node:
            if args.write_a_record:
                oldvalue = node.rdatasets[0].items[0].address
            else:
                oldvalue = node.rdatasets[0].items[0].target.strip('.')
            if oldvalue != newvalue:
                if newvalue:
                    logging.info('Updating record for %s: %s -> %s' % (name, oldvalue, newvalue))
                else:
                    logging.info('Deleting record for %s: %s' % (name, oldvalue))
                deletes.append((name, node.rdatasets[0]))
            else:
                logging.debug('Record %s unchanged' % name)
                continue
        else:
            logging.info('Creating record for %s: %s' % (name, newvalue))

        if newvalue:
            if args.write_a_record:
                rd = _create_rdataset('A', args.ttl, [newvalue], None, None, None, None)
            else:
                rd = _create_rdataset('CNAME', args.ttl, [newvalue], None, None, None, None)
            creates.append((name, rd))

    if not deletes and not creates:
        logging.info('No changes')
        return

    if args.dry_run:
        logging.info('Dry run - not making changes')
        return

    f = BindToR53Formatter()
    parts = f.replace_records(zone, creates, deletes)
    for xml in parts:
        ret = retry(r53.change_rrsets, args.zone, xml)
        if args.wait:
            wait_for_sync(ret, r53)
        else:
            logging.info('Success')
            pprint(ret.ChangeResourceRecordSetsResponse)
Example #19
0
def cmd_instances(args, r53):
    logging.info('Getting DNS records')
    zone = _get_records(args, r53)
    if args.off:
        filters = {}
    else:
        filters = {'instance-state-name': 'running'}

    if args.credentials:
        connections = _read_aws_cfg(args.credentials)
    else:
        connections = [
            boto.ec2.connect_to_region(region)
            for region in args.regions.split(',')
        ]

    def get_instances():
        for conn in connections:
            for r in conn.get_all_instances(filters=filters):
                for i in r.instances:
                    yield i

    suffix = '.' + zone.origin.to_text().strip('.')
    creates = []
    deletes = []
    instances = get_instances()
    # limit to instances with a Name tag
    instances = (i for i in instances if i.tags.get('Name'))
    if args.match:
        instances = (i for i in instances
                     if re.search(args.match, i.tags['Name']))
    logging.info('Getting EC2 instances')
    instances_by_name = {}
    for inst in instances:
        name = inst.tags.get('Name')
        if not name:
            continue

        # strip domain suffix if present
        if name.endswith(suffix):
            name = name[0:-len(suffix)]
        name = dns.name.from_text(name, zone.origin)

        if name not in instances_by_name or inst.state == 'running':
            # on duplicate named instances, running takes priority
            instances_by_name[name] = inst

    if args.write_a_record:
        rtype = dns.rdatatype.A
    else:
        rtype = dns.rdatatype.CNAME

    for name, inst in instances_by_name.iteritems():
        node = zone.get_node(name)
        if node and node.rdatasets and node.rdatasets[0].rdtype != rtype:
            # don't replace/update existing manually created records
            logging.warning(
                "Not overwriting record for %s as it appears to have been manually created"
                % name)
            continue

        newvalue = None
        if inst.state == 'running':
            if inst.public_dns_name and not args.internal:
                newvalue = inst.ip_address if args.write_a_record else inst.public_dns_name
            else:
                newvalue = inst.private_ip_address if args.write_a_record else inst.private_dns_name
        elif args.off == 'delete':
            newvalue = None
        elif args.off and name not in creates:
            newvalue = args.off

        if node:
            if args.write_a_record:
                oldvalue = node.rdatasets[0].items[0].address
            else:
                oldvalue = node.rdatasets[0].items[0].target.strip('.')
            if oldvalue != newvalue:
                if newvalue:
                    logging.info('Updating record for %s: %s -> %s' %
                                 (name, oldvalue, newvalue))
                else:
                    logging.info('Deleting record for %s: %s' %
                                 (name, oldvalue))
                deletes.append((name, node.rdatasets[0]))
            else:
                logging.debug('Record %s unchanged' % name)
                continue
        else:
            logging.info('Creating record for %s: %s' % (name, newvalue))

        if newvalue:
            if args.write_a_record:
                rd = _create_rdataset('A', args.ttl, [newvalue], None, None,
                                      None)
            else:
                rd = _create_rdataset('CNAME', args.ttl, [newvalue], None,
                                      None, None)
            creates.append((name, rd))

    if not deletes and not creates:
        logging.info('No changes')
        return

    if args.dry_run:
        logging.info('Dry run - not making changes')
        return

    f = BindToR53Formatter()
    parts = f.replace_records(zone, creates, deletes)
    for xml in parts:
        ret = r53.change_rrsets(args.zone, xml)
        if args.wait:
            wait_for_sync(ret, r53)
        else:
            logging.info('Success')
            pprint(ret.ChangeResourceRecordSetsResponse)