1""" Record namespace commands. """
2
3import os
4import click
5import json
6
7from gandi.cli.core.cli import cli
8from gandi.cli.core.utils import (
9    output_generic,
10)
11from gandi.cli.core.params import pass_gandi, StringConstraint
12
13
14@cli.group(name='record')
15@pass_gandi
16def record(gandi):
17    """Commands related to DNS zone records (xmlrpc)."""
18
19
20@record.command()
21@click.option('--zone-id', '-z', default=None, type=click.INT,
22              help='Zone ID to use, if not set default zone will be used.')
23@click.option('--output', '-o', is_flag=True,
24              help='Write the records into a file.')
25@click.option('--format', '-f', type=click.Choice(['text', 'json']),
26              help='Choose the output format', required=False)
27@click.option('--limit', help='Limit number of results.', default=100,
28              show_default=True)
29@click.argument('domain', required=True)
30@pass_gandi
31def list(gandi, domain, zone_id, output, format, limit):
32    """List DNS zone records for a domain."""
33    options = {
34        'items_per_page': limit,
35    }
36    output_keys = ['name', 'type', 'value', 'ttl']
37
38    if not zone_id:
39        result = gandi.domain.info(domain)
40        zone_id = result['zone_id']
41
42    if not zone_id:
43        gandi.echo('No zone records found, domain %s doesn\'t seems to be '
44                   'managed at Gandi.' % domain)
45        return
46
47    records = gandi.record.list(zone_id, options)
48
49    if not output and not format:
50        for num, rec in enumerate(records):
51            if num:
52                gandi.separator_line()
53            output_generic(gandi, rec, output_keys, justify=12)
54    elif output:
55        zone_filename = domain + "_" + str(zone_id)
56        if os.path.isfile(zone_filename):
57            open(zone_filename, 'w').close()
58        for record in records:
59            format_record = ('%s %s IN %s %s' %
60                             (record['name'], record['ttl'],
61                              record['type'], record['value']))
62            with open(zone_filename, 'ab') as zone_file:
63                zone_file.write(format_record + '\n')
64        gandi.echo('Your zone file have been writen in %s' % zone_filename)
65    elif format:
66        if format == 'text':
67            for record in records:
68                format_record = ('%s %s IN %s %s' %
69                                 (record['name'], record['ttl'],
70                                  record['type'], record['value']))
71                gandi.echo(format_record)
72        if format == 'json':
73            format_record = json.dumps(records, sort_keys=True,
74                                       indent=4, separators=(',', ': '))
75            gandi.echo(format_record)
76
77    return records
78
79
80@record.command()
81@click.option('--zone-id', '-z', default=None, type=click.INT,
82              help='Zone ID to use, if not set, default zone will be used.')
83@click.option('--name', default=None, required=True,
84              help='Relative name, may contain leading wildcard. '
85                   '`@` for empty name')
86@click.option('--type', default=None, required=True,
87              type=click.Choice(['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT',
88                                 'WKS', 'SRV', 'LOC', 'SPF']),
89              help='DNS record type')
90@click.option('--value', default=None, required=True,
91              type=StringConstraint(minlen=1, maxlen=1024),
92              help='Value for record. Semantics depends on the record type.'
93                   'Currently limited to 1024 ASCII characters.'
94                   'In case of TXT, each part between quotes is limited to 255'
95                   ' characters')
96@click.option('--ttl', default=None, required=False,
97              type=click.IntRange(min=300, max=2592000),
98              help='Time to live, in seconds, between 5 minutes and 30 days')
99@click.argument('domain', required=True)
100@pass_gandi
101def create(gandi, domain, zone_id, name, type, value, ttl):
102    """Create new DNS zone record entry for a domain."""
103
104    if not zone_id:
105        result = gandi.domain.info(domain)
106        zone_id = result['zone_id']
107
108    if not zone_id:
109        gandi.echo('No zone records found, domain %s doesn\'t seems to be '
110                   'managed at Gandi.' % domain)
111        return
112
113    record = {'type': type, 'name': name, 'value': value}
114    if ttl:
115        record['ttl'] = ttl
116
117    result = gandi.record.create(zone_id, record)
118    return result
119
120
121@record.command()
122@click.option('--zone-id', '-z', default=None, type=click.INT,
123              help='Zone ID to use, if not set, default zone will be used.')
124@click.option('--name', default=None,
125              help='Relative name of the record to delete.')
126@click.option('--type', default=None,
127              type=click.Choice(['A', 'AAAA', 'CNAME', 'MX', 'NS', 'TXT',
128                                 'WKS', 'SRV', 'LOC', 'SPF']),
129              help='DNS record type')
130@click.option('--value', default=None,
131              type=StringConstraint(minlen=1, maxlen=1024),
132              help='Value for record. Semantics depends on the record type.'
133                   'Currently limited to 1024 ASCII characters.'
134                   'In case of TXT, each part between quotes is limited to 255'
135                   ' characters')
136@click.argument('domain', required=True)
137@pass_gandi
138def delete(gandi, domain, zone_id, name, type, value):
139    """Delete a record entry for a domain"""
140    if not zone_id:
141        result = gandi.domain.info(domain)
142        zone_id = result['zone_id']
143
144    if not zone_id:
145        gandi.echo('No zone records found, domain %s doesn\'t seems to be '
146                   'managed at Gandi.' % domain)
147        return
148    if not name and not type and not value:
149        proceed = click.confirm('This command without parameters --type, '
150                                '--name or --value will remove all records'
151                                ' in this zone file. Are you sur to '
152                                'perform this action ?')
153        if not proceed:
154            return
155    record = {'name': name, 'type': type, 'value': value}
156    result = gandi.record.delete(zone_id, record)
157    return result
158
159
160@record.command()
161@click.option('--zone-id', '-z', default=None, type=click.INT,
162              help='Zone ID to use, if not set, default zone will be used.')
163@click.option('--file', '-f', type=click.File('r'),
164              required=False, help='Filename of the zone file.')
165@click.option('--record', '-r', default=None, required=False,
166              help="'name TTL IN TYPE [A, AAAA, MX, TXT, SPF] value' \n"
167              "Note that you can specify only a name, but in case of "
168              "multiple entries with the same name, only the first one will "
169              "be updated")
170@click.option('--new-record', default=None, required=False,
171              help="'name TTL IN TYPE [A, AAAA, MX, TXT, SPF] value'")
172@click.argument('domain', required=True)
173@pass_gandi
174def update(gandi, domain, zone_id, file, record, new_record):
175    """Update records entries for a domain.
176
177    You can update an individual record using --record and --new-record
178    parameters
179
180    Or you can use a plaintext file to update all records of a DNS zone at
181    once with --file parameter.
182    """
183    if not zone_id:
184        result = gandi.domain.info(domain)
185        zone_id = result['zone_id']
186
187    if not zone_id:
188        gandi.echo('No zone records found, domain %s doesn\'t seems to be'
189                   ' managed at Gandi.' % domain)
190        return
191    if file:
192        records = file.read()
193        result = gandi.record.zone_update(zone_id, records)
194        return result
195    elif record and new_record:
196        result = gandi.record.update(zone_id, record, new_record)
197        return result
198    else:
199        gandi.echo('You must indicate a zone file or a record.'
200                   ' Use `gandi record update --help` for more information')
201