Ejemplo n.º 1
0
 def get_records(self):
     if self.name != 'test.':
         return []
     r = Record()
     r.name = '_etcd-server._tcp.cluster.' + self.name
     r.type = 'SRV'
     return [r]
Ejemplo n.º 2
0
 def get_records(self):
     if self.name != 'test.':
         return []
     r = Record()
     r.name = '_etcd-server._tcp.cluster.' + self.name
     r.type = 'SRV'
     return [r]
Ejemplo n.º 3
0
    def create_record(self, hosted_zone_name, record_name, record_type, value):
        """
        Create a DNS record. Update an existing record if one already exists.
        Args:
            hosted_zone_name (str): the domain of the Hosted Zone
            record_name (str): the name of the record
            record_type (str): the type of record (A, AAAA, CNAME, etc)
            value (str): a single value to insert into the record
        """
        zone = throttled_call(self.route53.get_zone, hosted_zone_name)

        record = Record(record_name, record_type, ttl=5)
        record.add_value(value)

        logger.info("Setting Record %s of type %s to %s", record_name, record_type, value)

        changes = throttled_call(self.route53.get_all_rrsets, zone.id)
        changes.add_change_record('UPSERT', record)

        throttled_call(changes.commit)
Ejemplo n.º 4
0
    def create_record(self, hosted_zone_name, record_name, record_type, value):
        """
        Create a DNS record. Update an existing record if one already exists.
        Args:
            hosted_zone_name (str): the domain of the Hosted Zone
            record_name (str): the name of the record
            record_type (str): the type of record (A, AAAA, CNAME, etc)
            value (str): a single value to insert into the record
        """
        zone = self.route53.get_zone(hosted_zone_name)

        record = Record(record_name, record_type, ttl=60)
        record.add_value(value)

        logging.info("Setting Record %s of type %s to %s", record_name,
                     record_type, value)

        changes = self.route53.get_all_rrsets(zone.id)
        changes.add_change_record('UPSERT', record)

        changes.commit()
Ejemplo n.º 5
0
def group_values(lines):
    records = []
    for _, records in itertools.groupby(lines, lambda row: row[0:2]):
        for __, by_value in itertools.groupby(records, lambda row: row[-3:]):
            recs = list(by_value) # consume the iterator so we can grab positionally
            first = recs[0]

            record = Record()
            record.name = first[0]
            record.type = first[1]
            if first[2].startswith('ALIAS'):
                _, alias_hosted_zone_id, alias_dns_name = first[2].split(':')
                record.alias_hosted_zone_id = alias_hosted_zone_id
                record.alias_dns_name = alias_dns_name
            else:
                record.resource_records = [r[2] for r in recs]
                record.ttl = first[3]
            record.region = first[4] or None
            record.weight = first[5] or None
            record.identifier = first[6] or None

            yield record
Ejemplo n.º 6
0
def _create_mock_zone_and_records(route53):
    route53.create_hosted_zone(TEST_DOMAIN)
    route53.create_hosted_zone(TEST_DOMAIN2)
    example_com = route53.get_zone(TEST_DOMAIN)
    test_com = route53.get_zone(TEST_DOMAIN2)

    record_sets = route53.get_all_rrsets(example_com.id)

    record = Record(TEST_RECORD_NAME, TEST_RECORD_TYPE)
    record.add_value(TEST_RECORD_VALUE)

    record_sets.add_change_record('CREATE', record)
    record_sets.commit()

    record_sets = route53.get_all_rrsets(test_com.id)

    record = Record(TEST_RECORD_NAME2, TEST_RECORD_TYPE)
    record.add_value(TEST_RECORD_VALUE)

    record_sets.add_change_record('CREATE', record)
    record_sets.commit()
