diff options
-rw-r--r-- | API.txt | 145 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | ipalib/plugins/dns.py | 343 | ||||
-rw-r--r-- | ipalib/plugins/host.py | 104 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_dns_plugin.py | 42 |
5 files changed, 389 insertions, 247 deletions
@@ -633,15 +633,17 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, None) command: dnsrecord_add -args: 2,114,3 +args: 2,116,3 arg: Str('dnszoneidnsname', cli_name='dnszone', query=True, required=True) arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, required=True) option: Int('dnsttl', attribute=True, cli_name='ttl', multivalue=False, required=False) option: StrEnum('dnsclass', attribute=True, cli_name='class', multivalue=False, required=False, values=(u'IN', u'CS', u'CH', u'HS')) option: ARecord('arecord', attribute=True, cli_name='a_rec', csv=True, multivalue=True, option_group=u'A Record', required=False) option: Str('a_part_ip_address', attribute=False, cli_name='a_ip_address', multivalue=False, option_group=u'A Record', required=False) +option: Flag('a_extra_create_reverse', attribute=False, autofill=True, cli_name='a_create_reverse', default=False, multivalue=False, option_group=u'A Record', required=False) option: AAAARecord('aaaarecord', attribute=True, cli_name='aaaa_rec', csv=True, multivalue=True, option_group=u'AAAA Record', required=False) option: Str('aaaa_part_ip_address', attribute=False, cli_name='aaaa_ip_address', multivalue=False, option_group=u'AAAA Record', required=False) +option: Flag('aaaa_extra_create_reverse', attribute=False, autofill=True, cli_name='aaaa_create_reverse', default=False, multivalue=False, option_group=u'AAAA Record', required=False) option: A6Record('a6record', attribute=True, cli_name='a6_rec', csv=True, multivalue=True, option_group=u'A6 Record', required=False) option: Str('a6_part_data', attribute=False, cli_name='a6_data', multivalue=False, option_group=u'A6 Record', required=False) option: AFSDBRecord('afsdbrecord', attribute=True, cli_name='afsdb_rec', csv=True, multivalue=True, option_group=u'AFSDB Record', required=False) @@ -810,117 +812,46 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('result', <type 'dict'>, None) output: Output('value', <type 'unicode'>, None) command: dnsrecord_find -args: 2,115,4 +args: 2,44,4 arg: Str('dnszoneidnsname', cli_name='dnszone', query=True, required=True) arg: Str('criteria?', noextrawhitespace=False) option: Str('idnsname', attribute=True, autofill=False, cli_name='name', multivalue=False, primary_key=True, query=True, required=False) option: Int('dnsttl', attribute=True, autofill=False, cli_name='ttl', multivalue=False, query=True, required=False) option: StrEnum('dnsclass', attribute=True, autofill=False, cli_name='class', multivalue=False, query=True, required=False, values=(u'IN', u'CS', u'CH', u'HS')) -option: ARecord('arecord', attribute=True, autofill=False, cli_name='a_rec', csv=True, multivalue=True, option_group=u'A Record', query=True, required=False) -option: Str('a_part_ip_address', attribute=False, autofill=False, cli_name='a_ip_address', multivalue=False, option_group=u'A Record', query=True, required=False) -option: AAAARecord('aaaarecord', attribute=True, autofill=False, cli_name='aaaa_rec', csv=True, multivalue=True, option_group=u'AAAA Record', query=True, required=False) -option: Str('aaaa_part_ip_address', attribute=False, autofill=False, cli_name='aaaa_ip_address', multivalue=False, option_group=u'AAAA Record', query=True, required=False) -option: A6Record('a6record', attribute=True, autofill=False, cli_name='a6_rec', csv=True, multivalue=True, option_group=u'A6 Record', query=True, required=False) -option: Str('a6_part_data', attribute=False, autofill=False, cli_name='a6_data', multivalue=False, option_group=u'A6 Record', query=True, required=False) -option: AFSDBRecord('afsdbrecord', attribute=True, autofill=False, cli_name='afsdb_rec', csv=True, multivalue=True, option_group=u'AFSDB Record', query=True, required=False) -option: Int('afsdb_part_subtype', attribute=False, autofill=False, cli_name='afsdb_subtype', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'AFSDB Record', query=True, required=False) -option: Str('afsdb_part_hostname', attribute=False, autofill=False, cli_name='afsdb_hostname', multivalue=False, option_group=u'AFSDB Record', query=True, required=False) -option: APLRecord('aplrecord', attribute=True, autofill=False, cli_name='apl_rec', csv=True, multivalue=True, option_group=u'APL Record', query=True, required=False) -option: CERTRecord('certrecord', attribute=True, autofill=False, cli_name='cert_rec', csv=True, multivalue=True, option_group=u'CERT Record', query=True, required=False) -option: Int('cert_part_type', attribute=False, autofill=False, cli_name='cert_type', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'CERT Record', query=True, required=False) -option: Int('cert_part_key_tag', attribute=False, autofill=False, cli_name='cert_key_tag', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'CERT Record', query=True, required=False) -option: Int('cert_part_algorithm', attribute=False, autofill=False, cli_name='cert_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'CERT Record', query=True, required=False) -option: Str('cert_part_certificate_or_crl', attribute=False, autofill=False, cli_name='cert_certificate_or_crl', multivalue=False, option_group=u'CERT Record', query=True, required=False) -option: CNAMERecord('cnamerecord', attribute=True, autofill=False, cli_name='cname_rec', csv=True, multivalue=True, option_group=u'CNAME Record', query=True, required=False) -option: Str('cname_part_hostname', attribute=False, autofill=False, cli_name='cname_hostname', multivalue=False, option_group=u'CNAME Record', query=True, required=False) -option: DHCIDRecord('dhcidrecord', attribute=True, autofill=False, cli_name='dhcid_rec', csv=True, multivalue=True, option_group=u'DHCID Record', query=True, required=False) -option: DLVRecord('dlvrecord', attribute=True, autofill=False, cli_name='dlv_rec', csv=True, multivalue=True, option_group=u'DLV Record', query=True, required=False) -option: DNAMERecord('dnamerecord', attribute=True, autofill=False, cli_name='dname_rec', csv=True, multivalue=True, option_group=u'DNAME Record', query=True, required=False) -option: Str('dname_part_target', attribute=False, autofill=False, cli_name='dname_target', multivalue=False, option_group=u'DNAME Record', query=True, required=False) -option: DNSKEYRecord('dnskeyrecord', attribute=True, autofill=False, cli_name='dnskey_rec', csv=True, multivalue=True, option_group=u'DNSKEY Record', query=True, required=False) -option: DSRecord('dsrecord', attribute=True, autofill=False, cli_name='ds_rec', csv=True, multivalue=True, option_group=u'DS Record', query=True, required=False) -option: Int('ds_part_key_tag', attribute=False, autofill=False, cli_name='ds_key_tag', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'DS Record', query=True, required=False) -option: Int('ds_part_algorithm', attribute=False, autofill=False, cli_name='ds_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'DS Record', query=True, required=False) -option: Int('ds_part_digest_type', attribute=False, autofill=False, cli_name='ds_digest_type', maxvalue=255, minvalue=0, multivalue=False, option_group=u'DS Record', query=True, required=False) -option: Str('ds_part_digest', attribute=False, autofill=False, cli_name='ds_digest', multivalue=False, option_group=u'DS Record', query=True, required=False) -option: HIPRecord('hiprecord', attribute=True, autofill=False, cli_name='hip_rec', csv=True, multivalue=True, option_group=u'HIP Record', query=True, required=False) -option: IPSECKEYRecord('ipseckeyrecord', attribute=True, autofill=False, cli_name='ipseckey_rec', csv=True, multivalue=True, option_group=u'IPSECKEY Record', query=True, required=False) -option: KEYRecord('keyrecord', attribute=True, autofill=False, cli_name='key_rec', csv=True, multivalue=True, option_group=u'KEY Record', query=True, required=False) -option: Int('key_part_flags', attribute=False, autofill=False, cli_name='key_flags', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'KEY Record', query=True, required=False) -option: Int('key_part_protocol', attribute=False, autofill=False, cli_name='key_protocol', maxvalue=255, minvalue=0, multivalue=False, option_group=u'KEY Record', query=True, required=False) -option: Int('key_part_algorithm', attribute=False, autofill=False, cli_name='key_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'KEY Record', query=True, required=False) -option: Str('key_part_public_key', attribute=False, autofill=False, cli_name='key_public_key', multivalue=False, option_group=u'KEY Record', query=True, required=False) -option: KXRecord('kxrecord', attribute=True, autofill=False, cli_name='kx_rec', csv=True, multivalue=True, option_group=u'KX Record', query=True, required=False) -option: Int('kx_part_preference', attribute=False, autofill=False, cli_name='kx_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'KX Record', query=True, required=False) -option: Str('kx_part_exchanger', attribute=False, autofill=False, cli_name='kx_exchanger', multivalue=False, option_group=u'KX Record', query=True, required=False) -option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, option_group=u'LOC Record', query=True, required=False) -option: Int('loc_part_lat_deg', attribute=False, autofill=False, cli_name='loc_lat_deg', maxvalue=90, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False) -option: Int('loc_part_lat_min', attribute=False, autofill=False, cli_name='loc_lat_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False) -option: Decimal('loc_part_lat_sec', attribute=False, autofill=False, cli_name='loc_lat_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, query=True, required=False) -option: StrEnum('loc_part_lat_dir', attribute=False, autofill=False, cli_name='loc_lat_dir', multivalue=False, option_group=u'LOC Record', query=True, required=False, values=(u'N', u'S')) -option: Int('loc_part_lon_deg', attribute=False, autofill=False, cli_name='loc_lon_deg', maxvalue=180, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False) -option: Int('loc_part_lon_min', attribute=False, autofill=False, cli_name='loc_lon_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False) -option: Decimal('loc_part_lon_sec', attribute=False, autofill=False, cli_name='loc_lon_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, query=True, required=False) -option: StrEnum('loc_part_lon_dir', attribute=False, autofill=False, cli_name='loc_lon_dir', multivalue=False, option_group=u'LOC Record', query=True, required=False, values=(u'E', u'W')) -option: Decimal('loc_part_altitude', attribute=False, autofill=False, cli_name='loc_altitude', maxvalue=Decimal('42849672.95'), minvalue=Decimal('-100000.00'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False) -option: Decimal('loc_part_size', attribute=False, autofill=False, cli_name='loc_size', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False) -option: Decimal('loc_part_h_precision', attribute=False, autofill=False, cli_name='loc_h_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False) -option: Decimal('loc_part_v_precision', attribute=False, autofill=False, cli_name='loc_v_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False) -option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, option_group=u'MX Record', query=True, required=False) -option: Int('mx_part_preference', attribute=False, autofill=False, cli_name='mx_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'MX Record', query=True, required=False) -option: Str('mx_part_exchanger', attribute=False, autofill=False, cli_name='mx_exchanger', multivalue=False, option_group=u'MX Record', query=True, required=False) -option: NAPTRRecord('naptrrecord', attribute=True, autofill=False, cli_name='naptr_rec', csv=True, multivalue=True, option_group=u'NAPTR Record', query=True, required=False) -option: Int('naptr_part_order', attribute=False, autofill=False, cli_name='naptr_order', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: Int('naptr_part_preference', attribute=False, autofill=False, cli_name='naptr_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: Str('naptr_part_flags', attribute=False, autofill=False, cli_name='naptr_flags', multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: Str('naptr_part_service', attribute=False, autofill=False, cli_name='naptr_service', multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: Str('naptr_part_regexp', attribute=False, autofill=False, cli_name='naptr_regexp', multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: Str('naptr_part_replacement', attribute=False, autofill=False, cli_name='naptr_replacement', multivalue=False, option_group=u'NAPTR Record', query=True, required=False) -option: NSRecord('nsrecord', attribute=True, autofill=False, cli_name='ns_rec', csv=True, multivalue=True, option_group=u'NS Record', query=True, required=False) -option: Str('ns_part_hostname', attribute=False, autofill=False, cli_name='ns_hostname', multivalue=False, option_group=u'NS Record', query=True, required=False) -option: NSECRecord('nsecrecord', attribute=True, autofill=False, cli_name='nsec_rec', csv=True, multivalue=True, option_group=u'NSEC Record', query=True, required=False) -option: Str('nsec_part_next', attribute=False, autofill=False, cli_name='nsec_next', multivalue=False, option_group=u'NSEC Record', query=True, required=False) -option: StrEnum('nsec_part_types', attribute=False, autofill=False, cli_name='nsec_types', csv=True, multivalue=True, option_group=u'NSEC Record', query=True, required=False, values=(u'SOA', u'A', u'AAAA', u'A6', u'AFSDB', u'APL', u'CERT', u'CNAME', u'DHCID', u'DLV', u'DNAME', u'DNSKEY', u'DS', u'HIP', u'IPSECKEY', u'KEY', u'KX', u'LOC', u'MX', u'NAPTR', u'NS', u'NSEC', u'NSEC3', u'NSEC3PARAM', u'PTR', u'RRSIG', u'RP', u'SIG', u'SPF', u'SRV', u'SSHFP', u'TA', u'TKEY', u'TSIG', u'TXT')) -option: NSEC3Record('nsec3record', attribute=True, autofill=False, cli_name='nsec3_rec', csv=True, multivalue=True, option_group=u'NSEC3 Record', query=True, required=False) -option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, autofill=False, cli_name='nsec3param_rec', csv=True, multivalue=True, option_group=u'NSEC3PARAM Record', query=True, required=False) -option: PTRRecord('ptrrecord', attribute=True, autofill=False, cli_name='ptr_rec', csv=True, multivalue=True, option_group=u'PTR Record', query=True, required=False) -option: Str('ptr_part_hostname', attribute=False, autofill=False, cli_name='ptr_hostname', multivalue=False, option_group=u'PTR Record', query=True, required=False) -option: RRSIGRecord('rrsigrecord', attribute=True, autofill=False, cli_name='rrsig_rec', csv=True, multivalue=True, option_group=u'RRSIG Record', query=True, required=False) -option: StrEnum('rrsig_part_type_covered', attribute=False, autofill=False, cli_name='rrsig_type_covered', multivalue=False, option_group=u'RRSIG Record', query=True, required=False, values=(u'SOA', u'A', u'AAAA', u'A6', u'AFSDB', u'APL', u'CERT', u'CNAME', u'DHCID', u'DLV', u'DNAME', u'DNSKEY', u'DS', u'HIP', u'IPSECKEY', u'KEY', u'KX', u'LOC', u'MX', u'NAPTR', u'NS', u'NSEC', u'NSEC3', u'NSEC3PARAM', u'PTR', u'RRSIG', u'RP', u'SPF', u'SRV', u'SSHFP', u'TA', u'TKEY', u'TSIG', u'TXT')) -option: Int('rrsig_part_algorithm', attribute=False, autofill=False, cli_name='rrsig_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Int('rrsig_part_labels', attribute=False, autofill=False, cli_name='rrsig_labels', maxvalue=255, minvalue=0, multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Int('rrsig_part_original_ttl', attribute=False, autofill=False, cli_name='rrsig_original_ttl', minvalue=0, multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Str('rrsig_part_signature_expiration', attribute=False, autofill=False, cli_name='rrsig_signature_expiration', multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Str('rrsig_part_signature_inception', attribute=False, autofill=False, cli_name='rrsig_signature_inception', multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Int('rrsig_part_key_tag', attribute=False, autofill=False, cli_name='rrsig_key_tag', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Str('rrsig_part_signers_name', attribute=False, autofill=False, cli_name='rrsig_signers_name', multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: Str('rrsig_part_signature', attribute=False, autofill=False, cli_name='rrsig_signature', multivalue=False, option_group=u'RRSIG Record', query=True, required=False) -option: RPRecord('rprecord', attribute=True, autofill=False, cli_name='rp_rec', csv=True, multivalue=True, option_group=u'RP Record', query=True, required=False) -option: SIGRecord('sigrecord', attribute=True, autofill=False, cli_name='sig_rec', csv=True, multivalue=True, option_group=u'SIG Record', query=True, required=False) -option: StrEnum('sig_part_type_covered', attribute=False, autofill=False, cli_name='sig_type_covered', multivalue=False, option_group=u'SIG Record', query=True, required=False, values=(u'SOA', u'A', u'AAAA', u'A6', u'AFSDB', u'APL', u'CERT', u'CNAME', u'DHCID', u'DLV', u'DNAME', u'DNSKEY', u'DS', u'HIP', u'IPSECKEY', u'KEY', u'KX', u'LOC', u'MX', u'NAPTR', u'NS', u'NSEC', u'NSEC3', u'NSEC3PARAM', u'PTR', u'RRSIG', u'RP', u'SPF', u'SRV', u'SSHFP', u'TA', u'TKEY', u'TSIG', u'TXT')) -option: Int('sig_part_algorithm', attribute=False, autofill=False, cli_name='sig_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Int('sig_part_labels', attribute=False, autofill=False, cli_name='sig_labels', maxvalue=255, minvalue=0, multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Int('sig_part_original_ttl', attribute=False, autofill=False, cli_name='sig_original_ttl', minvalue=0, multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Str('sig_part_signature_expiration', attribute=False, autofill=False, cli_name='sig_signature_expiration', multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Str('sig_part_signature_inception', attribute=False, autofill=False, cli_name='sig_signature_inception', multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Int('sig_part_key_tag', attribute=False, autofill=False, cli_name='sig_key_tag', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Str('sig_part_signers_name', attribute=False, autofill=False, cli_name='sig_signers_name', multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: Str('sig_part_signature', attribute=False, autofill=False, cli_name='sig_signature', multivalue=False, option_group=u'SIG Record', query=True, required=False) -option: SPFRecord('spfrecord', attribute=True, autofill=False, cli_name='spf_rec', csv=True, multivalue=True, option_group=u'SPF Record', query=True, required=False) -option: SRVRecord('srvrecord', attribute=True, autofill=False, cli_name='srv_rec', csv=True, multivalue=True, option_group=u'SRV Record', query=True, required=False) -option: Int('srv_part_priority', attribute=False, autofill=False, cli_name='srv_priority', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'SRV Record', query=True, required=False) -option: Int('srv_part_weight', attribute=False, autofill=False, cli_name='srv_weight', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'SRV Record', query=True, required=False) -option: Int('srv_part_port', attribute=False, autofill=False, cli_name='srv_port', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'SRV Record', query=True, required=False) -option: Str('srv_part_target', attribute=False, autofill=False, cli_name='srv_target', multivalue=False, option_group=u'SRV Record', query=True, required=False) -option: SSHFPRecord('sshfprecord', attribute=True, autofill=False, cli_name='sshfp_rec', csv=True, multivalue=True, option_group=u'SSHFP Record', query=True, required=False) -option: Int('sshfp_part_algorithm', attribute=False, autofill=False, cli_name='sshfp_algorithm', maxvalue=255, minvalue=0, multivalue=False, option_group=u'SSHFP Record', query=True, required=False) -option: Int('sshfp_part_fp_type', attribute=False, autofill=False, cli_name='sshfp_fp_type', maxvalue=255, minvalue=0, multivalue=False, option_group=u'SSHFP Record', query=True, required=False) -option: Str('sshfp_part_fingerprint', attribute=False, autofill=False, cli_name='sshfp_fingerprint', multivalue=False, option_group=u'SSHFP Record', query=True, required=False) -option: TARecord('tarecord', attribute=True, autofill=False, cli_name='ta_rec', csv=True, multivalue=True, option_group=u'TA Record', query=True, required=False) -option: TKEYRecord('tkeyrecord', attribute=True, autofill=False, cli_name='tkey_rec', csv=True, multivalue=True, option_group=u'TKEY Record', query=True, required=False) -option: TSIGRecord('tsigrecord', attribute=True, autofill=False, cli_name='tsig_rec', csv=True, multivalue=True, option_group=u'TSIG Record', query=True, required=False) -option: TXTRecord('txtrecord', attribute=True, autofill=False, cli_name='txt_rec', csv=True, multivalue=True, option_group=u'TXT Record', query=True, required=False) -option: Str('txt_part_data', attribute=False, autofill=False, cli_name='txt_data', multivalue=False, option_group=u'TXT Record', query=True, required=False) +option: ARecord('arecord', attribute=True, autofill=False, cli_name='a_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: AAAARecord('aaaarecord', attribute=True, autofill=False, cli_name='aaaa_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: A6Record('a6record', attribute=True, autofill=False, cli_name='a6_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: AFSDBRecord('afsdbrecord', attribute=True, autofill=False, cli_name='afsdb_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: APLRecord('aplrecord', attribute=True, autofill=False, cli_name='apl_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: CERTRecord('certrecord', attribute=True, autofill=False, cli_name='cert_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: CNAMERecord('cnamerecord', attribute=True, autofill=False, cli_name='cname_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: DHCIDRecord('dhcidrecord', attribute=True, autofill=False, cli_name='dhcid_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: DLVRecord('dlvrecord', attribute=True, autofill=False, cli_name='dlv_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: DNAMERecord('dnamerecord', attribute=True, autofill=False, cli_name='dname_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: DNSKEYRecord('dnskeyrecord', attribute=True, autofill=False, cli_name='dnskey_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: DSRecord('dsrecord', attribute=True, autofill=False, cli_name='ds_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: HIPRecord('hiprecord', attribute=True, autofill=False, cli_name='hip_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: IPSECKEYRecord('ipseckeyrecord', attribute=True, autofill=False, cli_name='ipseckey_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: KEYRecord('keyrecord', attribute=True, autofill=False, cli_name='key_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: KXRecord('kxrecord', attribute=True, autofill=False, cli_name='kx_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: NAPTRRecord('naptrrecord', attribute=True, autofill=False, cli_name='naptr_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: NSRecord('nsrecord', attribute=True, autofill=False, cli_name='ns_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: NSECRecord('nsecrecord', attribute=True, autofill=False, cli_name='nsec_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: NSEC3Record('nsec3record', attribute=True, autofill=False, cli_name='nsec3_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, autofill=False, cli_name='nsec3param_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: PTRRecord('ptrrecord', attribute=True, autofill=False, cli_name='ptr_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: RRSIGRecord('rrsigrecord', attribute=True, autofill=False, cli_name='rrsig_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: RPRecord('rprecord', attribute=True, autofill=False, cli_name='rp_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: SIGRecord('sigrecord', attribute=True, autofill=False, cli_name='sig_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: SPFRecord('spfrecord', attribute=True, autofill=False, cli_name='spf_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: SRVRecord('srvrecord', attribute=True, autofill=False, cli_name='srv_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: SSHFPRecord('sshfprecord', attribute=True, autofill=False, cli_name='sshfp_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: TARecord('tarecord', attribute=True, autofill=False, cli_name='ta_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: TKEYRecord('tkeyrecord', attribute=True, autofill=False, cli_name='tkey_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: TSIGRecord('tsigrecord', attribute=True, autofill=False, cli_name='tsig_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) +option: TXTRecord('txtrecord', attribute=True, autofill=False, cli_name='txt_rec', csv=True, multivalue=True, option_group=None, query=True, required=False) option: Int('timelimit?', autofill=False, minvalue=0) option: Int('sizelimit?', autofill=False, minvalue=0) option: Flag('structured', autofill=True, default=False) @@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=26 +IPA_API_VERSION_MINOR=27 diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index aa2efdef..d7217293 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -83,8 +83,10 @@ EXAMPLES: Add LOC record for example.com: ipa dnsrecord-add example.com @ --loc-rec="49 11 42.4 N 16 36 29.6 E 227.64m" - Add new A record for www.example.com: (random IP) - ipa dnsrecord-add example.com www --a-rec=80.142.15.2 + Add new A record for www.example.com. Create a reverse record in appropriate + reverse zone as well. In this case a PTR record "2" pointing to www.example.com. + will be created in zone 15.142.80.in-addr.arpa. + ipa dnsrecord-add example.com www --a-rec=80.142.15.2 --a-create-reverse Add new PTR record for www.example.com ipa dnsrecord-add 15.142.80.in-addr.arpa. 2 --ptr-rec=www.example.com. @@ -324,8 +326,132 @@ def _normalize_hostname(domain_name): else: return domain_name +def is_forward_record(zone, str_address): + addr = netaddr.IPAddress(str_address) + if addr.version == 4: + result = api.Command['dnsrecord_find'](zone, arecord=str_address) + elif addr.version == 6: + result = api.Command['dnsrecord_find'](zone, aaaarecord=str_address) + else: + raise ValueError('Invalid address family') + + return result['count'] > 0 + +def add_forward_record(zone, name, str_address): + addr = netaddr.IPAddress(str_address) + try: + if addr.version == 4: + api.Command['dnsrecord_add'](zone, name, arecord=str_address) + elif addr.version == 6: + api.Command['dnsrecord_add'](zone, name, aaaarecord=str_address) + else: + raise ValueError('Invalid address family') + except errors.EmptyModlist: + pass # the entry already exists and matches + +def get_reverse_zone(ipaddr, prefixlen=None): + ip = netaddr.IPAddress(ipaddr) + revdns = unicode(ip.reverse_dns) + + if prefixlen is None: + revzone = u'' + + result = api.Command['dnszone_find']()['result'] + for zone in result: + zonename = zone['idnsname'][0] + if revdns.endswith(zonename) and len(zonename) > len(revzone): + revzone = zonename + else: + if ip.version == 4: + pos = 4 - prefixlen / 8 + elif ip.version == 6: + pos = 32 - prefixlen / 4 + items = ip.reverse_dns.split('.') + revzone = u'.'.join(items[pos:]) + + try: + api.Command['dnszone_show'](revzone) + except errors.NotFound: + revzone = u'' + + if len(revzone) == 0: + raise errors.NotFound( + reason=_('DNS reverse zone for IP address %(addr)s not found') % dict(addr=ipaddr) + ) + + revname = revdns[:-len(revzone)-1] + + return revzone, revname + +def add_records_for_host_validation(option_name, host, domain, ip_addresses, check_forward=True, check_reverse=True): + result = api.Command['dnszone_find']()['result'] + match = False + for zone in result: + if domain == zone['idnsname'][0]: + match = True + break + if not match: + raise errors.NotFound( + reason=_('DNS zone %(zone)s not found') % dict(zone=domain) + ) + if not isinstance(ip_addresses, (tuple, list)): + ip_addresses = [ip_addresses] + + for ip_address in ip_addresses: + try: + ip = CheckedIPAddress(ip_address, match_local=False) + except Exception, e: + raise errors.ValidationError(name=option_name, error=unicode(e)) + + if check_forward: + if is_forward_record(domain, unicode(ip)): + raise errors.DuplicateEntry( + message=_(u'IP address %(ip)s is already assigned in domain %(domain)s.')\ + % dict(ip=str(ip), domain=domain)) + + if check_reverse: + try: + prefixlen = None + if not ip.defaultnet: + prefixlen = ip.prefixlen + # we prefer lookup of the IP through the reverse zone + revzone, revname = get_reverse_zone(ip, prefixlen) + reverse = api.Command['dnsrecord_find'](revzone, idnsname=revname) + if reverse['count'] > 0: + raise errors.DuplicateEntry( + message=_(u'Reverse record for IP address %(ip)s already exists in reverse zone %(zone)s.')\ + % dict(ip=str(ip), zone=revzone)) + except errors.NotFound: + pass + + +def add_records_for_host(host, domain, ip_addresses, add_forward=True, add_reverse=True): + if not isinstance(ip_addresses, (tuple, list)): + ip_addresses = [ip_addresses] + + for ip_address in ip_addresses: + ip = CheckedIPAddress(ip_address, match_local=False) + + if add_forward: + add_forward_record(domain, host, unicode(ip)) + + if add_reverse: + try: + prefixlen = None + if not ip.defaultnet: + prefixlen = ip.prefixlen + revzone, revname = get_reverse_zone(ip, prefixlen) + addkw = { 'ptrrecord' : host + "." + domain } + api.Command['dnsrecord_add'](revzone, revname, **addkw) + except errors.EmptyModlist: + # the entry already exists and matches + pass + class DNSRecord(Str): + # a list of parts that create the actual raw DNS record parts = None + # an optional list of parameters used in record-specific operations + extra = None supported = True # supported RR types: https://fedorahosted.org/bind-dyndb-ldap/browser/doc/schema @@ -335,6 +461,7 @@ class DNSRecord(Str): option_group_format = _('%s Record') see_rfc_msg = _("(see RFC %s for details)") part_name_format = "%s_part_%s" + extra_name_format = "%s_extra_%s" cli_name_format = "%s_%s" format_error_msg = None @@ -478,30 +605,59 @@ class DNSRecord(Str): part.validate(val) return None + def _convert_dnsrecord_part(self, part): + """ + All parts of DNSRecord need to be processed and modified before they + can be added to global DNS API. For example a prefix need to be added + before part name so that the name is unique in the global namespace. + """ + name = self.part_name_format % (self.rrtype.lower(), part.name) + cli_name = self.cli_name_format % (self.rrtype.lower(), part.name) + label = self.part_label_format % (self.rrtype, unicode(part.label)) + option_group = self.option_group_format % self.rrtype + flags = list(part.flags) + ['dnsrecord_part', 'virtual_attribute',] + + if not part.required: + flags.append('dnsrecord_optional') + + return part.clone_rename(name, + cli_name=cli_name, + label=label, + required=False, + option_group=option_group, + flags=flags, + hint=self.name,) # name of parent RR param + + def _convert_dnsrecord_extra(self, extra): + """ + Parameters for special per-type behavior need to be processed in the + same way as record parts in _convert_dnsrecord_part(). + """ + name = self.extra_name_format % (self.rrtype.lower(), extra.name) + cli_name = self.cli_name_format % (self.rrtype.lower(), extra.name) + label = self.part_label_format % (self.rrtype, unicode(extra.label)) + option_group = self.option_group_format % self.rrtype + flags = list(extra.flags) + ['dnsrecord_extra', 'virtual_attribute',] + + return extra.clone_rename(name, + cli_name=cli_name, + label=label, + required=False, + option_group=option_group, + flags=flags, + hint=self.name,) # name of parent RR param + def get_parts(self): if self.parts is None: return tuple() - parts = [] + return tuple(self._convert_dnsrecord_part(part) for part in self.parts) - for part in self.parts: - name = self.part_name_format % (self.rrtype.lower(), part.name) - cli_name = self.cli_name_format % (self.rrtype.lower(), part.name) - label = self.part_label_format % (self.rrtype, unicode(part.label)) - option_group = self.option_group_format % self.rrtype - flags = list(part.flags) + ['dnsrecord_part', 'virtual_attribute',] - - if not part.required: - flags.append('dnsrecord_optional') - - parts.append(part.clone_rename(name, - cli_name=cli_name, - label=label, - required=False, - option_group=option_group, - flags=flags)) + def get_extra(self): + if self.extra is None: + return tuple() - return tuple(parts) + return tuple(self._convert_dnsrecord_extra(extra) for extra in self.extra) def prompt_parts(self, backend, mod_dnsvalue=None): mod_parts = None @@ -531,7 +687,54 @@ class DNSRecord(Str): return user_options -class ARecord(DNSRecord): + # callbacks for per-type special record behavior + def dnsrecord_add_pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + pass + + def dnsrecord_add_post_callback(self, ldap, dn, entry_attrs, *keys, **options): + pass + +class ForwardRecord(DNSRecord): + extra = ( + Flag('create_reverse?', + label=_('Create reverse'), + doc=_('Create reverse record for this IP Address'), + flags=['no_update'] + ), + ) + + def dnsrecord_add_pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + reverse_option = self._convert_dnsrecord_extra(self.extra[0]) + if options.get(reverse_option.name): + records = entry_attrs.get(self.name, []) + if not records: + # --<rrtype>-create-reverse is set, but there are not records + raise errors.RequirementError(name=self.name) + + for record in records: + add_records_for_host_validation(self.name, keys[-1], keys[-2], record, + check_forward=False, + check_reverse=True) + + setattr(context, '%s_reverse' % self.name, entry_attrs.get(self.name)) + + def dnsrecord_add_post_callback(self, ldap, dn, entry_attrs, *keys, **options): + rev_records = getattr(context, '%s_reverse' % self.name, []) + + if rev_records: + # make sure we don't run this post callback action again in nested + # commands, line adding PTR record in add_records_for_host + delattr(context, '%s_reverse' % self.name) + for record in rev_records: + try: + add_records_for_host(keys[-1], keys[-2], record, + add_forward=False, add_reverse=True) + except Exception, e: + raise errors.NonFatalError( + reason=_('Cannot create reverse record for "%(value)s": %(exc)s') \ + % dict(value=record, exc=unicode(e))) + +class ARecord(ForwardRecord): rrtype = 'A' rfc = 1035 parts = ( @@ -554,7 +757,7 @@ class A6Record(DNSRecord): # A6 RR type is obsolete and only a raw interface is provided return (value,) -class AAAARecord(DNSRecord): +class AAAARecord(ForwardRecord): rrtype = 'AAAA' rfc = 3596 parts = ( @@ -1169,6 +1372,9 @@ def __dns_record_options_iter(): for part in option.get_parts(): yield part + for extra in option.get_extra(): + yield extra + _dns_record_options = tuple(__dns_record_options_iter()) # dictionary of valid reverse zone -> number of address components @@ -1192,18 +1398,6 @@ def is_ns_rec_resolvable(name): reason=_('Nameserver \'%(host)s\' does not have a corresponding A/AAAA record') % {'host': name} ) -def add_forward_record(zone, name, str_address): - addr = netaddr.IPAddress(str_address) - try: - if addr.version == 4: - api.Command['dnsrecord_add'](zone, name, arecord=str_address) - elif addr.version == 6: - api.Command['dnsrecord_add'](zone, name, aaaarecord=str_address) - else: - raise ValueError('Invalid address family') - except errors.EmptyModlist: - pass # the entry already exists and matches - def dns_container_exists(ldap): try: ldap.get_entry(api.env.container_dns, []) @@ -1659,13 +1853,6 @@ class dnsrecord(LDAPObject): if not has_options: raise errors.OptionError(no_option_msg) - def get_record_option(self, rec_type): - name = '%srecord' % rec_type.lower() - if name in self.params: - return self.params[name] - else: - return None - def get_record_entry_attrs(self, entry_attrs): return dict((attr, val) for attr,val in entry_attrs.iteritems() \ if attr in self.params and not self.params[attr].primary_key) @@ -1766,19 +1953,60 @@ class dnsrecord_add(LDAPCreate): if hasattr(self.obj, rtype_cb): dn = getattr(self.obj, rtype_cb)(ldap, dn, entry_attrs, *keys, **options) - # check if any record part was added + precallback_attrs = [] for option in options: - option_part_re = re.match(r'([a-z0-9]+)_part_', option) + try: + param = self.params[option] + except KeyError: + continue - if option_part_re is not None: - record_option = self.obj.get_record_option(option_part_re.group(1)) - if record_option.name in entry_attrs: + if 'dnsrecord_part' in param.flags: + # check if any record part was added + try: + rrparam = self.params[param.hint] + except KeyError, AttributeError: + continue + + if rrparam.name in entry_attrs: # this record was already entered continue - parts = record_option.get_parts_from_kw(options) - dnsvalue = [record_option._convert_scalar(parts)] - entry_attrs[record_option.name] = dnsvalue + parts = rrparam.get_parts_from_kw(options) + dnsvalue = [rrparam._convert_scalar(parts)] + entry_attrs[rrparam.name] = dnsvalue + continue + + if 'dnsrecord_extra' in param.flags: + # do not run precallback for unset flags + if isinstance(param, Flag) and not options[option]: + continue + # extra option is passed, run per-type pre_callback for given RR type + precallback_attrs.append(param.hint) + + # run precallback also for all new RR type attributes in entry_attrs + for attr in entry_attrs: + try: + param = self.params[attr] + except KeyError: + continue + + if not isinstance(param, DNSRecord): + continue + precallback_attrs.append(attr) + + precallback_attrs = list(set(precallback_attrs)) + + for attr in precallback_attrs: + # run per-type + try: + param = self.params[attr] + except KeyError: + continue + param.dnsrecord_add_pre_callback(ldap, dn, entry_attrs, attrs_list, *keys, **options) + + # Store all new attrs so that DNSRecord post callback is called for + # new attributes only and not for all attributes in the LDAP entry + setattr(context, 'dnsrecord_precallback_attrs', precallback_attrs) try: (dn_, old_entry) = ldap.get_entry( @@ -1810,6 +2038,10 @@ class dnsrecord_add(LDAPCreate): raise exc def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + for attr in getattr(context, 'dnsrecord_precallback_attrs', []): + param = self.params[attr] + param.dnsrecord_add_post_callback(ldap, dn, entry_attrs, *keys, **options) + self.obj.postprocess_record(entry_attrs, **options) return dn @@ -1993,7 +2225,8 @@ class dnsrecord_del(LDAPUpdate): def get_options(self): for option in super(dnsrecord_del, self).get_options(): - if 'dnsrecord_part' in option.flags: + if any(flag in option.flags for flag in \ + ('dnsrecord_part', 'dnsrecord_extra',)): continue elif isinstance(option, DNSRecord): yield option.clone(option_group=None) @@ -2138,6 +2371,16 @@ class dnsrecord_find(LDAPSearch): dnsrecord.structured_flag, ) + def get_options(self): + for option in super(dnsrecord_find, self).get_options(): + if any(flag in option.flags for flag in \ + ('dnsrecord_part', 'dnsrecord_extra',)): + continue + elif isinstance(option, DNSRecord): + yield option.clone(option_group=None) + continue + yield option + def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options): # include zone record (root entry) in the search return (filter, base_dn, ldap.SCOPE_SUBTREE) diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index 012817e6..df9ad737 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -31,8 +31,8 @@ from ipalib.plugins.baseldap import * from ipalib.plugins.service import split_principal from ipalib.plugins.service import validate_certificate from ipalib.plugins.service import set_certificate_attrs -from ipalib.plugins.dns import dns_container_exists, _record_types -from ipalib.plugins.dns import add_forward_record +from ipalib.plugins.dns import dns_container_exists, _record_types, add_records_for_host_validation, add_records_for_host +from ipalib.plugins.dns import get_reverse_zone from ipalib import _, ngettext from ipalib import x509 from ipalib.dn import * @@ -105,51 +105,6 @@ def validate_host(ugettext, fqdn): return _('Fully-qualified hostname required') return None -def is_forward_record(zone, str_address): - addr = netaddr.IPAddress(str_address) - if addr.version == 4: - result = api.Command['dnsrecord_find'](zone, arecord=str_address) - elif addr.version == 6: - result = api.Command['dnsrecord_find'](zone, aaaarecord=str_address) - else: - raise ValueError('Invalid address family') - - return result['count'] > 0 - -def get_reverse_zone(ipaddr, prefixlen=None): - ip = netaddr.IPAddress(ipaddr) - revdns = unicode(ip.reverse_dns) - - if prefixlen is None: - revzone = u'' - - result = api.Command['dnszone_find']()['result'] - for zone in result: - zonename = zone['idnsname'][0] - if revdns.endswith(zonename) and len(zonename) > len(revzone): - revzone = zonename - else: - if ip.version == 4: - pos = 4 - prefixlen / 8 - elif ip.version == 6: - pos = 32 - prefixlen / 4 - items = ip.reverse_dns.split('.') - revzone = u'.'.join(items[pos:]) - - try: - api.Command['dnszone_show'](revzone) - except errors.NotFound: - revzone = u'' - - if len(revzone) == 0: - raise errors.NotFound( - reason=_('DNS reverse zone for IP address %(addr)s not found') % dict(addr=ipaddr) - ) - - revname = revdns[:-len(revzone)-1] - - return revzone, revname - def remove_fwd_ptr(ipaddr, host, domain, recordtype): api.log.debug('deleting ipaddr %s' % ipaddr) try: @@ -421,35 +376,15 @@ class host_add(LDAPCreate): ) def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - if 'ip_address' in options and dns_container_exists(ldap): + if options.get('ip_address') and dns_container_exists(ldap): parts = keys[-1].split('.') + host = parts[0] domain = unicode('.'.join(parts[1:])) - result = api.Command['dnszone_find']()['result'] - match = False - for zone in result: - if domain == zone['idnsname'][0]: - match = True - break - if not match: - raise errors.NotFound( - reason=_('DNS zone %(zone)s not found') % dict(zone=domain) - ) - ip = CheckedIPAddress(options['ip_address'], match_local=False) - if not options.get('no_reverse', False): - try: - prefixlen = None - if not ip.defaultnet: - prefixlen = ip.prefixlen - # we prefer lookup of the IP through the reverse zone - revzone, revname = get_reverse_zone(ip, prefixlen) - reverse = api.Command['dnsrecord_find'](revzone, idnsname=revname) - if reverse['count'] > 0: - raise errors.DuplicateEntry(message=u'This IP address is already assigned.') - except errors.NotFound: - pass - else: - if is_forward_record(domain, unicode(ip)): - raise errors.DuplicateEntry(message=u'This IP address is already assigned.') + check_reverse = not options.get('no_reverse', False) + add_records_for_host_validation('ip_address', host, domain, + options['ip_address'], + check_forward=True, + check_reverse=check_reverse) if not options.get('force', False) and not 'ip_address' in options: util.validate_host_dns(self.log, keys[-1]) if 'locality' in entry_attrs: @@ -489,24 +424,15 @@ class host_add(LDAPCreate): if dns_container_exists(ldap): try: parts = keys[-1].split('.') + host = parts[0] domain = unicode('.'.join(parts[1:])) - if 'ip_address' in options: - ip = CheckedIPAddress(options['ip_address'], match_local=False) - add_forward_record(domain, parts[0], unicode(ip)) - - if not options.get('no_reverse', False): - try: - prefixlen = None - if not ip.defaultnet: - prefixlen = ip.prefixlen - revzone, revname = get_reverse_zone(ip, prefixlen) - addkw = { 'ptrrecord' : keys[-1]+'.' } - api.Command['dnsrecord_add'](revzone, revname, **addkw) - except errors.EmptyModlist: - # the entry already exists and matches - pass + if options.get('ip_address'): + add_reverse = not options.get('no_reverse', False) + add_records_for_host(host, domain, options['ip_address'], + add_forward=True, + add_reverse=add_reverse) del options['ip_address'] update_sshfp_record(domain, unicode(parts[0]), entry_attrs) diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py index 5d05d3af..7b1a4532 100644 --- a/tests/test_xmlrpc/test_dns_plugin.py +++ b/tests/test_xmlrpc/test_dns_plugin.py @@ -42,6 +42,8 @@ dnsres1 = u'testdnsres' dnsres1_dn = DN(('idnsname',dnsres1), dnszone1_dn) dnsrev1 = u'80' dnsrev1_dn = DN(('idnsname',dnsrev1), revdnszone1_dn) +dnsrev2 = u'81' +dnsrev2_dn = DN(('idnsname',dnsrev2), revdnszone1_dn) class test_dns(Declarative): @@ -874,6 +876,46 @@ class test_dns(Declarative): dict( + desc='Try to create duplicate PTR record for %r with --a-create-reverse' % dnsres1, + command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'80.142.15.80', + 'a_extra_create_reverse' : True}), + expected=errors.DuplicateEntry(message=u''), + ), + + + dict( + desc='Create A record %r in zone %r with --a-create-reverse' % (dnsres1, dnszone1), + command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'80.142.15.81', + 'a_extra_create_reverse' : True}), + expected={ + 'value': dnsres1, + 'summary': None, + 'result': { + 'dn': unicode(dnsres1_dn), + 'idnsname': [dnsres1], + 'objectclass': [u'top', u'idnsrecord'], + 'arecord': [u'80.142.15.81'], + }, + }, + ), + + + dict( + desc='Check reverse record for %r created via --a-create-reverse' % dnsres1, + command=('dnsrecord_show', [revdnszone1, dnsrev2], {}), + expected={ + 'value': dnsrev2, + 'summary': None, + 'result': { + 'dn': unicode(dnsrev2_dn), + 'idnsname': [dnsrev2], + 'ptrrecord': [dnsres1 + '.' + dnszone1 + '.'], + }, + }, + ), + + + dict( desc='Delete zone %r' % dnszone1, command=('dnszone_del', [dnszone1], {}), expected={ |