Ejemplo n.º 7
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
        command                      = dict(choices=['get', 'create', 'delete'], required=True),
        zone                         = dict(required=True),
        hosted_zone_id               = dict(required=False, default=None),
        record                       = dict(required=True),
        ttl                          = dict(required=False, type='int', default=3600),
        type                         = dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'SOA'], required=True),
        alias                        = dict(required=False, type='bool'),
        alias_hosted_zone_id         = dict(required=False),
        alias_evaluate_target_health = dict(required=False, type='bool', default=False),
        value                        = dict(required=False),
        overwrite                    = dict(required=False, type='bool'),
        retry_interval               = dict(required=False, default=500),
        private_zone                 = dict(required=False, type='bool', default=False),
        identifier                   = dict(required=False, default=None),
        weight                       = dict(required=False, type='int'),
        region                       = dict(required=False),
        health_check                 = dict(required=False),
        failover                     = dict(required=False,choices=['PRIMARY','SECONDARY']),
        vpc_id                       = dict(required=False),
        wait                         = dict(required=False, type='bool', default=False),
        wait_timeout                 = dict(required=False, type='int', default=300),
    )
    )
    module = AnsibleModule(argument_spec=argument_spec)

    if not HAS_BOTO:
        module.fail_json(msg='boto required for this module')

    if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION):
        module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION))

    command_in                      = module.params.get('command')
    zone_in                         = module.params.get('zone').lower()
    hosted_zone_id_in               = module.params.get('hosted_zone_id')
    ttl_in                          = module.params.get('ttl')
    record_in                       = module.params.get('record').lower()
    type_in                         = module.params.get('type')
    value_in                        = module.params.get('value')
    alias_in                        = module.params.get('alias')
    alias_hosted_zone_id_in         = module.params.get('alias_hosted_zone_id')
    alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health')
    retry_interval_in               = module.params.get('retry_interval')
    private_zone_in                 = module.params.get('private_zone')
    identifier_in                   = module.params.get('identifier')
    weight_in                       = module.params.get('weight')
    region_in                       = module.params.get('region')
    health_check_in                 = module.params.get('health_check')
    failover_in                     = module.params.get('failover')
    vpc_id_in                       = module.params.get('vpc_id')
    wait_in                         = module.params.get('wait')
    wait_timeout_in                 = module.params.get('wait_timeout')

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)

    value_list = ()

    if isinstance(value_in, str):
        if value_in:
            value_list = sorted([s.strip() for s in value_in.split(',')])
    elif isinstance(value_in, list):
        value_list = sorted(value_in)

    if zone_in[-1:] != '.':
        zone_in += "."

    if record_in[-1:] != '.':
        record_in += "."

    if command_in == 'create' or command_in == 'delete':
        if not value_in:
            module.fail_json(msg = "parameter 'value' required for create/delete")
        elif alias_in:
            if len(value_list) != 1:
                module.fail_json(msg = "parameter 'value' must contain a single dns name for alias create/delete")
            elif not alias_hosted_zone_id_in:
                module.fail_json(msg = "parameter 'alias_hosted_zone_id' required for alias create/delete")
        elif ( weight_in is not None or region_in is not None or failover_in is not None ) and identifier_in is None:
            module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier")

    if command_in == 'create':
        if ( weight_in is not None or region_in is not None or failover_in is not None ) and identifier_in is None:
          module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier")
        elif ( weight_in is None and region_in is None and failover_in is None ) and identifier_in is not None:
          module.fail_json(msg= "You have specified identifier which makes sense only if you specify one of: weight, region or failover.")



    if vpc_id_in and not private_zone_in:
        module.fail_json(msg="parameter 'private_zone' must be true when specifying parameter"
            " 'vpc_id'")


    # connect to the route53 endpoint
    try:
        conn = Route53Connection(**aws_connect_kwargs)
    except boto.exception.BotoServerError as e:
        module.fail_json(msg = e.error_message)

    # Find the named zone ID
    zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone is None:
        errmsg = "Zone %s does not exist in Route53" % zone_in
        module.fail_json(msg = errmsg)

    record = {}

    found_record = False
    wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
        identifier=identifier_in, weight=weight_in, region=region_in,
        health_check=health_check_in, failover=failover_in)
    for v in value_list:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in)
        else:
            wanted_rset.add_value(v)

    sets = conn.get_all_rrsets(zone.id, name=record_in, type=type_in, identifier=identifier_in)
    for rset in sets:
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
        # tripping of things like * and @.
        decoded_name = rset.name.replace(r'\052', '*')
        decoded_name = decoded_name.replace(r'\100', '@')
        #Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
        rset.name = decoded_name

        if identifier_in is not None:
            identifier_in = str(identifier_in)

        if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
            found_record = True
            record['zone'] = zone_in
            record['type'] = rset.type
            record['record'] = decoded_name
            record['ttl'] = rset.ttl
            record['value'] = ','.join(sorted(rset.resource_records))
            record['values'] = sorted(rset.resource_records)
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            record['identifier'] = rset.identifier
            record['weight'] = rset.weight
            record['region'] = rset.region
            record['failover'] = rset.failover
            record['health_check'] = rset.health_check
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            if rset.alias_dns_name:
              record['alias'] = True
              record['value'] = rset.alias_dns_name
              record['values'] = [rset.alias_dns_name]
              record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id
              record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health
            else:
              record['alias'] = False
              record['value'] = ','.join(sorted(rset.resource_records))
              record['values'] = sorted(rset.resource_records)
            if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml():
                module.exit_json(changed=False)
            break

    if command_in == 'get':
        if type_in == 'NS':
            ns = record['values']
        else:
            # Retrieve name servers associated to the zone.
            ns = conn.get_zone(zone_in).get_nameservers()

        module.exit_json(changed=False, set=record, nameservers=ns)

    if command_in == 'delete' and not found_record:
        module.exit_json(changed=False)

    changes = ResourceRecordSets(conn, zone.id)

    if command_in == 'create' or command_in == 'delete':
        if command_in == 'create' and found_record:
            if not module.params['overwrite']:
                module.fail_json(msg = "Record already exists with different value. Set 'overwrite' to replace it")
            command = 'UPSERT'
        else:
            command = command_in.upper()
        changes.add_change_record(command, wanted_rset)

    try:
        result = invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in)
    except boto.route53.exception.DNSServerError as e:
        txt = e.body.split("<Message>")[1]
        txt = txt.split("</Message>")[0]
        if "but it already exists" in txt:
                module.exit_json(changed=False)
        else:
                module.fail_json(msg = txt)
    except TimeoutError:
        module.fail_json(msg='Timeout waiting for changes to replicate')

    module.exit_json(changed=True)
Ejemplo n.º 8
0
    # Find the named zone ID
    zone = get_zone_by_name(conn, module, zone_in, private_zone_in)

    # Verify that the requested zone is already defined in Route53
    if zone is None:
        errmsg = "Zone %s does not exist in Route53" % zone_in
        module.fail_json(msg=errmsg)

    record = {}

    found_record = False
    wanted_rset = Record(name=record_in,
                         type=type_in,
                         ttl=ttl_in,
                         identifier=identifier_in,
                         weight=weight_in,
                         region=region_in,
                         health_check=health_check_in,
                         failover=failover_in)
    for v in value_list:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v)
        else:
            wanted_rset.add_value(v)

    sets = conn.get_all_rrsets(zone.id,
                               name=record_in,
                               type=type_in,
                               identifier=identifier_in)
    for rset in sets:
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
Ejemplo n.º 9
0
def group_values(lines):
    records = []
    for _, records in itertools.groupby(lines, lambda row: row[0:2]):
        for __, by_value in itertools.groupby(records, lambda row: row[-3:]):
            recs = list(
                by_value)  # consume the iterator so we can grab positionally
            first = recs[0]

            record = Record()
            record.name = first[0]
            record.type = first[1]
            if first[2].startswith('ALIAS'):
                _, alias_hosted_zone_id, alias_dns_name = first[2].split(':')
                record.alias_hosted_zone_id = alias_hosted_zone_id
                record.alias_dns_name = alias_dns_name
            else:
                record.resource_records = [r[2] for r in recs]
                record.ttl = first[3]
            record.region = first[4] or None
            record.weight = first[5] or None
            record.identifier = first[6] or None
            record.failover = first[7] or None
            if first[8] == 'True':
                record.alias_evaluate_target_health = True
            elif first[8] == 'False':
                record.alias_evaluate_target_health = False
            else:
                record.alias_evaluate_target_health = None

            yield record
Ejemplo n.º 10
0
    def test_record_commit(self):
        rrsets = ResourceRecordSets(self.service_connection)
        rrsets.add_change_record(
            'CREATE', Record('vanilla.example.com', 'A', 60, ['1.2.3.4']))
        rrsets.add_change_record(
            'CREATE',
            Record('alias.example.com',
                   'AAAA',
                   alias_hosted_zone_id='Z123OTHER',
                   alias_dns_name='target.other',
                   alias_evaluate_target_health=True))
        rrsets.add_change_record(
            'CREATE',
            Record('wrr.example.com',
                   'CNAME',
                   60, ['cname.target'],
                   weight=10,
                   identifier='weight-1'))
        rrsets.add_change_record(
            'CREATE',
            Record('lbr.example.com',
                   'TXT',
                   60, ['text record'],
                   region='us-west-2',
                   identifier='region-1'))
        rrsets.add_change_record(
            'CREATE',
            Record('failover.example.com',
                   'A',
                   60, ['2.2.2.2'],
                   health_check='hc-1234',
                   failover='PRIMARY',
                   identifier='primary'))

        changes_xml = rrsets.to_xml()

        # the whitespacing doesn't match exactly, so we'll pretty print and drop all new lines
        # not the best, but
        actual_xml = re.sub(
            r"\s*[\r\n]+", "\n",
            xml.dom.minidom.parseString(changes_xml).toprettyxml())
        expected_xml = re.sub(
            r"\s*[\r\n]+", "\n",
            xml.dom.minidom.parseString(b"""
<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
    <ChangeBatch>
        <Comment>None</Comment>
        <Changes>
            <Change>
                <Action>CREATE</Action>
                <ResourceRecordSet>
                    <Name>vanilla.example.com</Name>
                    <Type>A</Type>
                    <TTL>60</TTL>
                    <ResourceRecords>
                        <ResourceRecord>
                            <Value>1.2.3.4</Value>
                        </ResourceRecord>
                    </ResourceRecords>
                </ResourceRecordSet>
            </Change>
            <Change>
                <Action>CREATE</Action>
                <ResourceRecordSet>
                    <Name>alias.example.com</Name>
                    <Type>AAAA</Type>
                    <AliasTarget>
                        <HostedZoneId>Z123OTHER</HostedZoneId>
                        <DNSName>target.other</DNSName>
                        <EvaluateTargetHealth>true</EvaluateTargetHealth>
                    </AliasTarget>
                </ResourceRecordSet>
            </Change>
            <Change>
                <Action>CREATE</Action>
                <ResourceRecordSet>
                    <Name>wrr.example.com</Name>
                    <Type>CNAME</Type>
                    <SetIdentifier>weight-1</SetIdentifier>
                    <Weight>10</Weight>
                    <TTL>60</TTL>
                    <ResourceRecords>
                        <ResourceRecord>
                            <Value>cname.target</Value>
                        </ResourceRecord>
                    </ResourceRecords>
                </ResourceRecordSet>
            </Change>
            <Change>
                <Action>CREATE</Action>
                <ResourceRecordSet>
                    <Name>lbr.example.com</Name>
                    <Type>TXT</Type>
                    <SetIdentifier>region-1</SetIdentifier>
                    <Region>us-west-2</Region>
                    <TTL>60</TTL>
                    <ResourceRecords>
                        <ResourceRecord>
                            <Value>text record</Value>
                        </ResourceRecord>
                    </ResourceRecords>
                </ResourceRecordSet>
            </Change>
            <Change>
                <Action>CREATE</Action>
                <ResourceRecordSet>
                    <Name>failover.example.com</Name>
                    <Type>A</Type>
                    <SetIdentifier>primary</SetIdentifier>
                    <Failover>PRIMARY</Failover>
                    <TTL>60</TTL>
                    <ResourceRecords>
                        <ResourceRecord>
                            <Value>2.2.2.2</Value>
                        </ResourceRecord>
                    </ResourceRecords>
                    <HealthCheckId>hc-1234</HealthCheckId>
                </ResourceRecordSet>
            </Change>
        </Changes>
    </ChangeBatch>
</ChangeResourceRecordSetsRequest>
        """).toprettyxml())

        # Note: the alias XML should not include the TTL, even if it's specified in the object model
        self.assertEqual(actual_xml, expected_xml)
Ejemplo n.º 11
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(
        dict(
            state=dict(
                type='str',
                required=True,
                choices=['absent', 'create', 'delete', 'get', 'present'],
                aliases=['command']),
            zone=dict(type='str'),
            hosted_zone_id=dict(type='str'),
            record=dict(type='str', required=True),
            ttl=dict(type='int', default=3600),
            type=dict(type='str',
                      required=True,
                      choices=[
                          'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR',
                          'SOA', 'SPF', 'SRV', 'TXT'
                      ]),
            alias=dict(type='bool'),
            alias_hosted_zone_id=dict(type='str'),
            alias_evaluate_target_health=dict(type='bool', default=False),
            value=dict(type='list'),
            overwrite=dict(type='bool'),
            retry_interval=dict(type='int', default=500),
            private_zone=dict(type='bool', default=False),
            identifier=dict(type='str'),
            weight=dict(type='int'),
            region=dict(type='str'),
            health_check=dict(type='str'),
            failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']),
            vpc_id=dict(type='str'),
            wait=dict(type='bool', default=False),
            wait_timeout=dict(type='int', default=300),
        ))

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True,
        required_one_of=[['zone', 'hosted_zone_id']],
        # If alias is True then you must specify alias_hosted_zone as well
        required_together=[['alias', 'alias_hosted_zone_id']],
        # state=present, absent, create, delete THEN value is required
        required_if=(
            ('state', 'present', ['value']),
            ('state', 'create', ['value']),
            ('state', 'absent', ['value']),
            ('state', 'delete', ['value']),
        ),
        # failover, region and weight are mutually exclusive
        mutually_exclusive=[('failover', 'region', 'weight')],
        # failover, region and weight require identifier
        required_by=dict(
            failover=('identifier', ),
            region=('identifier', ),
            weight=('identifier', ),
        ),
    )

    if not HAS_BOTO:
        module.fail_json(msg='boto required for this module')

    if distutils.version.StrictVersion(
            boto.__version__) < distutils.version.StrictVersion(
                MINIMUM_BOTO_VERSION):
        module.fail_json(
            msg='Found boto in version %s, but >= %s is required' %
            (boto.__version__, MINIMUM_BOTO_VERSION))

    if module.params['state'] in ('present', 'create'):
        command_in = 'create'
    elif module.params['state'] in ('absent', 'delete'):
        command_in = 'delete'
    elif module.params['state'] == 'get':
        command_in = 'get'

    zone_in = (module.params.get('zone') or '').lower()
    hosted_zone_id_in = module.params.get('hosted_zone_id')
    ttl_in = module.params.get('ttl')
    record_in = module.params.get('record').lower()
    type_in = module.params.get('type')
    value_in = module.params.get('value') or []
    alias_in = module.params.get('alias')
    alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id')
    alias_evaluate_target_health_in = module.params.get(
        'alias_evaluate_target_health')
    retry_interval_in = module.params.get('retry_interval')

    if module.params['vpc_id'] is not None:
        private_zone_in = True
    else:
        private_zone_in = module.params.get('private_zone')

    identifier_in = module.params.get('identifier')
    weight_in = module.params.get('weight')
    region_in = module.params.get('region')
    health_check_in = module.params.get('health_check')
    failover_in = module.params.get('failover')
    vpc_id_in = module.params.get('vpc_id')
    wait_in = module.params.get('wait')
    wait_timeout_in = module.params.get('wait_timeout')

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)

    if zone_in[-1:] != '.':
        zone_in += "."

    if record_in[-1:] != '.':
        record_in += "."

    if command_in == 'create' or command_in == 'delete':
        if alias_in and len(value_in) != 1:
            module.fail_json(
                msg=
                "parameter 'value' must contain a single dns name for alias records"
            )
        if (weight_in is None and region_in is None
                and failover_in is None) and identifier_in is not None:
            module.fail_json(
                msg=
                "You have specified identifier which makes sense only if you specify one of: weight, region or failover."
            )

    # connect to the route53 endpoint
    try:
        conn = Route53Connection(**aws_connect_kwargs)
    except boto.exception.BotoServerError as e:
        module.fail_json(msg=e.error_message)

    # Find the named zone ID
    zone_id = hosted_zone_id_in or get_zone_id_by_name(
        conn, module, zone_in, private_zone_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone_id is None:
        errmsg = "Zone %s does not exist in Route53" % (zone_in
                                                        or hosted_zone_id_in)
        module.fail_json(msg=errmsg)

    record = {}

    found_record = False
    wanted_rset = Record(name=record_in,
                         type=type_in,
                         ttl=ttl_in,
                         identifier=identifier_in,
                         weight=weight_in,
                         region=region_in,
                         health_check=health_check_in,
                         failover=failover_in)
    for v in value_in:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v,
                                  alias_evaluate_target_health_in)
        else:
            wanted_rset.add_value(v)

    need_to_sort_records = (type_in == 'CAA')

    # Sort records for wanted_rset if necessary (keep original list)
    unsorted_records = wanted_rset.resource_records
    if need_to_sort_records:
        wanted_rset.resource_records = sorted(unsorted_records)

    sets = invoke_with_throttling_retries(conn.get_all_rrsets,
                                          zone_id,
                                          name=record_in,
                                          type=type_in,
                                          identifier=identifier_in)
    sets_iter = iter(sets)
    while True:
        try:
            rset = invoke_with_throttling_retries(next, sets_iter)
        except StopIteration:
            break
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
        # tripping of things like * and @.
        decoded_name = rset.name.replace(r'\052', '*')
        decoded_name = decoded_name.replace(r'\100', '@')
        # Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
        rset.name = decoded_name

        if identifier_in is not None:
            identifier_in = str(identifier_in)

        if rset.type == type_in and decoded_name.lower() == record_in.lower(
        ) and rset.identifier == identifier_in:
            if need_to_sort_records:
                # Sort records
                rset.resource_records = sorted(rset.resource_records)
            found_record = True
            record['zone'] = zone_in
            record['type'] = rset.type
            record['record'] = decoded_name
            record['ttl'] = rset.ttl
            record['identifier'] = rset.identifier
            record['weight'] = rset.weight
            record['region'] = rset.region
            record['failover'] = rset.failover
            record['health_check'] = rset.health_check
            record['hosted_zone_id'] = zone_id
            if rset.alias_dns_name:
                record['alias'] = True
                record['value'] = rset.alias_dns_name
                record['values'] = [rset.alias_dns_name]
                record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id
                record[
                    'alias_evaluate_target_health'] = rset.alias_evaluate_target_health
            else:
                record['alias'] = False
                record['value'] = ','.join(sorted(rset.resource_records))
                record['values'] = sorted(rset.resource_records)
            if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml(
            ):
                module.exit_json(changed=False)

        # We need to look only at the first rrset returned by the above call,
        # so break here. The returned elements begin with the one matching our
        # requested name, type, and identifier, if such an element exists,
        # followed by all others that come after it in alphabetical order.
        # Therefore, if the first set does not match, no subsequent set will
        # match either.
        break

    if command_in == 'get':
        if type_in == 'NS':
            ns = record.get('values', [])
        else:
            # Retrieve name servers associated to the zone.
            z = invoke_with_throttling_retries(conn.get_zone, zone_in)
            ns = invoke_with_throttling_retries(z.get_nameservers)

        module.exit_json(changed=False, set=record, nameservers=ns)

    if command_in == 'delete' and not found_record:
        module.exit_json(changed=False)

    changes = ResourceRecordSets(conn, zone_id)

    if command_in == 'create' or command_in == 'delete':
        if command_in == 'create' and found_record:
            if not module.params['overwrite']:
                module.fail_json(
                    msg=
                    "Record already exists with different value. Set 'overwrite' to replace it"
                )
            command = 'UPSERT'
        else:
            command = command_in.upper()
        # Restore original order of records
        wanted_rset.resource_records = unsorted_records
        changes.add_change_record(command, wanted_rset)

    if not module.check_mode:
        try:
            invoke_with_throttling_retries(commit, changes, retry_interval_in,
                                           wait_in, wait_timeout_in)
        except boto.route53.exception.DNSServerError as e:
            txt = e.body.split("<Message>")[1]
            txt = txt.split("</Message>")[0]
            if "but it already exists" in txt:
                module.exit_json(changed=False)
            else:
                module.fail_json(msg=txt)
        except TimeoutError:
            module.fail_json(msg='Timeout waiting for changes to replicate')

    module.exit_json(changed=True)
Ejemplo n.º 12
0
    def create_dns_record(self,
                          fqdn,
                          record_type,
                          zone_id,
                          records,
                          ttl=60,
                          routing_policy='simple',
                          weight=None,
                          identifier=None,
                          region=None,
                          health_check_id=None,
                          failover="primary"):
        botoconn = self.__get_boto_conn()
        if routing_policy == 'simple':
            weight = None
            identifier = None
            region = None
            health_check_id = None
            failover = None
        elif routing_policy == 'weighted':
            region = None
            failover = None
            if not weight:
                raise AttributeError(
                    "weight must be provided for weighted routing policy")
            if not identifier:
                raise AttributeError(
                    "identifier must be provided for weighted routing policy")
        elif routing_policy == 'latency':
            weight = None
            failover = None
            if not region:
                raise AttributeError(
                    "region must be provided for latency routing policy")
            if not identifier:
                raise AttributeError(
                    "identifier must be provided for latency routing policy")
        elif routing_policy == 'failover':
            weight = None
            region = None
            if not failover:
                raise AttributeError(
                    "failover must be provided for failover routing policy")
            if not identifier:
                raise AttributeError(
                    "identifier must be provided for failover routing policy")

        health_check = None
        if health_check_id:
            self.db.execute(
                "select healthcheck_id from route53_healthchecks where id=%s",
                (health_check_id, ))
            row = self.db.fetchone()
            if not row:
                raise ResourceNotFound(
                    "Could not find information on health check {0}".format(
                        health_check_id))
            health_check = row[0]

        zones = botoconn.get_zones()
        zone = None
        # unfortunately boto's get_zone only takes a zone name which is not necessarily unique :(
        for z in zones:
            if z.id == zone_id:
                zone = z
                break
        if not zone:
            raise ResourceNotFound("Zone ID {0} not found".format(zone_id))

        rrset = zone.get_records()
        record_type = record_type.upper()
        if failover is not None:
            failover = failover.upper()
        rec = Record(name=fqdn,
                     type=record_type,
                     ttl=ttl,
                     resource_records=records,
                     identifier=identifier,
                     weight=weight,
                     region=region,
                     health_check=health_check,
                     failover=failover)
        rrset.add_change_record('CREATE', rec)
        response = rrset.commit()

        if 'ChangeResourceRecordSetsResponse' in response:
            name = rec.name
            if name[len(name) - 1] == '.':
                name = name[0:len(name) - 1]
            ident = rec.identifier
            if not rec.identifier:
                ident = ""
            self.db.execute(
                "insert into route53_records set zone_id=%s, name=%s, type=%s, identifier=%s, resource_records=%s, ttl=%s, alias_hosted_zone_id=%s, "
                "alias_dns_name=%s, weight=%s, region=%s, healthcheck_id=%s on duplicate key update resource_records=%s, ttl=%s, alias_hosted_zone_id=%s, "
                "alias_dns_name=%s, weight=%s, region=%s, healthcheck_id=%s",
                (zone_id, name, rec.type, ident, "\n".join(
                    rec.resource_records), rec.ttl, rec.alias_hosted_zone_id,
                 rec.alias_dns_name, rec.weight, rec.region, rec.health_check,
                 "\n".join(
                     rec.resource_records), rec.ttl, rec.alias_hosted_zone_id,
                 rec.alias_dns_name, rec.weight, rec.region, rec.health_check))
            self.dbconn.commit()
            self.logger.info("Created new dns entry for {0} -> {1}".format(
                fqdn, " \\n ".join(records)))
            self.db.execute(
                "update route53_zones z set record_sets = (select count(*) from route53_records where zone_id=z.zone_id)"
            )
            self.dbconn.commit()
Ejemplo n.º 13
0
    except boto.exception.BotoServerError, e:
        module.fail_json(msg = e.error_message)

    # Find the named zone ID
    zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone is None:
        errmsg = "Zone %s does not exist in Route53" % zone_in
        module.fail_json(msg = errmsg)

    record = {}
    
    found_record = False
    wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
        identifier=identifier_in, weight=weight_in, region=region_in,
        health_check=health_check_in, failover=failover_in)
    for v in value_list:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v)
        else:
            wanted_rset.add_value(v)

    sets = conn.get_all_rrsets(zone.id, name=record_in, type=type_in, identifier=identifier_in)
    for rset in sets:
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
        # tripping of things like * and @.
        decoded_name = rset.name.replace(r'\052', '*')
        decoded_name = decoded_name.replace(r'\100', '@')
        #Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
	rset.name = decoded_name
Ejemplo n.º 14
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
            command                      = dict(choices=['get', 'create', 'delete'], required=True),
            zone                         = dict(required=True),
            hosted_zone_id               = dict(required=False, default=None),
            record                       = dict(required=True),
            ttl                          = dict(required=False, type='int', default=3600),
            type                         = dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'SOA'], required=True),
            alias                        = dict(required=False, type='bool'),
            alias_hosted_zone_id         = dict(required=False),
            alias_evaluate_target_health = dict(required=False, type='bool', default=False),
            value                        = dict(required=False),
            overwrite                    = dict(required=False, type='bool'),
            retry_interval               = dict(required=False, default=500),
            private_zone                 = dict(required=False, type='bool', default=False),
            identifier                   = dict(required=False, default=None),
            weight                       = dict(required=False, type='int'),
            region                       = dict(required=False),
            health_check                 = dict(required=False),
            failover                     = dict(required=False,choices=['PRIMARY','SECONDARY']),
            vpc_id                       = dict(required=False),
            wait                         = dict(required=False, type='bool', default=False),
            wait_timeout                 = dict(required=False, type='int', default=300),
        )
    )
    module = AnsibleModule(argument_spec=argument_spec)

    if not HAS_BOTO:
        module.fail_json(msg='boto required for this module')

    if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION):
        module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION))

    command_in                      = module.params.get('command')
    zone_in                         = module.params.get('zone').lower()
    hosted_zone_id_in               = module.params.get('hosted_zone_id')
    ttl_in                          = module.params.get('ttl')
    record_in                       = module.params.get('record').lower()
    type_in                         = module.params.get('type')
    value_in                        = module.params.get('value')
    alias_in                        = module.params.get('alias')
    alias_hosted_zone_id_in         = module.params.get('alias_hosted_zone_id')
    alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health')
    retry_interval_in               = module.params.get('retry_interval')
    private_zone_in                 = module.params.get('private_zone')
    identifier_in                   = module.params.get('identifier')
    weight_in                       = module.params.get('weight')
    region_in                       = module.params.get('region')
    health_check_in                 = module.params.get('health_check')
    failover_in                     = module.params.get('failover')
    vpc_id_in                       = module.params.get('vpc_id')
    wait_in                         = module.params.get('wait')
    wait_timeout_in                 = module.params.get('wait_timeout')

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)

    value_list = ()

    if isinstance(value_in, str):
        if value_in:
            value_list = sorted([s.strip() for s in value_in.split(',')])
    elif isinstance(value_in, list):
        value_list = sorted(value_in)

    if zone_in[-1:] != '.':
        zone_in += "."

    if record_in[-1:] != '.':
        record_in += "."

    if command_in == 'create' or command_in == 'delete':
        if not value_in:
            module.fail_json(msg = "parameter 'value' required for create/delete")
        elif alias_in:
            if len(value_list) != 1:
                module.fail_json(msg = "parameter 'value' must contain a single dns name for alias create/delete")
            elif not alias_hosted_zone_id_in:
                module.fail_json(msg = "parameter 'alias_hosted_zone_id' required for alias create/delete")
        elif ( weight_in!=None or region_in!=None or failover_in!=None ) and identifier_in==None:
            module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier")

    if command_in == 'create':
        if ( weight_in!=None or region_in!=None or failover_in!=None ) and identifier_in==None:
          module.fail_json(msg= "If you specify failover, region or weight you must also specify identifier")
        elif  ( weight_in==None and region_in==None and failover_in==None ) and identifier_in!=None:
          module.fail_json(msg= "You have specified identifier which makes sense only if you specify one of: weight, region or failover.")



    if vpc_id_in and not private_zone_in:
        module.fail_json(msg="parameter 'private_zone' must be true when specifying parameter"
            " 'vpc_id'")


    # connect to the route53 endpoint
    try:
        conn = Route53Connection(**aws_connect_kwargs)
    except boto.exception.BotoServerError as e:
        module.fail_json(msg = e.error_message)

    # Find the named zone ID
    zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone is None:
        errmsg = "Zone %s does not exist in Route53" % zone_in
        module.fail_json(msg = errmsg)

    record = {}

    found_record = False
    wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
        identifier=identifier_in, weight=weight_in, region=region_in,
        health_check=health_check_in, failover=failover_in)
    for v in value_list:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in)
        else:
            wanted_rset.add_value(v)

    sets = conn.get_all_rrsets(zone.id, name=record_in, type=type_in, identifier=identifier_in)
    for rset in sets:
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
        # tripping of things like * and @.
        decoded_name = rset.name.replace(r'\052', '*')
        decoded_name = decoded_name.replace(r'\100', '@')
        #Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
        rset.name = decoded_name

        if identifier_in is not None:
            identifier_in = str(identifier_in)

        if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
            found_record = True
            record['zone'] = zone_in
            record['type'] = rset.type
            record['record'] = decoded_name
            record['ttl'] = rset.ttl
            record['value'] = ','.join(sorted(rset.resource_records))
            record['values'] = sorted(rset.resource_records)
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            record['identifier'] = rset.identifier
            record['weight'] = rset.weight
            record['region'] = rset.region
            record['failover'] = rset.failover
            record['health_check'] = rset.health_check
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            if rset.alias_dns_name:
              record['alias'] = True
              record['value'] = rset.alias_dns_name
              record['values'] = [rset.alias_dns_name]
              record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id
              record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health
            else:
              record['alias'] = False
              record['value'] = ','.join(sorted(rset.resource_records))
              record['values'] = sorted(rset.resource_records)
            if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml():
                module.exit_json(changed=False)
            break

    if command_in == 'get':
        if type_in == 'NS':
            ns = record['values']
        else:
            # Retrieve name servers associated to the zone.
            ns = conn.get_zone(zone_in).get_nameservers()

        module.exit_json(changed=False, set=record, nameservers=ns)

    if command_in == 'delete' and not found_record:
        module.exit_json(changed=False)

    changes = ResourceRecordSets(conn, zone.id)

    if command_in == 'create' or command_in == 'delete':
        if command_in == 'create' and found_record:
            if not module.params['overwrite']:
                module.fail_json(msg = "Record already exists with different value. Set 'overwrite' to replace it")
            command = 'UPSERT'
        else:
            command = command_in.upper()
        changes.add_change_record(command, wanted_rset)

    try:
        result = invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in)
    except boto.route53.exception.DNSServerError as e:
        txt = e.body.split("<Message>")[1]
        txt = txt.split("</Message>")[0]
        if "but it already exists" in txt:
                module.exit_json(changed=False)
        else:
                module.fail_json(msg = txt)
    except TimeoutError:
        module.fail_json(msg='Timeout waiting for changes to replicate')

    module.exit_json(changed=True)
Ejemplo n.º 15
0
def main():
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
        state=dict(aliases=['command'], choices=['present', 'absent', 'get', 'create', 'delete'], required=True),
        zone=dict(required=True),
        hosted_zone_id=dict(required=False, default=None),
        record=dict(required=True),
        ttl=dict(required=False, type='int', default=3600),
        type=dict(choices=['A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'NS', 'SOA'], required=True),
        alias=dict(required=False, type='bool'),
        alias_hosted_zone_id=dict(required=False),
        alias_evaluate_target_health=dict(required=False, type='bool', default=False),
        value=dict(required=False, type='list', default=[]),
        overwrite=dict(required=False, type='bool'),
        retry_interval=dict(required=False, default=500),
        private_zone=dict(required=False, type='bool', default=False),
        identifier=dict(required=False, default=None),
        weight=dict(required=False, type='int'),
        region=dict(required=False),
        health_check=dict(required=False),
        failover=dict(required=False, choices=['PRIMARY', 'SECONDARY']),
        vpc_id=dict(required=False),
        wait=dict(required=False, type='bool', default=False),
        wait_timeout=dict(required=False, type='int', default=300),
    ))

    # state=present, absent, create, delete THEN value is required
    required_if = [('state', 'present', ['value']), ('state', 'create', ['value'])]
    required_if.extend([('state', 'absent', ['value']), ('state', 'delete', ['value'])])

    # If alias is True then you must specify alias_hosted_zone as well
    required_together = [['alias', 'alias_hosted_zone_id']]

    # failover, region, and weight are mutually exclusive
    mutually_exclusive = [('failover', 'region', 'weight')]

    module = AnsibleModule(argument_spec=argument_spec, required_together=required_together, required_if=required_if,
                           mutually_exclusive=mutually_exclusive)

    if not HAS_BOTO:
        module.fail_json(msg='boto required for this module')

    if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION):
        module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION))

    if module.params['state'] in ('present', 'create'):
        command_in = 'create'
    elif module.params['state'] in ('absent', 'delete'):
        command_in = 'delete'
    elif module.params['state'] == 'get':
        command_in = 'get'

    zone_in = module.params.get('zone').lower()
    hosted_zone_id_in = module.params.get('hosted_zone_id')
    ttl_in = module.params.get('ttl')
    record_in = module.params.get('record').lower()
    type_in = module.params.get('type')
    value_in = module.params.get('value')
    alias_in = module.params.get('alias')
    alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id')
    alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health')
    retry_interval_in = module.params.get('retry_interval')

    if module.params['vpc_id'] is not None:
        private_zone_in = True
    else:
        private_zone_in = module.params.get('private_zone')

    identifier_in = module.params.get('identifier')
    weight_in = module.params.get('weight')
    region_in = module.params.get('region')
    health_check_in = module.params.get('health_check')
    failover_in = module.params.get('failover')
    vpc_id_in = module.params.get('vpc_id')
    wait_in = module.params.get('wait')
    wait_timeout_in = module.params.get('wait_timeout')

    region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module)

    if zone_in[-1:] != '.':
        zone_in += "."

    if record_in[-1:] != '.':
        record_in += "."

    if command_in == 'create' or command_in == 'delete':
        if alias_in and len(value_in) != 1:
            module.fail_json(msg="parameter 'value' must contain a single dns name for alias records")
        if (weight_in is not None or region_in is not None or failover_in is not None) and identifier_in is None:
            module.fail_json(msg="If you specify failover, region or weight you must also specify identifier")
        if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None:
            module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.")

    # connect to the route53 endpoint
    try:
        conn = Route53Connection(**aws_connect_kwargs)
    except boto.exception.BotoServerError as e:
        module.fail_json(msg=e.error_message)

    # Find the named zone ID
    zone = get_zone_by_name(conn, module, zone_in, private_zone_in, hosted_zone_id_in, vpc_id_in)

    # Verify that the requested zone is already defined in Route53
    if zone is None:
        errmsg = "Zone %s does not exist in Route53" % zone_in
        module.fail_json(msg=errmsg)

    record = {}

    found_record = False
    wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in,
                         identifier=identifier_in, weight=weight_in,
                         region=region_in, health_check=health_check_in,
                         failover=failover_in)
    for v in value_in:
        if alias_in:
            wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in)
        else:
            wanted_rset.add_value(v)

    sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone.id, name=record_in,
                                          type=type_in, identifier=identifier_in)
    sets_iter = iter(sets)
    while True:
        try:
            rset = invoke_with_throttling_retries(next, sets_iter)
        except StopIteration:
            break
        # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round
        # tripping of things like * and @.
        decoded_name = rset.name.replace(r'\052', '*')
        decoded_name = decoded_name.replace(r'\100', '@')
        # Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
        rset.name = decoded_name

        if identifier_in is not None:
            identifier_in = str(identifier_in)

        if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
            found_record = True
            record['zone'] = zone_in
            record['type'] = rset.type
            record['record'] = decoded_name
            record['ttl'] = rset.ttl
            record['value'] = ','.join(sorted(rset.resource_records))
            record['values'] = sorted(rset.resource_records)
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            record['identifier'] = rset.identifier
            record['weight'] = rset.weight
            record['region'] = rset.region
            record['failover'] = rset.failover
            record['health_check'] = rset.health_check
            if hosted_zone_id_in:
                record['hosted_zone_id'] = hosted_zone_id_in
            if rset.alias_dns_name:
                record['alias'] = True
                record['value'] = rset.alias_dns_name
                record['values'] = [rset.alias_dns_name]
                record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id
                record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health
            else:
                record['alias'] = False
                record['value'] = ','.join(sorted(rset.resource_records))
                record['values'] = sorted(rset.resource_records)
            if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml():
                module.exit_json(changed=False)

        # We need to look only at the first rrset returned by the above call,
        # so break here. The returned elements begin with the one matching our
        # requested name, type, and identifier, if such an element exists,
        # followed by all others that come after it in alphabetical order.
        # Therefore, if the first set does not match, no subsequent set will
        # match either.
        break

    if command_in == 'get':
        if type_in == 'NS':
            ns = record.get('values', [])
        else:
            # Retrieve name servers associated to the zone.
            z = invoke_with_throttling_retries(conn.get_zone, zone_in)
            ns = invoke_with_throttling_retries(z.get_nameservers)

        module.exit_json(changed=False, set=record, nameservers=ns)

    if command_in == 'delete' and not found_record:
        module.exit_json(changed=False)

    changes = ResourceRecordSets(conn, zone.id)

    if command_in == 'create' or command_in == 'delete':
        if command_in == 'create' and found_record:
            if not module.params['overwrite']:
                module.fail_json(msg="Record already exists with different value. Set 'overwrite' to replace it")
            command = 'UPSERT'
        else:
            command = command_in.upper()
        changes.add_change_record(command, wanted_rset)

    try:
        result = invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in)
    except boto.route53.exception.DNSServerError as e:
        txt = e.body.split("<Message>")[1]
        txt = txt.split("</Message>")[0]
        if "but it already exists" in txt:
            module.exit_json(changed=False)
        else:
            module.fail_json(msg=txt)
    except TimeoutError:
        module.fail_json(msg='Timeout waiting for changes to replicate')

    module.exit_json(changed=True)