summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt328
-rw-r--r--ipalib/plugins/dns.py1347
-rw-r--r--ipalib/util.py16
-rw-r--r--tests/test_xmlrpc/test_dns_plugin.py19
4 files changed, 971 insertions, 739 deletions
diff --git a/API.txt b/API.txt
index 493d5a3ff..895225a1c 100644
--- a/API.txt
+++ b/API.txt
@@ -613,135 +613,93 @@ 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, required=False)
+option: AAAARecord('aaaarecord', attribute=True, cli_name='aaaa_rec', csv=True, multivalue=True, required=False)
+option: A6Record('a6record', attribute=True, cli_name='a6_rec', csv=True, multivalue=True, required=False)
+option: AFSDBRecord('afsdbrecord', attribute=True, cli_name='afsdb_rec', csv=True, multivalue=True, required=False)
+option: APLRecord('aplrecord', attribute=True, cli_name='apl_rec', csv=True, multivalue=True, required=False)
+option: CERTRecord('certrecord', attribute=True, cli_name='cert_rec', csv=True, multivalue=True, required=False)
+option: CNAMERecord('cnamerecord', attribute=True, cli_name='cname_rec', csv=True, multivalue=True, required=False)
+option: DHCIDRecord('dhcidrecord', attribute=True, cli_name='dhcid_rec', csv=True, multivalue=True, required=False)
+option: DLVRecord('dlvrecord', attribute=True, cli_name='dlv_rec', csv=True, multivalue=True, required=False)
+option: DNAMERecord('dnamerecord', attribute=True, cli_name='dname_rec', csv=True, multivalue=True, required=False)
+option: DNSKEYRecord('dnskeyrecord', attribute=True, cli_name='dnskey_rec', csv=True, multivalue=True, required=False)
+option: DSRecord('dsrecord', attribute=True, cli_name='ds_rec', csv=True, multivalue=True, required=False)
+option: HIPRecord('hiprecord', attribute=True, cli_name='hip_rec', csv=True, multivalue=True, required=False)
+option: IPSECKEYRecord('ipseckeyrecord', attribute=True, cli_name='ipseckey_rec', csv=True, multivalue=True, required=False)
+option: KEYRecord('keyrecord', attribute=True, cli_name='key_rec', csv=True, multivalue=True, required=False)
+option: KXRecord('kxrecord', attribute=True, cli_name='kx_rec', csv=True, multivalue=True, required=False)
+option: LOCRecord('locrecord', attribute=True, cli_name='loc_rec', csv=True, multivalue=True, required=False)
+option: MXRecord('mxrecord', attribute=True, cli_name='mx_rec', csv=True, multivalue=True, required=False)
+option: NAPTRRecord('naptrrecord', attribute=True, cli_name='naptr_rec', csv=True, multivalue=True, required=False)
+option: NSRecord('nsrecord', attribute=True, cli_name='ns_rec', csv=True, multivalue=True, required=False)
+option: NSECRecord('nsecrecord', attribute=True, cli_name='nsec_rec', csv=True, multivalue=True, required=False)
+option: NSEC3Record('nsec3record', attribute=True, cli_name='nsec3_rec', csv=True, multivalue=True, required=False)
+option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, cli_name='nsec3param_rec', csv=True, multivalue=True, required=False)
+option: PTRRecord('ptrrecord', attribute=True, cli_name='ptr_rec', csv=True, multivalue=True, required=False)
+option: RRSIGRecord('rrsigrecord', attribute=True, cli_name='rrsig_rec', csv=True, multivalue=True, required=False)
+option: RPRecord('rprecord', attribute=True, cli_name='rp_rec', csv=True, multivalue=True, required=False)
+option: SIGRecord('sigrecord', attribute=True, cli_name='sig_rec', csv=True, multivalue=True, required=False)
+option: SPFRecord('spfrecord', attribute=True, cli_name='spf_rec', csv=True, multivalue=True, required=False)
+option: SRVRecord('srvrecord', attribute=True, cli_name='srv_rec', csv=True, multivalue=True, required=False)
+option: SSHFPRecord('sshfprecord', attribute=True, cli_name='sshfp_rec', csv=True, multivalue=True, required=False)
+option: TARecord('tarecord', attribute=True, cli_name='ta_rec', csv=True, multivalue=True, required=False)
+option: TKEYRecord('tkeyrecord', attribute=True, cli_name='tkey_rec', csv=True, multivalue=True, required=False)
+option: TSIGRecord('tsigrecord', attribute=True, cli_name='tsig_rec', csv=True, multivalue=True, required=False)
+option: TXTRecord('txtrecord', attribute=True, cli_name='txt_rec', csv=True, multivalue=True, required=False)
option: Str('setattr*', cli_name='setattr', exclude='webui')
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('force', autofill=True, default=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('version?', exclude='webui')
-option: Str('arecord*', attribute=True, cli_name='a_rec', csv=True)
-option: Str('aaaarecord*', attribute=True, cli_name='aaaa_rec', csv=True)
-option: Str('a6record*', attribute=True, cli_name='a6_rec', csv=True)
-option: Str('afsdbrecord*', attribute=True, cli_name='afsdb_rec', csv=True)
-option: Str('aplrecord*', attribute=True, cli_name='apl_rec', csv=True)
-option: Str('certrecord*', attribute=True, cli_name='cert_rec', csv=True)
-option: Str('cnamerecord*', attribute=True, cli_name='cname_rec', csv=True)
-option: Str('dhcidrecord*', attribute=True, cli_name='dhcid_rec', csv=True)
-option: Str('dlvrecord*', attribute=True, cli_name='dlv_rec', csv=True)
-option: Str('dnamerecord*', attribute=True, cli_name='dname_rec', csv=True)
-option: Str('dnskeyrecord*', attribute=True, cli_name='dnskey_rec', csv=True)
-option: Str('dsrecord*', attribute=True, cli_name='ds_rec', csv=True)
-option: Str('hiprecord*', attribute=True, cli_name='hip_rec', csv=True)
-option: Str('ipseckeyrecord*', attribute=True, cli_name='ipseckey_rec', csv=True)
-option: Str('keyrecord*', attribute=True, cli_name='key_rec', csv=True)
-option: Str('kxrecord*', attribute=True, cli_name='kx_rec', csv=True)
-option: Str('locrecord*', attribute=True, cli_name='loc_rec', csv=True)
-option: Str('mxrecord*', attribute=True, cli_name='mx_rec', csv=True)
-option: Str('naptrrecord*', attribute=True, cli_name='naptr_rec', csv=True)
-option: Str('nsrecord*', attribute=True, cli_name='ns_rec', csv=True)
-option: Str('nsecrecord*', attribute=True, cli_name='nsec_rec', csv=True)
-option: Str('nsec3record*', attribute=True, cli_name='nsec3_rec', csv=True)
-option: Str('nsec3paramrecord*', attribute=True, cli_name='nsec3param_rec', csv=True)
-option: Str('ptrrecord*', attribute=True, cli_name='ptr_rec', csv=True)
-option: Str('rrsigrecord*', attribute=True, cli_name='rrsig_rec', csv=True)
-option: Str('rprecord*', attribute=True, cli_name='rp_rec', csv=True)
-option: Str('sigrecord*', attribute=True, cli_name='sig_rec', csv=True)
-option: Str('spfrecord*', attribute=True, cli_name='spf_rec', csv=True)
-option: Str('srvrecord*', attribute=True, cli_name='srv_rec', csv=True)
-option: Str('sshfprecord*', attribute=True, cli_name='sshfp_rec', csv=True)
-option: Str('tarecord*', attribute=True, cli_name='ta_rec', csv=True)
-option: Str('tkeyrecord*', attribute=True, cli_name='tkey_rec', csv=True)
-option: Str('tsigrecord*', attribute=True, cli_name='tsig_rec', csv=True)
-option: Str('txtrecord*', attribute=True, cli_name='txt_rec', csv=True)
-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_record
-args: 2,37,3
-arg: Str('dnszoneidnsname', cli_name='dnszone', query=True, required=True)
-arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
-option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
-option: Str('version?', exclude='webui')
-option: Str('arecord*', attribute=True, cli_name='a_rec', csv=True)
-option: Str('aaaarecord*', attribute=True, cli_name='aaaa_rec', csv=True)
-option: Str('a6record*', attribute=True, cli_name='a6_rec', csv=True)
-option: Str('afsdbrecord*', attribute=True, cli_name='afsdb_rec', csv=True)
-option: Str('aplrecord*', attribute=True, cli_name='apl_rec', csv=True)
-option: Str('certrecord*', attribute=True, cli_name='cert_rec', csv=True)
-option: Str('cnamerecord*', attribute=True, cli_name='cname_rec', csv=True)
-option: Str('dhcidrecord*', attribute=True, cli_name='dhcid_rec', csv=True)
-option: Str('dlvrecord*', attribute=True, cli_name='dlv_rec', csv=True)
-option: Str('dnamerecord*', attribute=True, cli_name='dname_rec', csv=True)
-option: Str('dnskeyrecord*', attribute=True, cli_name='dnskey_rec', csv=True)
-option: Str('dsrecord*', attribute=True, cli_name='ds_rec', csv=True)
-option: Str('hiprecord*', attribute=True, cli_name='hip_rec', csv=True)
-option: Str('ipseckeyrecord*', attribute=True, cli_name='ipseckey_rec', csv=True)
-option: Str('keyrecord*', attribute=True, cli_name='key_rec', csv=True)
-option: Str('kxrecord*', attribute=True, cli_name='kx_rec', csv=True)
-option: Str('locrecord*', attribute=True, cli_name='loc_rec', csv=True)
-option: Str('mxrecord*', attribute=True, cli_name='mx_rec', csv=True)
-option: Str('naptrrecord*', attribute=True, cli_name='naptr_rec', csv=True)
-option: Str('nsrecord*', attribute=True, cli_name='ns_rec', csv=True)
-option: Str('nsecrecord*', attribute=True, cli_name='nsec_rec', csv=True)
-option: Str('nsec3record*', attribute=True, cli_name='nsec3_rec', csv=True)
-option: Str('nsec3paramrecord*', attribute=True, cli_name='nsec3param_rec', csv=True)
-option: Str('ptrrecord*', attribute=True, cli_name='ptr_rec', csv=True)
-option: Str('rrsigrecord*', attribute=True, cli_name='rrsig_rec', csv=True)
-option: Str('rprecord*', attribute=True, cli_name='rp_rec', csv=True)
-option: Str('sigrecord*', attribute=True, cli_name='sig_rec', csv=True)
-option: Str('spfrecord*', attribute=True, cli_name='spf_rec', csv=True)
-option: Str('srvrecord*', attribute=True, cli_name='srv_rec', csv=True)
-option: Str('sshfprecord*', attribute=True, cli_name='sshfp_rec', csv=True)
-option: Str('tarecord*', attribute=True, cli_name='ta_rec', csv=True)
-option: Str('tkeyrecord*', attribute=True, cli_name='tkey_rec', csv=True)
-option: Str('tsigrecord*', attribute=True, cli_name='tsig_rec', csv=True)
-option: Str('txtrecord*', attribute=True, cli_name='txt_rec', csv=True)
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_del
-args: 2,38,3
+args: 2,40,3
arg: Str('dnszoneidnsname', cli_name='dnszone', query=True, required=True)
arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Int('dnsttl', attribute=True, autofill=False, cli_name='ttl', multivalue=False, required=False)
+option: StrEnum('dnsclass', attribute=True, autofill=False, cli_name='class', multivalue=False, 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, required=False)
+option: AAAARecord('aaaarecord', attribute=True, autofill=False, cli_name='aaaa_rec', csv=True, multivalue=True, required=False)
+option: A6Record('a6record', attribute=True, autofill=False, cli_name='a6_rec', csv=True, multivalue=True, required=False)
+option: AFSDBRecord('afsdbrecord', attribute=True, autofill=False, cli_name='afsdb_rec', csv=True, multivalue=True, required=False)
+option: APLRecord('aplrecord', attribute=True, autofill=False, cli_name='apl_rec', csv=True, multivalue=True, required=False)
+option: CERTRecord('certrecord', attribute=True, autofill=False, cli_name='cert_rec', csv=True, multivalue=True, required=False)
+option: CNAMERecord('cnamerecord', attribute=True, autofill=False, cli_name='cname_rec', csv=True, multivalue=True, required=False)
+option: DHCIDRecord('dhcidrecord', attribute=True, autofill=False, cli_name='dhcid_rec', csv=True, multivalue=True, required=False)
+option: DLVRecord('dlvrecord', attribute=True, autofill=False, cli_name='dlv_rec', csv=True, multivalue=True, required=False)
+option: DNAMERecord('dnamerecord', attribute=True, autofill=False, cli_name='dname_rec', csv=True, multivalue=True, required=False)
+option: DNSKEYRecord('dnskeyrecord', attribute=True, autofill=False, cli_name='dnskey_rec', csv=True, multivalue=True, required=False)
+option: DSRecord('dsrecord', attribute=True, autofill=False, cli_name='ds_rec', csv=True, multivalue=True, required=False)
+option: HIPRecord('hiprecord', attribute=True, autofill=False, cli_name='hip_rec', csv=True, multivalue=True, required=False)
+option: IPSECKEYRecord('ipseckeyrecord', attribute=True, autofill=False, cli_name='ipseckey_rec', csv=True, multivalue=True, required=False)
+option: KEYRecord('keyrecord', attribute=True, autofill=False, cli_name='key_rec', csv=True, multivalue=True, required=False)
+option: KXRecord('kxrecord', attribute=True, autofill=False, cli_name='kx_rec', csv=True, multivalue=True, required=False)
+option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, required=False)
+option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, required=False)
+option: NAPTRRecord('naptrrecord', attribute=True, autofill=False, cli_name='naptr_rec', csv=True, multivalue=True, required=False)
+option: NSRecord('nsrecord', attribute=True, autofill=False, cli_name='ns_rec', csv=True, multivalue=True, required=False)
+option: NSECRecord('nsecrecord', attribute=True, autofill=False, cli_name='nsec_rec', csv=True, multivalue=True, required=False)
+option: NSEC3Record('nsec3record', attribute=True, autofill=False, cli_name='nsec3_rec', csv=True, multivalue=True, required=False)
+option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, autofill=False, cli_name='nsec3param_rec', csv=True, multivalue=True, required=False)
+option: PTRRecord('ptrrecord', attribute=True, autofill=False, cli_name='ptr_rec', csv=True, multivalue=True, required=False)
+option: RRSIGRecord('rrsigrecord', attribute=True, autofill=False, cli_name='rrsig_rec', csv=True, multivalue=True, required=False)
+option: RPRecord('rprecord', attribute=True, autofill=False, cli_name='rp_rec', csv=True, multivalue=True, required=False)
+option: SIGRecord('sigrecord', attribute=True, autofill=False, cli_name='sig_rec', csv=True, multivalue=True, required=False)
+option: SPFRecord('spfrecord', attribute=True, autofill=False, cli_name='spf_rec', csv=True, multivalue=True, required=False)
+option: SRVRecord('srvrecord', attribute=True, autofill=False, cli_name='srv_rec', csv=True, multivalue=True, required=False)
+option: SSHFPRecord('sshfprecord', attribute=True, autofill=False, cli_name='sshfp_rec', csv=True, multivalue=True, required=False)
+option: TARecord('tarecord', attribute=True, autofill=False, cli_name='ta_rec', csv=True, multivalue=True, required=False)
+option: TKEYRecord('tkeyrecord', attribute=True, autofill=False, cli_name='tkey_rec', csv=True, multivalue=True, required=False)
+option: TSIGRecord('tsigrecord', attribute=True, autofill=False, cli_name='tsig_rec', csv=True, multivalue=True, required=False)
+option: TXTRecord('txtrecord', attribute=True, autofill=False, cli_name='txt_rec', csv=True, multivalue=True, required=False)
option: Flag('del_all', autofill=True, default=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('version?', exclude='webui')
-option: Str('arecord*', attribute=True, cli_name='a_rec', csv=True)
-option: Str('aaaarecord*', attribute=True, cli_name='aaaa_rec', csv=True)
-option: Str('a6record*', attribute=True, cli_name='a6_rec', csv=True)
-option: Str('afsdbrecord*', attribute=True, cli_name='afsdb_rec', csv=True)
-option: Str('aplrecord*', attribute=True, cli_name='apl_rec', csv=True)
-option: Str('certrecord*', attribute=True, cli_name='cert_rec', csv=True)
-option: Str('cnamerecord*', attribute=True, cli_name='cname_rec', csv=True)
-option: Str('dhcidrecord*', attribute=True, cli_name='dhcid_rec', csv=True)
-option: Str('dlvrecord*', attribute=True, cli_name='dlv_rec', csv=True)
-option: Str('dnamerecord*', attribute=True, cli_name='dname_rec', csv=True)
-option: Str('dnskeyrecord*', attribute=True, cli_name='dnskey_rec', csv=True)
-option: Str('dsrecord*', attribute=True, cli_name='ds_rec', csv=True)
-option: Str('hiprecord*', attribute=True, cli_name='hip_rec', csv=True)
-option: Str('ipseckeyrecord*', attribute=True, cli_name='ipseckey_rec', csv=True)
-option: Str('keyrecord*', attribute=True, cli_name='key_rec', csv=True)
-option: Str('kxrecord*', attribute=True, cli_name='kx_rec', csv=True)
-option: Str('locrecord*', attribute=True, cli_name='loc_rec', csv=True)
-option: Str('mxrecord*', attribute=True, cli_name='mx_rec', csv=True)
-option: Str('naptrrecord*', attribute=True, cli_name='naptr_rec', csv=True)
-option: Str('nsrecord*', attribute=True, cli_name='ns_rec', csv=True)
-option: Str('nsecrecord*', attribute=True, cli_name='nsec_rec', csv=True)
-option: Str('nsec3record*', attribute=True, cli_name='nsec3_rec', csv=True)
-option: Str('nsec3paramrecord*', attribute=True, cli_name='nsec3param_rec', csv=True)
-option: Str('ptrrecord*', attribute=True, cli_name='ptr_rec', csv=True)
-option: Str('rrsigrecord*', attribute=True, cli_name='rrsig_rec', csv=True)
-option: Str('rprecord*', attribute=True, cli_name='rp_rec', csv=True)
-option: Str('sigrecord*', attribute=True, cli_name='sig_rec', csv=True)
-option: Str('spfrecord*', attribute=True, cli_name='spf_rec', csv=True)
-option: Str('srvrecord*', attribute=True, cli_name='srv_rec', csv=True)
-option: Str('sshfprecord*', attribute=True, cli_name='sshfp_rec', csv=True)
-option: Str('tarecord*', attribute=True, cli_name='ta_rec', csv=True)
-option: Str('tkeyrecord*', attribute=True, cli_name='tkey_rec', csv=True)
-option: Str('tsigrecord*', attribute=True, cli_name='tsig_rec', csv=True)
-option: Str('txtrecord*', attribute=True, cli_name='txt_rec', csv=True)
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)
@@ -760,91 +718,97 @@ 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, query=True, required=False)
+option: AAAARecord('aaaarecord', attribute=True, autofill=False, cli_name='aaaa_rec', csv=True, multivalue=True, query=True, required=False)
+option: A6Record('a6record', attribute=True, autofill=False, cli_name='a6_rec', csv=True, multivalue=True, query=True, required=False)
+option: AFSDBRecord('afsdbrecord', attribute=True, autofill=False, cli_name='afsdb_rec', csv=True, multivalue=True, query=True, required=False)
+option: APLRecord('aplrecord', attribute=True, autofill=False, cli_name='apl_rec', csv=True, multivalue=True, query=True, required=False)
+option: CERTRecord('certrecord', attribute=True, autofill=False, cli_name='cert_rec', csv=True, multivalue=True, query=True, required=False)
+option: CNAMERecord('cnamerecord', attribute=True, autofill=False, cli_name='cname_rec', csv=True, multivalue=True, query=True, required=False)
+option: DHCIDRecord('dhcidrecord', attribute=True, autofill=False, cli_name='dhcid_rec', csv=True, multivalue=True, query=True, required=False)
+option: DLVRecord('dlvrecord', attribute=True, autofill=False, cli_name='dlv_rec', csv=True, multivalue=True, query=True, required=False)
+option: DNAMERecord('dnamerecord', attribute=True, autofill=False, cli_name='dname_rec', csv=True, multivalue=True, query=True, required=False)
+option: DNSKEYRecord('dnskeyrecord', attribute=True, autofill=False, cli_name='dnskey_rec', csv=True, multivalue=True, query=True, required=False)
+option: DSRecord('dsrecord', attribute=True, autofill=False, cli_name='ds_rec', csv=True, multivalue=True, query=True, required=False)
+option: HIPRecord('hiprecord', attribute=True, autofill=False, cli_name='hip_rec', csv=True, multivalue=True, query=True, required=False)
+option: IPSECKEYRecord('ipseckeyrecord', attribute=True, autofill=False, cli_name='ipseckey_rec', csv=True, multivalue=True, query=True, required=False)
+option: KEYRecord('keyrecord', attribute=True, autofill=False, cli_name='key_rec', csv=True, multivalue=True, query=True, required=False)
+option: KXRecord('kxrecord', attribute=True, autofill=False, cli_name='kx_rec', csv=True, multivalue=True, query=True, required=False)
+option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, query=True, required=False)
+option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, query=True, required=False)
+option: NAPTRRecord('naptrrecord', attribute=True, autofill=False, cli_name='naptr_rec', csv=True, multivalue=True, query=True, required=False)
+option: NSRecord('nsrecord', attribute=True, autofill=False, cli_name='ns_rec', csv=True, multivalue=True, query=True, required=False)
+option: NSECRecord('nsecrecord', attribute=True, autofill=False, cli_name='nsec_rec', csv=True, multivalue=True, query=True, required=False)
+option: NSEC3Record('nsec3record', attribute=True, autofill=False, cli_name='nsec3_rec', csv=True, multivalue=True, query=True, required=False)
+option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, autofill=False, cli_name='nsec3param_rec', csv=True, multivalue=True, query=True, required=False)
+option: PTRRecord('ptrrecord', attribute=True, autofill=False, cli_name='ptr_rec', csv=True, multivalue=True, query=True, required=False)
+option: RRSIGRecord('rrsigrecord', attribute=True, autofill=False, cli_name='rrsig_rec', csv=True, multivalue=True, query=True, required=False)
+option: RPRecord('rprecord', attribute=True, autofill=False, cli_name='rp_rec', csv=True, multivalue=True, query=True, required=False)
+option: SIGRecord('sigrecord', attribute=True, autofill=False, cli_name='sig_rec', csv=True, multivalue=True, query=True, required=False)
+option: SPFRecord('spfrecord', attribute=True, autofill=False, cli_name='spf_rec', csv=True, multivalue=True, query=True, required=False)
+option: SRVRecord('srvrecord', attribute=True, autofill=False, cli_name='srv_rec', csv=True, multivalue=True, query=True, required=False)
+option: SSHFPRecord('sshfprecord', attribute=True, autofill=False, cli_name='sshfp_rec', csv=True, multivalue=True, query=True, required=False)
+option: TARecord('tarecord', attribute=True, autofill=False, cli_name='ta_rec', csv=True, multivalue=True, query=True, required=False)
+option: TKEYRecord('tkeyrecord', attribute=True, autofill=False, cli_name='tkey_rec', csv=True, multivalue=True, query=True, required=False)
+option: TSIGRecord('tsigrecord', attribute=True, autofill=False, cli_name='tsig_rec', csv=True, multivalue=True, query=True, required=False)
+option: TXTRecord('txtrecord', attribute=True, autofill=False, cli_name='txt_rec', csv=True, multivalue=True, query=True, required=False)
option: Int('timelimit?', autofill=False, minvalue=0)
option: Int('sizelimit?', autofill=False, minvalue=0)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('version?', exclude='webui')
option: Flag('pkey_only?', autofill=True, default=False)
-option: Str('arecord', attribute=True, cli_name='a_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('aaaarecord', attribute=True, cli_name='aaaa_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('a6record', attribute=True, cli_name='a6_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('afsdbrecord', attribute=True, cli_name='afsdb_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('aplrecord', attribute=True, cli_name='apl_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('certrecord', attribute=True, cli_name='cert_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('cnamerecord', attribute=True, cli_name='cname_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('dhcidrecord', attribute=True, cli_name='dhcid_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('dlvrecord', attribute=True, cli_name='dlv_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('dnamerecord', attribute=True, cli_name='dname_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('dnskeyrecord', attribute=True, cli_name='dnskey_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('dsrecord', attribute=True, cli_name='ds_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('hiprecord', attribute=True, cli_name='hip_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('ipseckeyrecord', attribute=True, cli_name='ipseckey_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('keyrecord', attribute=True, cli_name='key_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('kxrecord', attribute=True, cli_name='kx_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('locrecord', attribute=True, cli_name='loc_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('mxrecord', attribute=True, cli_name='mx_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('naptrrecord', attribute=True, cli_name='naptr_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('nsrecord', attribute=True, cli_name='ns_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('nsecrecord', attribute=True, cli_name='nsec_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('nsec3record', attribute=True, cli_name='nsec3_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('nsec3paramrecord', attribute=True, cli_name='nsec3param_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('ptrrecord', attribute=True, cli_name='ptr_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('rrsigrecord', attribute=True, cli_name='rrsig_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('rprecord', attribute=True, cli_name='rp_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('sigrecord', attribute=True, cli_name='sig_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('spfrecord', attribute=True, cli_name='spf_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('srvrecord', attribute=True, cli_name='srv_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('sshfprecord', attribute=True, cli_name='sshfp_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('tarecord', attribute=True, cli_name='ta_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('tkeyrecord', attribute=True, cli_name='tkey_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('tsigrecord', attribute=True, cli_name='tsig_rec', csv=True, multivalue=True, query=True, required=False)
-option: Str('txtrecord', attribute=True, cli_name='txt_rec', csv=True, multivalue=True, query=True, required=False)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
output: Output('count', <type 'int'>, None)
output: Output('truncated', <type 'bool'>, None)
command: dnsrecord_mod
-args: 2,37,3
+args: 2,43,3
arg: Str('dnszoneidnsname', cli_name='dnszone', query=True, required=True)
arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Int('dnsttl', attribute=True, autofill=False, cli_name='ttl', multivalue=False, required=False)
+option: StrEnum('dnsclass', attribute=True, autofill=False, cli_name='class', multivalue=False, 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, required=False)
+option: AAAARecord('aaaarecord', attribute=True, autofill=False, cli_name='aaaa_rec', csv=True, multivalue=True, required=False)
+option: A6Record('a6record', attribute=True, autofill=False, cli_name='a6_rec', csv=True, multivalue=True, required=False)
+option: AFSDBRecord('afsdbrecord', attribute=True, autofill=False, cli_name='afsdb_rec', csv=True, multivalue=True, required=False)
+option: APLRecord('aplrecord', attribute=True, autofill=False, cli_name='apl_rec', csv=True, multivalue=True, required=False)
+option: CERTRecord('certrecord', attribute=True, autofill=False, cli_name='cert_rec', csv=True, multivalue=True, required=False)
+option: CNAMERecord('cnamerecord', attribute=True, autofill=False, cli_name='cname_rec', csv=True, multivalue=True, required=False)
+option: DHCIDRecord('dhcidrecord', attribute=True, autofill=False, cli_name='dhcid_rec', csv=True, multivalue=True, required=False)
+option: DLVRecord('dlvrecord', attribute=True, autofill=False, cli_name='dlv_rec', csv=True, multivalue=True, required=False)
+option: DNAMERecord('dnamerecord', attribute=True, autofill=False, cli_name='dname_rec', csv=True, multivalue=True, required=False)
+option: DNSKEYRecord('dnskeyrecord', attribute=True, autofill=False, cli_name='dnskey_rec', csv=True, multivalue=True, required=False)
+option: DSRecord('dsrecord', attribute=True, autofill=False, cli_name='ds_rec', csv=True, multivalue=True, required=False)
+option: HIPRecord('hiprecord', attribute=True, autofill=False, cli_name='hip_rec', csv=True, multivalue=True, required=False)
+option: IPSECKEYRecord('ipseckeyrecord', attribute=True, autofill=False, cli_name='ipseckey_rec', csv=True, multivalue=True, required=False)
+option: KEYRecord('keyrecord', attribute=True, autofill=False, cli_name='key_rec', csv=True, multivalue=True, required=False)
+option: KXRecord('kxrecord', attribute=True, autofill=False, cli_name='kx_rec', csv=True, multivalue=True, required=False)
+option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, required=False)
+option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, required=False)
+option: NAPTRRecord('naptrrecord', attribute=True, autofill=False, cli_name='naptr_rec', csv=True, multivalue=True, required=False)
+option: NSRecord('nsrecord', attribute=True, autofill=False, cli_name='ns_rec', csv=True, multivalue=True, required=False)
+option: NSECRecord('nsecrecord', attribute=True, autofill=False, cli_name='nsec_rec', csv=True, multivalue=True, required=False)
+option: NSEC3Record('nsec3record', attribute=True, autofill=False, cli_name='nsec3_rec', csv=True, multivalue=True, required=False)
+option: NSEC3PARAMRecord('nsec3paramrecord', attribute=True, autofill=False, cli_name='nsec3param_rec', csv=True, multivalue=True, required=False)
+option: PTRRecord('ptrrecord', attribute=True, autofill=False, cli_name='ptr_rec', csv=True, multivalue=True, required=False)
+option: RRSIGRecord('rrsigrecord', attribute=True, autofill=False, cli_name='rrsig_rec', csv=True, multivalue=True, required=False)
+option: RPRecord('rprecord', attribute=True, autofill=False, cli_name='rp_rec', csv=True, multivalue=True, required=False)
+option: SIGRecord('sigrecord', attribute=True, autofill=False, cli_name='sig_rec', csv=True, multivalue=True, required=False)
+option: SPFRecord('spfrecord', attribute=True, autofill=False, cli_name='spf_rec', csv=True, multivalue=True, required=False)
+option: SRVRecord('srvrecord', attribute=True, autofill=False, cli_name='srv_rec', csv=True, multivalue=True, required=False)
+option: SSHFPRecord('sshfprecord', attribute=True, autofill=False, cli_name='sshfp_rec', csv=True, multivalue=True, required=False)
+option: TARecord('tarecord', attribute=True, autofill=False, cli_name='ta_rec', csv=True, multivalue=True, required=False)
+option: TKEYRecord('tkeyrecord', attribute=True, autofill=False, cli_name='tkey_rec', csv=True, multivalue=True, required=False)
+option: TSIGRecord('tsigrecord', attribute=True, autofill=False, cli_name='tsig_rec', csv=True, multivalue=True, required=False)
+option: TXTRecord('txtrecord', attribute=True, autofill=False, cli_name='txt_rec', csv=True, multivalue=True, required=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Flag('rights', autofill=True, default=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('version?', exclude='webui')
-option: Str('arecord*', attribute=True, cli_name='a_rec', csv=True)
-option: Str('aaaarecord*', attribute=True, cli_name='aaaa_rec', csv=True)
-option: Str('a6record*', attribute=True, cli_name='a6_rec', csv=True)
-option: Str('afsdbrecord*', attribute=True, cli_name='afsdb_rec', csv=True)
-option: Str('aplrecord*', attribute=True, cli_name='apl_rec', csv=True)
-option: Str('certrecord*', attribute=True, cli_name='cert_rec', csv=True)
-option: Str('cnamerecord*', attribute=True, cli_name='cname_rec', csv=True)
-option: Str('dhcidrecord*', attribute=True, cli_name='dhcid_rec', csv=True)
-option: Str('dlvrecord*', attribute=True, cli_name='dlv_rec', csv=True)
-option: Str('dnamerecord*', attribute=True, cli_name='dname_rec', csv=True)
-option: Str('dnskeyrecord*', attribute=True, cli_name='dnskey_rec', csv=True)
-option: Str('dsrecord*', attribute=True, cli_name='ds_rec', csv=True)
-option: Str('hiprecord*', attribute=True, cli_name='hip_rec', csv=True)
-option: Str('ipseckeyrecord*', attribute=True, cli_name='ipseckey_rec', csv=True)
-option: Str('keyrecord*', attribute=True, cli_name='key_rec', csv=True)
-option: Str('kxrecord*', attribute=True, cli_name='kx_rec', csv=True)
-option: Str('locrecord*', attribute=True, cli_name='loc_rec', csv=True)
-option: Str('mxrecord*', attribute=True, cli_name='mx_rec', csv=True)
-option: Str('naptrrecord*', attribute=True, cli_name='naptr_rec', csv=True)
-option: Str('nsrecord*', attribute=True, cli_name='ns_rec', csv=True)
-option: Str('nsecrecord*', attribute=True, cli_name='nsec_rec', csv=True)
-option: Str('nsec3record*', attribute=True, cli_name='nsec3_rec', csv=True)
-option: Str('nsec3paramrecord*', attribute=True, cli_name='nsec3param_rec', csv=True)
-option: Str('ptrrecord*', attribute=True, cli_name='ptr_rec', csv=True)
-option: Str('rrsigrecord*', attribute=True, cli_name='rrsig_rec', csv=True)
-option: Str('rprecord*', attribute=True, cli_name='rp_rec', csv=True)
-option: Str('sigrecord*', attribute=True, cli_name='sig_rec', csv=True)
-option: Str('spfrecord*', attribute=True, cli_name='spf_rec', csv=True)
-option: Str('srvrecord*', attribute=True, cli_name='srv_rec', csv=True)
-option: Str('sshfprecord*', attribute=True, cli_name='sshfp_rec', csv=True)
-option: Str('tarecord*', attribute=True, cli_name='ta_rec', csv=True)
-option: Str('tkeyrecord*', attribute=True, cli_name='tkey_rec', csv=True)
-option: Str('tsigrecord*', attribute=True, cli_name='tsig_rec', csv=True)
-option: Str('txtrecord*', attribute=True, cli_name='txt_rec', csv=True)
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)
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index d4f91af74..33856c276 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -1,6 +1,6 @@
# Authors:
-# Pavel Zuna <pzuna@redhat.com>
# Martin Kosek <mkosek@redhat.com>
+# Pavel Zuna <pzuna@redhat.com>
#
# Copyright (C) 2010 Red Hat
# see file 'COPYING' for use and warranty information
@@ -22,9 +22,10 @@ import netaddr
import time
import re
+from ipalib.request import context
from ipalib import api, errors, output
from ipalib import Command
-from ipalib import Flag, Bool, Int, Str, StrEnum
+from ipalib.parameters import Flag, Bool, Int, Float, Str, StrEnum
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
from ipalib.util import validate_zonemgr, normalize_zonemgr, validate_hostname
@@ -166,13 +167,24 @@ def _reverse_zone_name(netstr):
else:
return None
-def _validate_ipaddr(ugettext, ipaddr):
+def _validate_ipaddr(ugettext, ipaddr, ip_version=None):
try:
ip = netaddr.IPAddress(ipaddr)
+
+ if ip_version is not None:
+ if ip.version != ip_version:
+ return _('invalid IP address version (is %(value)d, must be %(required_value)d)!') \
+ % dict(value=ip.version, required_value=ip_version)
except (netaddr.AddrFormatError, ValueError):
return _('invalid IP address format')
return None
+def _validate_ip4addr(ugettext, ipaddr):
+ return _validate_ipaddr(ugettext, ipaddr, 4)
+
+def _validate_ip6addr(ugettext, ipaddr):
+ return _validate_ipaddr(ugettext, ipaddr, 6)
+
def _validate_ipnet(ugettext, ipnet):
try:
net = netaddr.IPNetwork(ipnet)
@@ -180,191 +192,401 @@ def _validate_ipnet(ugettext, ipnet):
return _('invalid IP network format')
return None
-def _validate_srv(ugettext, srv):
- """see RFC 2782"""
- try:
- prio, weight, port, host = srv.split()
- except ValueError:
- return _('format must be specified as "priority weight port target"')
-
- try:
- prio = int(prio)
- weight = int(weight)
- port = int(port)
- except ValueError:
- return _('format must be specified as "priority weight port target" '\
- '(see RFC 2782 for details)')
-
- return None
-
-def _validate_mx(ugettext, mx):
- """see RFC 1035"""
- try:
- prio, host = mx.split()
- except ValueError:
- return _('format must be specified as "priority mailserver" '\
- '(see RFC 1035 for details)')
-
- try:
- prio = int(prio)
- except ValueError:
- return _('the value of priority must be integer')
-
- if prio < 0 or prio > 65535:
- return _('the value of priority must be between 0 and 65535')
-
- return None
-
-def _validate_naptr(ugettext, naptr):
- """see RFC 2915"""
+def _domain_name_validator(ugettext, value):
try:
- order, pref, flags, svc, regexp, replacement = naptr.split()
- except ValueError:
- return _('format must be specified as "order preference flags service '\
- 'regexp replacement" (see RFC 2915 for details)')
-
- try:
- order = int(order)
- pref = int(pref)
- except ValueError:
- return _('order and preference must be integers')
-
- if order < 0 or order > 65535 or pref < 0 or pref > 65535:
- return _('the value of order and preference must be between 0 and 65535')
-
- flags = flags.replace('"','')
- flags = flags.replace('\'','')
- if len(flags) != 1:
- return _('flag must be a single character (quotation is allowed)')
- if flags.upper() not in "SAUP":
- return _('flag must be one of "S", "A", "U", or "P"')
+ # Allow domain name which is not fully qualified. These are supported
+ # in bind and then translated as <non-fqdn-name>.<domain>.
+ validate_hostname(value, check_fqdn=False)
+ except ValueError, e:
+ return _('invalid domain-name: %s') \
+ % unicode(e)
return None
-def _validate_afsdb(ugettext, afsdb):
- """see RFC 1183"""
- try:
- sub, host = afsdb.split()
- except ValueError:
- return _('format must be specified as "subtype hostname" (see RFC 1183 for details)')
-
+def _hostname_validator(ugettext, value):
try:
- sub = int(sub)
- except ValueError:
- return _('the value of subtype must be integer')
-
- if sub < 0 or sub > 65535:
- return _('the value of subtype must be between 0 and 65535')
+ validate_hostname(value)
+ except ValueError, e:
+ return _('invalid domain-name: %s') \
+ % unicode(e)
return None
-def _validate_cert(ugettext, cert):
- """see RFC 4398"""
- try:
- cert_type, key_tag, algorithm, certificate = cert.split()
- except ValueError:
- return _('format must be specified as "type key_tag algorithm certificate_or_crl" '\
- '(see RFC 4398 for details)')
+def _normalize_hostname(domain_name):
+ """Make it fully-qualified"""
+ if domain_name[-1] != '.':
+ return domain_name + '.'
+ else:
+ return domain_name
- try:
- cert_type = int(cert_type)
- key_tag = int(key_tag)
- algorithm = int(algorithm)
- except ValueError:
- return _('key_tag, algorithm and digest_type must be integers')
+class DNSRecord(Str):
+ parts = None
+ supported = True
+ # supported RR types: https://fedorahosted.org/bind-dyndb-ldap/browser/doc/schema
+
+ label_format = _("%s record")
+ part_label_format = "%s %s"
+ doc_format = _('Comma-separated list of raw %s records')
+ option_group_format = _('%s Record')
+ see_rfc_msg = _("(see RFC %s for details)")
+ part_name_format = "%s_part_%s"
+ cli_name_format = "%s_%s"
+ format_error_msg = None
+
+ kwargs = Str.kwargs + (
+ ('validatedns', bool, True),
+ ('normalizedns', bool, True),
+ )
- if cert_type < 0 or cert_type > 65535 or key_tag < 0 or key_tag > 65535:
- return _('the value of type and key_tag must be between 0 and 65535')
+ # should be replaced in subclasses
+ rrtype = None
+ rfc = None
+
+ def __init__(self, name=None, *rules, **kw):
+ if self.rrtype not in _record_types:
+ raise ValueError("Unknown RR type: %s. Must be one of %s" % \
+ (str(self.rrtype), ", ".join(_record_types)))
+ if not name:
+ name = "%srecord*" % self.rrtype.lower()
+ kw.setdefault('cli_name', '%s_rec' % self.rrtype.lower())
+ kw.setdefault('label', self.label_format % self.rrtype)
+ kw.setdefault('doc', self.doc_format % self.rrtype)
+ kw['csv'] = True
+
+ if not self.supported:
+ kw['flags'] = ('no_option',)
+
+ super(DNSRecord, self).__init__(name, *rules, **kw)
+
+ def _get_part_values(self, value):
+ values = value.split()
+ if len(values) != len(self.parts):
+ return None
+ return tuple(values)
+
+ def _convert_scalar(self, value, index=None):
+ if isinstance(value, (tuple, list)):
+ # convert parsed values to the string
+ if len(value) != len(self.parts):
+ raise errors.ConversionError(name=self.name, index=index,
+ error=_("Invalid number of parts!"))
+ return u" ".join(super(DNSRecord, self)._convert_scalar(v, index) \
+ for v in value if v is not None)
+ return super(DNSRecord, self)._convert_scalar(value, index)
+
+ def normalize(self, value):
+ if self.normalizedns: #pylint: disable=E1101
+ if isinstance(value, (tuple, list)):
+ value = tuple(
+ self._normalize_parts(v) for v in value \
+ if v is not None
+ )
+ elif value is not None:
+ value = (self._normalize_parts(value),)
+
+ return super(DNSRecord, self).normalize(value)
+
+ def _normalize_parts(self, value):
+ """
+ Normalize a DNS record value using normalizers for its parts.
+ """
+ if self.parts is None:
+ return value
+ try:
+ values = self._get_part_values(value)
+ if not values:
+ return value
- if algorithm < 0 or algorithm > 255:
- return _('the value of algorithm must be between 0 and 255')
+ new_values = [ part.normalize(values[part_id]) \
+ for part_id, part in enumerate(self.parts) ]
- return None
+ value = self._convert_scalar(new_values)
+ except Exception:
+ # cannot normalize, rather return original value than fail
+ pass
+ return value
-def _validate_cname(ugettext, cname):
- """see RFC 1035"""
- try:
- validate_hostname(cname)
- except ValueError, e:
- return _('format must be specified as "domain_name" (see RFC 1035 for details): %s') \
- % unicode(e)
+ def _rule_validatedns(self, _, value):
+ if not self.validatedns: #pylint: disable=E1101
+ return
- return None
+ if value is None:
+ return
-def _validate_dname(ugettext, dname):
- """see RFC 2672"""
- try:
- validate_hostname(dname)
- except ValueError, e:
- return _('format must be specified as "target" (see RFC 2672 for details): %s') \
- % unicode(e)
+ if not self.supported:
+ return _('DNS RR type "%s" is not supported by bind-dyndb-ldap plugin') \
+ % self.rrtype
- return None
+ if self.parts is None:
+ return
-def _validate_ds(ugettext, ds):
- """see RFC 4034"""
- try:
- key_tag, algorithm, digest_type, digest = ds.split()
- except ValueError:
- return _('format must be specified as "key_tag algorithm digest_type digest" '\
- '(see RFC 4034 for details)')
+ # validate record format
+ values = self._get_part_values(value)
+ if not values:
+ if not self.format_error_msg:
+ part_names = [part.name.upper() for part in self.parts]
+
+ if self.rfc:
+ see_rfc_msg = " " + self.see_rfc_msg % self.rfc
+ else:
+ see_rfc_msg = ""
+ return _('format must be specified as "%(format)s" %(rfcs)s') \
+ % dict(format=" ".join(part_names), rfcs=see_rfc_msg)
+ else:
+ return self.format_error_msg
+
+ # validate every part
+ for part_id, part in enumerate(self.parts):
+ val = part.normalize(values[part_id])
+ val = part.convert(val)
+ part.validate(val)
+ return None
- try:
- key_tag = int(key_tag)
- algorithm = int(algorithm)
- digest_type = int(digest_type)
- except ValueError:
- return _('key_tag, algorithm and digest_type must be integers')
+class ARecord(DNSRecord):
+ rrtype = 'A'
+ rfc = 1035
+ parts = (
+ Str('ip_address',
+ _validate_ip4addr,
+ label=_('IP Address'),
+ ),
+ )
- if key_tag < 0 or key_tag > 65535:
- return _('the value of flags must be between 0 and 65535')
+class A6Record(DNSRecord):
+ rrtype = 'A6'
+ rfc = 3226
+ parts = None # experimental rr type
+
+class AAAARecord(DNSRecord):
+ rrtype = 'AAAA'
+ rfc = 3596
+ parts = (
+ Str('ip_address',
+ _validate_ip6addr,
+ label=_('IP Address'),
+ ),
+ )
- if algorithm < 0 or algorithm > 255 or digest_type < 0 or digest_type > 255:
- return _('the value of algorithm and digest_type must be between 0 and 255')
+class AFSDBRecord(DNSRecord):
+ rrtype = 'AFSDB'
+ rfc = 1183
+ parts = (
+ Int('subtype?',
+ label=_('Subtype'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('hostname',
+ _domain_name_validator,
+ label=_('Hostname'),
+ ),
+ )
- return None
+class APLRecord(DNSRecord):
+ rrtype = 'APL'
+ rfc = 3123
+ supported = False
+
+class CERTRecord(DNSRecord):
+ rrtype = 'CERT'
+ rfc = 4398
+ parts = (
+ Int('type',
+ label=_('Certificate Type'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('key_tag',
+ label=_('Key Tag'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('algorithm',
+ label=_('Algorithm'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Str('certificate_or_crl',
+ label=_('Certificate/CRL'),
+ ),
+ )
-def _validate_key(ugettext, key):
- """see RFC 2535"""
- try:
- flags, protocol, algorithm, digest = key.split()
- except ValueError:
- return _('format must be specified as "flags protocol algorithm public_key" '\
- '(see RFC 2535 for details)')
+class CNAMERecord(DNSRecord):
+ rrtype = 'CNAME'
+ rfc = 1035
+ parts = (
+ Str('hostname',
+ _domain_name_validator,
+ label=_('Hostname'),
+ doc=_('A hostname which this alias hostname points to'),
+ ),
+ )
- try:
- flags = int(flags)
- protocol = int(protocol)
- algorithm = int(algorithm)
- except ValueError:
- return _('flags, protocol and algorithm must be integers')
+class DHCIDRecord(DNSRecord):
+ rrtype = 'DHCID'
+ rfc = 4701
+ supported = False
+
+class DLVRecord(DNSRecord):
+ rrtype = 'DLV'
+ rfc = 4431
+ supported = False
+
+class DNAMERecord(DNSRecord):
+ rrtype = 'DNAME'
+ rfc = 2672
+ parts = (
+ Str('target',
+ _domain_name_validator,
+ label=_('Target'),
+ ),
+ )
- if flags < 0 or flags > 65535:
- return _('the value of flags must be between 0 and 65535')
+class DNSKEYRecord(DNSRecord):
+ rrtype = 'DNSKEY'
+ rfc = 4034
+ supported = False
+
+class DSRecord(DNSRecord):
+ rrtype = 'DS'
+ rfc = 4034
+ parts = (
+ Int('key_tag',
+ label=_('Key Tag'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('algorithm',
+ label=_('Algorithm'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Int('digest_type',
+ label=_('Digest Type'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Str('digest',
+ label=_('Digest'),
+ ),
+ )
- if protocol < 0 or protocol > 255:
- return _('the value of protocol must be between 0 and 255')
+class HIPRecord(DNSRecord):
+ rrtype = 'HIP'
+ rfc = 5205
+ supported = False
+
+class KEYRecord(DNSRecord):
+ rrtype = 'KEY'
+ rfc = 2535
+ parts = (
+ Int('flags',
+ label=_('Flags'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('protocol',
+ label=_('Protocol'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Int('algorithm',
+ label=_('Algorithm'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Str('public_key',
+ label=_('Public Key'),
+ ),
+ )
- if algorithm < 0 or algorithm > 255:
- return _('the value of algorithm must be between 0 and 255')
+class IPSECKEYRecord(DNSRecord):
+ rrtype = 'IPSECKEY'
+ rfc = 4025
+ supported = False
+
+class KXRecord(DNSRecord):
+ rrtype = 'KX'
+ rfc = 2230
+ parts = (
+ Int('preference',
+ label=_('Preference'),
+ doc=_('Preference given to this exchanger. Lower values are more preferred'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('exchanger',
+ _domain_name_validator,
+ label=_('Exchanger'),
+ doc=_('A host willing to act as a key exchanger'),
+ ),
+ )
- return None
+class LOCRecord(DNSRecord):
+ rrtype = 'LOC'
+ rfc = 1876
+ parts = (
+ Int('lat_deg',
+ label=_('Degrees Latitude'),
+ minvalue=0,
+ maxvalue=90,
+ ),
+ Int('lat_min?',
+ label=_('Minutes Latitude'),
+ minvalue=0,
+ maxvalue=59,
+ ),
+ Float('lat_sec?',
+ label=_('Seconds Latitude'),
+ minvalue=0.0,
+ maxvalue=59.999,
+ ),
+ StrEnum('lat_dir',
+ label=_('Direction Latitude'),
+ values=(u'N', u'S',),
+ ),
+ Int('lon_deg',
+ label=_('Degrees Longtitude'),
+ minvalue=0,
+ maxvalue=180,
+ ),
+ Int('lon_min?',
+ label=_('Minutes Longtitude'),
+ minvalue=0,
+ maxvalue=59,
+ ),
+ Float('lon_sec?',
+ label=_('Seconds Longtitude'),
+ minvalue=0.0,
+ maxvalue=59.999,
+ ),
+ StrEnum('lon_dir',
+ label=_('Direction Longtitude'),
+ values=(u'E', u'W',),
+ ),
+ Float('altitude',
+ label=_('Altitude'),
+ minvalue=-100000.00,
+ maxvalue=42849672.95,
+ ),
+ Float('size?',
+ label=_('Size'),
+ minvalue=0.0,
+ maxvalue=90000000.00,
+ ),
+ Float('h_precision?',
+ label=_('Horizontal Precision'),
+ minvalue=0.0,
+ maxvalue=90000000.00,
+ ),
+ Float('v_precision?',
+ label=_('Vertical Precision'),
+ minvalue=0.0,
+ maxvalue=90000000.00,
+ ),
+ )
-def _validate_loc(ugettext, loc):
- """see RFC 1876"""
- regex = re.compile(\
- r'(?P<d1>\d{1,2}\s+)(?P<m1>\d{1,2}\s+)?(?P<s1>\d{1,2}\.?\d{1,3}?\s+)'\
- r'?[N|S]\s+'\
- r'(?P<d2>\d{1,2}\s+)(?P<m2>\d{1,2}\s+)?(?P<s2>\d{1,2}\.?\d{1,3}?\s+)'\
- r'?[W|E]\s+'\
- r'(?P<alt>-?\d{1,8}\.?\d{1,2}?)m?\s*'\
- r'(?P<siz>\d{1,8}\.?\d{1,2}?)?m?\s*'\
- r'(?P<hp>\d{1,8}\.?\d{1,2}?)?m?\s*(?P<vp>\d{1,8}\.?\d{1,2}?)?m?\s*$')
-
- m = regex.match(loc)
-
- if m is None:
- return _("""format must be specified as
+ format_error_msg = _("""format must be specified as
"d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]] {"E"|"W"} alt["m"] [siz["m"] [hp["m"] [vp["m"]]]]"
where:
d1: [0 .. 90] (degrees latitude)
@@ -375,208 +597,308 @@ def _validate_loc(ugettext, loc):
siz, hp, vp: [0 .. 90000000.00] (size/precision in meters)
See RFC 1876 for details""")
- attrs = {}
- for attr in ('d1', 'd2', 'm1', 'm2'):
- if m.group(attr) is not None:
- try:
- attrs[attr] = int(m.group(attr))
- except ValueError:
- return _('%s must be integer') % attr
-
- for attr in ('s1', 's2', 'alt', 'siz', 'hp', 'vp'):
- if m.group(attr) is not None:
- try:
- attrs[attr] = float(m.group(attr))
- except ValueError:
- return _('%s must be float') % attr
-
- if attrs.get('d1', 0) > 90 or attrs.get('d2', 0) > 90:
- return _(u'd1 and d2 must be between 0 and 90')
-
- if attrs.get('m1', 0) >= 60 or attrs.get('m2', 0) >= 60 or \
- attrs.get('s1', 0) >= 60 or attrs.get('s2', 0) >= 60:
- return _('m1, m2, s1 and s2 must be between 0 and 59.999')
-
- if attrs.get('alt', 0) < -100000.00 or attrs.get('alt', 0) > 42849672.95:
- return _('alt must be between -100000.00 and 42849672.95')
-
- if attrs.get('siz', 0) > 90000000.00 or attrs.get('hp', 0) > 90000000.00 or \
- attrs.get('vp', 0) > 90000000.00:
- return _('siz, hp and vp must be between 0 and 90000000.00')
-
- return None
+ def _get_part_values(self, value):
+ regex = re.compile(\
+ r'(?P<d1>\d{1,2}\s+)(?P<m1>\d{1,2}\s+)?(?P<s1>\d{1,2}\.?\d{1,3}?\s+)?'\
+ r'(?P<dir1>[N|S])\s+'\
+ r'(?P<d2>\d{1,3}\s+)(?P<m2>\d{1,2}\s+)?(?P<s2>\d{1,2}\.?\d{1,3}?\s+)?'\
+ r'(?P<dir2>[W|E])\s+'\
+ r'(?P<alt>-?\d{1,8}\.?\d{1,2}?)m?\s*'\
+ r'(?P<siz>\d{1,8}\.?\d{1,2}?)?m?\s*'\
+ r'(?P<hp>\d{1,8}\.?\d{1,2}?)?m?\s*(?P<vp>\d{1,8}\.?\d{1,2}?)?m?\s*$')
+
+ m = regex.match(value)
+
+ if m is None:
+ return None
+
+ return tuple(x.strip() if x is not None else x for x in m.groups())
+
+class MXRecord(DNSRecord):
+ rrtype = 'MX'
+ rfc = 1035
+ parts = (
+ Int('preference',
+ label=_('Preference'),
+ doc=_('Preference given to this exchanger. Lower values are more preferred'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('exchanger',
+ _domain_name_validator,
+ label=_('Exchanger'),
+ doc=_('A host willing to act as a mail exchanger'),
+ ),
+ )
-def _validate_ns(ugettext, ns):
- """see RFC 1035"""
- try:
- ns, = ns.split()
- except ValueError:
- return _('format must be specified as "domain_name" (see RFC 1035 for details)')
+class NSRecord(DNSRecord):
+ rrtype = 'NS'
+ rfc = 1035
- return None
+ parts = (
+ Str('hostname',
+ _domain_name_validator,
+ label=_('Hostname'),
+ ),
+ )
-def _validate_nsec(ugettext, nsec):
- """see RFC 4034"""
- fields = nsec.split()
+class NSECRecord(DNSRecord):
+ rrtype = 'NSEC'
+ rfc = 4034
+ format_error_msg = _('format must be specified as "NEXT TYPE1 '\
+ '[TYPE2 [TYPE3 [...]]]" (see RFC 4034 for details)')
+ _allowed_types = (u'SOA',) + _record_types
+
+ parts = (
+ Str('next',
+ _domain_name_validator,
+ label=_('Next Domain Name'),
+ ),
+ StrEnum('types',
+ label=_('Type Map'),
+ multivalue=True,
+ values=_allowed_types,
+ ),
+ )
- if len(fields) < 2:
- return _('format must be specified as "next_domain_name type1 '\
- '[type2 [type3 [...]]]" (see RFC 4034 for details)')
+ def _get_part_values(self, value):
+ values = value.split()
- allowed_types = (u'SOA',) + _record_types
- for i in range(1, len(fields)):
- sig_type = fields[i]
- if sig_type not in allowed_types:
- return _('type must be one of ' + u', '.join(allowed_types))
+ if len(values) < 2:
+ return None
- return None
+ return (values[0], tuple(values[1:]))
-def _validate_kx(ugettext, kx):
- """see RFC 2230"""
- try:
- preference, exchanger = kx.split()
- except ValueError:
- return _('format must be specified as "preference exchanger" '\
- '(see RFC 2230 for details)')
+class NSEC3Record(DNSRecord):
+ rrtype = 'NSEC3'
+ rfc = 5155
+ supported = False
- try:
- preference = int(preference)
- except ValueError:
- return _(u'the value of preference must be integer')
+class NSEC3PARAMRecord(DNSRecord):
+ rrtype = 'NSEC3PARAM'
+ rfc = 5155
+ supported = False
- if preference < 0 or preference > 65535:
- return _('the value of preference must be between 0 and 65535')
+def _validate_naptr_flags(ugettext, flags):
+ allowed_flags = u'SAUP'
+ flags = flags.replace('"','').replace('\'','')
- return None
+ for flag in flags:
+ if flag not in allowed_flags:
+ return _('flags must be one of "S", "A", "U", or "P"')
-def _validate_ptr(ugettext, ptr):
- """see RFC 1035"""
- try:
- validate_hostname(ptr)
- except ValueError, e:
- return _('format must be specified as "domain_name" (see RFC 1035 for details): %s') \
- % unicode(e)
+class NAPTRRecord(DNSRecord):
+ rrtype = 'NAPTR'
+ rfc = 2915
- return None
+ parts = (
+ Int('order',
+ label=_('Order'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('preference',
+ label=_('Preference'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('flags',
+ _validate_naptr_flags,
+ label=_('Flags'),
+ normalizer=lambda x:x.upper()
+ ),
+ Str('service',
+ label=_('Service'),
+ ),
+ Str('regexp',
+ label=_('Regular Expression'),
+ ),
+ Str('replacement',
+ label=_('Replacement'),
+ ),
+ )
-def _validate_sig(ugettext, sig):
- """see RFCs 2535, 4034"""
- try:
- sig_type, algorithm, labels, ttl, sig_expiration, \
- sig_inception, tag, signer, signature = sig.split()
- except ValueError:
- return _('format must be specified as "type_covered algorithm labels original_ttl ' \
- 'signature_expiration signature_inception key_tag signers_name signature" '\
- '(see RFC 2535, 4034 for details)')
+class PTRRecord(DNSRecord):
+ rrtype = 'PTR'
+ rfc = 1035
+ parts = (
+ Str('hostname',
+ _hostname_validator,
+ normalizer=_normalize_hostname,
+ label=_('Hostname'),
+ doc=_('The hostname this reverse record points to'),
+ ),
+ )
- allowed_types = [x for x in _record_types if x != u'SIG']
- if sig_type not in allowed_types:
- return _('type_covered must be one of ' + u', '.join(allowed_types))
+class RPRecord(DNSRecord):
+ rrtype = 'RP'
+ rfc = 1183
+ supported = False
+
+class SRVRecord(DNSRecord):
+ rrtype = 'SRV'
+ rfc = 2782
+ parts = (
+ Int('priority',
+ label=_('Priority'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('weight',
+ label=_('Weight'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Int('port',
+ label=_('Port'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('target',
+ label=_('Target'),
+ doc=_('The domain name of the target host or \'.\' if the service is decidedly not available at this domain'),
+ ),
+ )
+def _sig_time_validator(ugettext, value):
+ time_format = "%Y%m%d%H%M%S"
try:
- algorithm = int(algorithm)
- labels = int(labels)
- ttl = int(ttl)
- tag = int(tag)
+ time.strptime(value, time_format)
except ValueError:
- return _('algorithm, labels, original_ttl and key_tag must be integers')
-
- try:
- time_format = "%Y%m%d%H%M%S"
- sig_inception = time.strptime(sig_inception, time_format)
- sig_expiration = time.strptime(sig_expiration, time_format)
- except ValueError, e:
- return _('signature_expiration and signature_inception must follow time ' \
- 'format "YYYYMMDDHHMMSS"')
-
- if algorithm < 0 or algorithm > 255 or labels < 0 or labels > 255:
- return _('the value of algorithm and labels must be between 0 and 255')
+ return _('the value does not follow "YYYYMMDDHHMMSS" time format')
- if ttl < 0 or ttl > 4294967295:
- return _('the value of original_ttl must be between 0 and 4294967295')
- if tag < 0 or tag > 65535:
- return _('the value of tag must be between 0 and 65535')
+class SIGRecord(DNSRecord):
+ rrtype = 'SIG'
+ rfc = 2535
+ _allowed_types = tuple([u'SOA'] + [x for x in _record_types if x != u'SIG'])
- return None
-
-def _validate_sshfp(ugettext, sshfp):
- """see RFCs 4255"""
- try:
- algorithm, fp_type, fingerprint = sshfp.split()
- except ValueError:
- return _('format must be specified as "algorithm fp_type fingerprint" '\
- '(see RFC 4255 for details)')
-
- try:
- algorithm = int(algorithm)
- fp_type = int(fp_type)
- except ValueError:
- return _('algorithm and fp_type must be integers')
-
- if algorithm < 0 or algorithm > 255 or fp_type < 0 or fp_type > 255:
- return _('the value of algorithm and fp_type must be between 0 and 255')
+ parts = (
+ StrEnum('type_covered',
+ label=_('Type Covered'),
+ values=_allowed_types,
+ ),
+ Int('algorithm',
+ label=_('Algorithm'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Int('labels',
+ label=_('Labels'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Int('original_ttl',
+ label=_('Original TTL'),
+ minvalue=0,
+ maxvalue=4294967295,
+ ),
+ Str('signature_expiration',
+ _sig_time_validator,
+ label=_('Signature Expiration'),
+ ),
+ Str('signature_inception',
+ _sig_time_validator,
+ label=_('Signature Inception'),
+ ),
+ Int('key_tag',
+ label=_('Key Tag'),
+ minvalue=0,
+ maxvalue=65535,
+ ),
+ Str('signers_name',
+ label=_('Signer\'s Name'),
+ ),
+ Str('signature',
+ label=_('Signature'),
+ ),
+ )
- return None
+class SPFRecord(DNSRecord):
+ rrtype = 'SPF'
+ rfc = 4408
+ supported = False
+
+class RRSIGRecord(SIGRecord):
+ rrtype = 'RRSIG'
+ rfc = 4034
+
+class SSHFPRecord(DNSRecord):
+ rrtype = 'SSHFP'
+ rfc = 4255
+ parts = (
+ Int('algorithm',
+ label=_('Algorithm'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Int('fp_type',
+ label=_('Fingerprint Type'),
+ minvalue=0,
+ maxvalue=255,
+ ),
+ Str('fingerprint',
+ label=_('Fingerprint'),
+ ),
+ )
-def _validate_unsupported(ugettext, val):
- """
- See https://fedorahosted.org/bind-dyndb-ldap/browser/doc/schema for a
- list of supported records in bind-dyndb-ldap plugin
- """
- return _('This DNS RR type is not supported by bind-dyndb-ldap plugin')
+class TARecord(DNSRecord):
+ rrtype = 'TA'
+ supported = False
+class TKEYRecord(DNSRecord):
+ rrtype = 'TKEY'
+ supported = False
-def _normalize_domain_name(domain_name):
- """Make it fully-qualified"""
- if domain_name[-1] != '.':
- return domain_name + '.'
- else:
- return domain_name
+class TSIGRecord(DNSRecord):
+ rrtype = 'TSIG'
+ supported = False
-# Not validated RR types:
-# - A6: downgraded to experimental state by RFC 3363, AAAA is preferred
-
-_record_validators = {
- u'A': _validate_ipaddr,
- u'AAAA': _validate_ipaddr,
- u'AFSDB': _validate_afsdb,
- u'APL': _validate_unsupported,
- u'CERT': _validate_cert,
- u'CNAME': _validate_cname,
- u'DHCID': _validate_unsupported,
- u'DLV': _validate_unsupported,
- u'DNAME': _validate_dname,
- u'DNSKEY': _validate_unsupported,
- u'DS': _validate_ds,
- u'HIP': _validate_unsupported,
- u'KEY': _validate_key,
- u'IPSECKEY': _validate_unsupported,
- u'KX': _validate_kx,
- u'LOC': _validate_loc,
- u'MX': _validate_mx,
- u'NS': _validate_ns,
- u'NSEC': _validate_nsec,
- u'NSEC3': _validate_unsupported,
- u'NSEC3PARAM': _validate_unsupported,
- u'NAPTR': _validate_naptr,
- u'PTR': _validate_ptr,
- u'RP': _validate_unsupported,
- u'SRV': _validate_srv,
- u'SIG': _validate_sig,
- u'RRSIG': _validate_sig,
- u'SSHFP': _validate_sshfp,
- u'TA': _validate_unsupported,
- u'TKEY': _validate_unsupported,
- u'TSIG': _validate_unsupported,
-}
+class TXTRecord(DNSRecord):
+ rrtype = 'TXT'
+ rfc = 1035
+ parts = (
+ Str('data',
+ label=_('Text Data'),
+ ),
+ )
-_record_normalizers = {
- u'CNAME': _normalize_domain_name,
- u'DNAME': _normalize_domain_name,
- u'NS': _normalize_domain_name,
- u'PTR': _normalize_domain_name,
-}
+_dns_record_options = (
+ ARecord(),
+ AAAARecord(),
+ A6Record(),
+ AFSDBRecord(),
+ APLRecord(),
+ CERTRecord(),
+ CNAMERecord(),
+ DHCIDRecord(),
+ DLVRecord(),
+ DNAMERecord(),
+ DNSKEYRecord(),
+ DSRecord(),
+ HIPRecord(),
+ IPSECKEYRecord(),
+ KEYRecord(),
+ KXRecord(),
+ LOCRecord(),
+ MXRecord(),
+ NAPTRRecord(),
+ NSRecord(),
+ NSECRecord(),
+ NSEC3Record(),
+ NSEC3PARAMRecord(),
+ PTRRecord(),
+ RRSIGRecord(),
+ RPRecord(),
+ SIGRecord(),
+ SPFRecord(),
+ SRVRecord(),
+ SSHFPRecord(),
+ TARecord(),
+ TKEYRecord(),
+ TSIGRecord(),
+ TXTRecord(),
+)
# dictionary of valid reverse zone -> number of address components
_valid_reverse_zones = {
@@ -591,18 +913,6 @@ def zone_is_reverse(zone_name):
return False
-
-def has_cli_options(entry, no_option_msg, allow_empty_attrs=False):
- entry = dict((t, entry.get(t, [])) for t in _record_attributes)
- if allow_empty_attrs:
- numattr = len(entry)
- else:
- numattr = reduce(lambda x,y: x+y,
- map(lambda x: len(x), [ v for v in entry.values() if v is not None ]))
- if numattr == 0:
- raise errors.OptionError(no_option_msg)
- return entry
-
def is_ns_rec_resolvable(name):
try:
return api.Command['dns_resolve'](name)
@@ -841,7 +1151,7 @@ class dnszone_find(LDAPSearch):
if 'idnsname' not in options:
options['idnsname'] = self.obj.params['idnsname'].get_default(**options)
del options['name_from_ip']
- return super(dnszone_find, self).args_options_2_entry(self, *args, **options)
+ return super(dnszone_find, self).args_options_2_entry(*args, **options)
takes_options = LDAPSearch.takes_options + (
Flag('forward_only',
@@ -922,7 +1232,7 @@ class dnsrecord(LDAPObject):
object_name = _('DNS resource record')
object_name_plural = _('DNS resource records')
object_class = ['top', 'idnsrecord']
- default_attributes = _record_attributes + ['idnsname']
+ default_attributes = ['idnsname'] + _record_attributes
label = _('DNS Resource Records')
label_singular = _('DNS Resource Record')
@@ -945,7 +1255,7 @@ class dnsrecord(LDAPObject):
doc=_('DNS class'),
values=_record_classes,
),
- )
+ ) + _dns_record_options
def _nsrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
if options.get('force', False):
@@ -974,8 +1284,8 @@ class dnsrecord(LDAPObject):
ip_addr_comp_count = len(addr.split('.')) + len(zone.split('.'))
if ip_addr_comp_count != zone_len:
raise errors.ValidationError(name='cn',
- error=unicode(_('Reverse zone %s requires exactly %d IP address components, %d given')
- % (zone_name, zone_len, ip_addr_comp_count)))
+ error=unicode(_('Reverse zone %(name)s requires exactly %(count)d IP address components, %(user_count)d given')
+ % dict(name=zone_name, count=zone_len, user_count=ip_addr_comp_count)))
return dn
@@ -1016,47 +1326,38 @@ class dnsrecord(LDAPObject):
return dns_masters
-api.register(dnsrecord)
-
-
-class dnsrecord_cmd_w_record_options(Command):
- """
- Base class for DNS record commands with record options.
- """
- record_param_doc = 'comma-separated list of %s records'
+ def has_cli_options(self, options, no_option_msg, allow_empty_attrs=False):
+ if any(k in options for k in ('setattr', 'addattr', 'delattr')):
+ return
- def get_record_options(self):
- for t in _record_types:
- t = t.encode('utf-8')
- yield self.get_record_option(t)
+ has_options = False
+ for attr in options.keys():
+ if attr in self.params and not self.params[attr].primary_key:
+ if options[attr] or allow_empty_attrs:
+ has_options = True
+ break
- def record_options_2_entry(self, **options):
- entries = dict((t, options.get(t, [])) for t in _record_attributes)
- entries.update(dict((k, []) for (k,v) in entries.iteritems() if v == None ))
- return entries
+ if not has_options:
+ raise errors.OptionError(no_option_msg)
def get_record_option(self, rec_type):
- doc = self.record_param_doc % rec_type
- validator = _record_validators.get(rec_type)
- normalizer = _record_normalizers.get(rec_type)
- if validator:
- return Str(
- '%srecord*' % rec_type.lower(), validator, normalizer=normalizer,
- cli_name='%s_rec' % rec_type.lower(), doc=doc,
- label='%s record' % rec_type, csv=True, attribute=True
- )
+ name = '%srecord' % rec_type.lower()
+ if name in self.params:
+ return self.params[name]
else:
- return Str(
- '%srecord*' % rec_type.lower(), cli_name='%s_rec' % rec_type.lower(),
- normalizer=normalizer, doc=doc, label='%s record' % rec_type,
- csv=True, attribute=True
- )
+ 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)
def prompt_record_options(self, rec_type_list):
user_options = {}
# ask for all usual record types
for rec_type in rec_type_list:
rec_option = self.get_record_option(rec_type)
+ if rec_option is None:
+ continue
raw = self.Backend.textui.prompt(rec_option.label,optional=True)
rec_value = rec_option(raw)
if rec_value is not None:
@@ -1064,88 +1365,10 @@ class dnsrecord_cmd_w_record_options(Command):
return user_options
-
-class dnsrecord_mod_record(LDAPQuery, dnsrecord_cmd_w_record_options):
- """
- Base class for adding/removing records from DNS resource entries.
- """
- has_output = output.standard_entry
-
- def get_options(self):
- for option in super(dnsrecord_mod_record, self).get_options():
- yield option
- for option in self.get_record_options():
- yield option
-
- def execute(self, *keys, **options):
- ldap = self.obj.backend
-
- dn = self.obj.get_dn(*keys, **options)
-
- entry_attrs = self.record_options_2_entry(**options)
-
- dn = self.pre_callback(ldap, dn, entry_attrs, *keys, **options)
-
- try:
- (dn, old_entry_attrs) = ldap.get_entry(dn, entry_attrs.keys())
- except errors.NotFound:
- self.obj.handle_not_found(*keys)
-
- self.update_old_entry_callback(entry_attrs, old_entry_attrs)
-
- try:
- ldap.update_entry(dn, old_entry_attrs)
- except errors.EmptyModlist:
- pass
-
- if options.get('all', False):
- attrs_list = ['*']
- else:
- attrs_list = list(
- set(self.obj.default_attributes + entry_attrs.keys())
- )
-
- try:
- (dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
- except errors.NotFound:
- self.obj.handle_not_found(*keys)
-
- if self.obj.is_pkey_zone_record(*keys):
- entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
-
- retval = self.post_callback(keys, entry_attrs)
- if retval:
- return retval
-
- return dict(result=entry_attrs, value=keys[-1])
-
- def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
- pass
-
- def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
- return dn
-
- def post_callback(self, keys, entry_attrs):
- pass
-
-
-class dnsrecord_add_record(dnsrecord_mod_record):
- """
- Add records to DNS resource.
- """
- NO_CLI = True
-
- def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
- for (a, v) in entry_attrs.iteritems():
- if not isinstance(v, (list, tuple)):
- v = [v]
- old_entry_attrs.setdefault(a, [])
- old_entry_attrs[a] += v
-
-api.register(dnsrecord_add_record)
+api.register(dnsrecord)
-class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
+class dnsrecord_add(LDAPCreate):
__doc__ = _('Add new DNS resource record.')
no_option_msg = 'No options to add a specific record provided.\n' \
@@ -1158,21 +1381,18 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
),
)
- def get_options(self):
- for option in super(dnsrecord_add, self).get_options():
- yield option
- for option in self.get_record_options():
- yield option
-
def args_options_2_entry(self, *keys, **options):
- has_cli_options(options, self.no_option_msg)
+ self.obj.has_cli_options(options, self.no_option_msg)
return super(dnsrecord_add, self).args_options_2_entry(*keys, **options)
def interactive_prompt_callback(self, kw):
- for param in kw.keys():
- if param in _record_attributes:
- # some record type entered, skip this helper
- return
+ try:
+ self.obj.has_cli_options(kw, self.no_option_msg)
+ except errors.OptionError:
+ pass
+ else:
+ # some record type entered, skip this helper
+ return
# check zone type
if kw['idnsname'] == _dns_zone_record:
@@ -1183,45 +1403,55 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
top_record_types = _top_record_types
# ask for all usual record types
- user_options = self.prompt_record_options(top_record_types)
+ user_options = self.obj.prompt_record_options(top_record_types)
kw.update(user_options)
- def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
for rtype in options:
rtype_cb = '_%s_pre_callback' % rtype
if hasattr(self.obj, rtype_cb):
dn = getattr(self.obj, rtype_cb)(ldap, dn, entry_attrs, *keys, **options)
+ try:
+ (dn_, old_entry) = ldap.get_entry(
+ dn, entry_attrs.keys(),
+ normalize=self.obj.normalize_dn)
+ for attr in old_entry.keys():
+ if attr not in _record_attributes:
+ continue
+ if not isinstance(entry_attrs[attr], (tuple, list)):
+ vals = [entry_attrs[attr]]
+ else:
+ vals = list(entry_attrs[attr])
+ entry_attrs[attr] = list(set(old_entry[attr] + vals))
+ except errors.NotFound:
+ pass
return dn
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
if call_func.func_name == 'add_entry':
if isinstance(exc, errors.DuplicateEntry):
- self.obj.methods.add_record(
- *keys, **self.record_options_2_entry(**options)
- )
+ # A new record is being added to existing LDAP DNS object
+ # Update can be safely run as old record values has been
+ # already merged in pre_callback
+ ldap = self.obj.backend
+ dn = call_args[0]
+ entry_attrs = self.obj.get_record_entry_attrs(call_args[1])
+ ldap.update_entry(dn, entry_attrs, **call_kwargs)
return
raise exc
api.register(dnsrecord_add)
-class dnsrecord_mod(dnsrecord_mod_record):
+class dnsrecord_mod(LDAPUpdate):
__doc__ = _('Modify a DNS resource record.')
no_option_msg = 'No options to modify a specific record provided.'
- def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
- for (a, v) in entry_attrs.iteritems():
- if not isinstance(v, (list, tuple)):
- v = [v]
- old_entry_attrs.setdefault(a, [])
- if v or v is None: # overwrite the old entry
- old_entry_attrs[a] = v
-
- def record_options_2_entry(self, **options):
- entries = dict((t, options.get(t, [])) for t in _record_attributes)
- return has_cli_options(entries, self.no_option_msg, True)
+ def args_options_2_entry(self, *keys, **options):
+ self.obj.has_cli_options(options, self.no_option_msg, True)
+ return super(dnsrecord_mod, self).args_options_2_entry(*keys, **options)
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
for rtype in options:
@@ -1233,12 +1463,26 @@ class dnsrecord_mod(dnsrecord_mod_record):
return dn
- def post_callback(self, keys, entry_attrs):
+ def execute(self, *keys, **options):
+ result = super(dnsrecord_mod, self).execute(*keys, **options)
+
+ # remove if empty
if not self.obj.is_pkey_zone_record(*keys):
- for a in _record_attributes:
- if a in entry_attrs and entry_attrs[a]:
- return
- return self.obj.methods.delentry(*keys)
+ dn = self.obj.get_dn(*keys, **options)
+ ldap = self.obj.backend
+ (dn_, old_entry) = ldap.get_entry(
+ dn, _record_attributes,
+ normalize=self.obj.normalize_dn)
+
+ del_all = True
+ for attr in old_entry:
+ if old_entry[attr]:
+ del_all = False
+ break
+
+ if del_all:
+ return self.obj.methods.delentry(*keys)
+ return result
api.register(dnsrecord_mod)
@@ -1253,7 +1497,7 @@ class dnsrecord_delentry(LDAPDelete):
api.register(dnsrecord_delentry)
-class dnsrecord_del(dnsrecord_mod_record):
+class dnsrecord_del(LDAPUpdate):
__doc__ = _('Delete DNS resource record.')
no_option_msg = _('Neither --del-all nor options to delete a specific record provided.\n'\
@@ -1265,23 +1509,70 @@ class dnsrecord_del(dnsrecord_mod_record):
),
)
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ try:
+ (dn_, old_entry) = ldap.get_entry(
+ dn, _record_attributes,
+ normalize=self.obj.normalize_dn)
+ except errors.NotFound:
+ self.obj.handle_not_found(*keys)
+
+ for attr in entry_attrs.keys():
+ if attr not in _record_attributes:
+ continue
+ if not isinstance(entry_attrs[attr], (tuple, list)):
+ vals = [entry_attrs[attr]]
+ else:
+ vals = entry_attrs[attr]
+
+ for val in vals:
+ try:
+ old_entry[attr].remove(val)
+ except (KeyError, ValueError):
+ raise errors.AttrValueNotFound(attr=attr,
+ value=val)
+ entry_attrs[attr] = list(set(old_entry[attr]))
+
+ if not self.obj.is_pkey_zone_record(*keys):
+ del_all = True
+ for attr in old_entry:
+ if old_entry[attr]:
+ del_all = False
+ break
+ setattr(context, 'del_all', del_all)
+
+ return dn
+
def execute(self, *keys, **options):
if options.get('del_all', False):
+ if self.obj.is_pkey_zone_record(*keys):
+ raise errors.ValidationError(
+ name='del_all',
+ error=_('Zone record \'%s\' cannot be deleted') \
+ % _dns_zone_record
+ )
return self.obj.methods.delentry(*keys)
- return super(dnsrecord_del, self).execute(*keys, **options)
+ result = super(dnsrecord_del, self).execute(*keys, **options)
+
+ if getattr(context, 'del_all', False):
+ return self.obj.methods.delentry(*keys)
+ return result
- def record_options_2_entry(self, **options):
- entry = super(dnsrecord_del, self).record_options_2_entry(**options)
- return has_cli_options(entry, self.no_option_msg)
+ def args_options_2_entry(self, *keys, **options):
+ self.obj.has_cli_options(options, self.no_option_msg)
+ return super(dnsrecord_del, self).args_options_2_entry(*keys, **options)
def interactive_prompt_callback(self, kw):
if kw.get('del_all', False):
return
- for param in kw.keys():
- if param in _record_attributes:
- # we have something to delete, skip this helper
- return
+ try:
+ self.obj.has_cli_options(kw, self.no_option_msg)
+ except errors.OptionError:
+ pass
+ else:
+ # some record type entered, skip this helper
+ return
# get DNS record first so that the NotFound exception is raised
# before the helper would start
@@ -1296,12 +1587,9 @@ class dnsrecord_del(dnsrecord_mod_record):
return
# ask user for records to be removed
- dns_record = api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
- rec_types = [rec_type for rec_type in dns_record if rec_type in _record_attributes]
-
self.Backend.textui.print_plain(_(u'Current DNS record contents:\n'))
present_params = []
- for param in self.params():
+ for param in self.params:
if param.name in _record_attributes and param.name in dns_record:
present_params.append(param)
rec_type_content = u', '.join(dns_record[param.name])
@@ -1313,41 +1601,20 @@ class dnsrecord_del(dnsrecord_mod_record):
deleted_values = []
for rec_value in dns_record[param.name]:
user_del_value = self.Backend.textui.prompt_yesno(
- _("Delete %s '%s'?") % (param.label, rec_value), default=False)
+ _("Delete %(name)s '%(value)s'?") \
+ % dict(name=param.label, value=rec_value), default=False)
if user_del_value is True:
deleted_values.append(rec_value)
if deleted_values:
deleted_list = u','.join(deleted_values)
kw[param.name] = param(deleted_list)
- def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
- for (a, v) in entry_attrs.iteritems():
- if not isinstance(v, (list, tuple)):
- v = [v]
- for val in v:
- try:
- old_entry_attrs[a].remove(val)
- except (KeyError, ValueError):
- raise errors.NotFound(reason=_('%s record with value %s not found') %
- (self.obj.attr_to_cli(a), val))
-
- def post_callback(self, keys, entry_attrs):
- if not self.obj.is_pkey_zone_record(*keys):
- for a in _record_attributes:
- if a in entry_attrs and entry_attrs[a]:
- return
- return self.obj.methods.delentry(*keys)
-
api.register(dnsrecord_del)
-class dnsrecord_show(LDAPRetrieve, dnsrecord_cmd_w_record_options):
+class dnsrecord_show(LDAPRetrieve):
__doc__ = _('Display DNS resource.')
- def has_output_params(self):
- for option in self.get_record_options():
- yield option
-
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
if self.obj.is_pkey_zone_record(*keys):
entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
@@ -1356,21 +1623,11 @@ class dnsrecord_show(LDAPRetrieve, dnsrecord_cmd_w_record_options):
api.register(dnsrecord_show)
-class dnsrecord_find(LDAPSearch, dnsrecord_cmd_w_record_options):
+class dnsrecord_find(LDAPSearch):
__doc__ = _('Search for DNS resources.')
- def get_options(self):
- for option in super(dnsrecord_find, self).get_options():
- yield option
- for option in self.get_record_options():
- yield option.clone(query=True)
-
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
- record_attrs = self.record_options_2_entry(**options)
- record_filter = ldap.make_filter(record_attrs, rules=ldap.MATCH_ALL)
- filter = ldap.combine_filters(
- (filter, record_filter), rules=ldap.MATCH_ALL
- )
+ # include zone record (root entry) in the search
return (filter, base_dn, ldap.SCOPE_SUBTREE)
def post_callback(self, ldap, entries, truncated, *args, **options):
diff --git a/ipalib/util.py b/ipalib/util.py
index d575329e7..da933a86a 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -257,8 +257,12 @@ def validate_zonemgr(zonemgr):
if not all(regex_domain.match(part) for part in domain.split(".")):
raise ValueError(_('domain name may only include letters, numbers, and -'))
-def validate_hostname(hostname):
- """ See RFC 952, 1123"""
+def validate_hostname(hostname, check_fqdn=True):
+ """ See RFC 952, 1123
+
+ :param hostname Checked value
+ :param check_fqdn Check if hostname is fully qualified
+ """
regex_name = re.compile(r'^[a-z0-9]([a-z0-9-]?[a-z0-9])*$', re.IGNORECASE)
if len(hostname) > 255:
@@ -267,12 +271,12 @@ def validate_hostname(hostname):
if hostname.endswith('.'):
hostname = hostname[:-1]
- if '.' not in hostname:
- raise ValueError(_('hostname is not fully qualified'))
+ if check_fqdn and '.' not in hostname:
+ raise ValueError(_('not fully qualified'))
if not all(regex_name.match(part) for part in hostname.split(".")):
- raise ValueError(_('hostname parts may only include letters, numbers, and - ' \
- '(which is not allowed as the last character)'))
+ raise ValueError(_('only letters, numbers, and - are allowed. ' \
+ '- must not be the last name character'))
class cachedproperty(object):
"""
diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py
index 1d800f82a..00d4f9b7d 100644
--- a/tests/test_xmlrpc/test_dns_plugin.py
+++ b/tests/test_xmlrpc/test_dns_plugin.py
@@ -414,6 +414,13 @@ class test_dns(Declarative):
dict(
+ desc='Try to delete root zone record \'@\' in %r' % (dnszone1),
+ command=('dnsrecord_del', [dnszone1, u'@'], {'del_all' : True}),
+ expected=errors.ValidationError(name='del_all', error=''),
+ ),
+
+
+ dict(
desc='Create record %r in zone %r' % (dnszone1, dnsres1),
command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}),
expected={
@@ -575,13 +582,13 @@ class test_dns(Declarative):
dict(
desc='Try to add invalid LOC record to zone %r using dnsrecord_add' % (dnszone1),
- command=('dnsrecord_add', [dnszone1, u'@'], {'locrecord': u"91 11 42.4 N 16 36 29.6 E 227.64m" }),
+ command=('dnsrecord_add', [dnszone1, u'@'], {'locrecord': u"91 11 42.4 N 16 36 29.6 E 227.64" }),
expected=errors.ValidationError(name='locrecord', error=''),
),
dict(
desc='Add LOC record to zone %r using dnsrecord_add' % (dnszone1),
- command=('dnsrecord_add', [dnszone1, u'@'], {'locrecord': u"49 11 42.4 N 16 36 29.6 E 227.64m" }),
+ command=('dnsrecord_add', [dnszone1, u'@'], {'locrecord': u"49 11 42.4 N 16 36 29.6 E 227.64" }),
expected={
'value': u'@',
'summary': None,
@@ -591,7 +598,7 @@ class test_dns(Declarative):
'idnsname': [dnszone1],
'mxrecord': [u"0 %s" % dnszone1_mname],
'nsrecord': [dnszone1_mname],
- 'locrecord': [u"49 11 42.4 N 16 36 29.6 E 227.64m"],
+ 'locrecord': [u"49 11 42.4 N 16 36 29.6 E 227.64"],
},
},
),
@@ -604,7 +611,7 @@ class test_dns(Declarative):
dict(
desc='Add CNAME record to %r using dnsrecord_add' % (dnsres1),
- command=('dnsrecord_add', [dnszone1, dnsres1], {'cnamerecord': u'foo-1.example.com' }),
+ command=('dnsrecord_add', [dnszone1, dnsres1], {'cnamerecord': u'foo-1.example.com.' }),
expected={
'value': dnsres1,
'summary': None,
@@ -626,7 +633,7 @@ class test_dns(Declarative):
dict(
desc='Add KX record to %r using dnsrecord_add' % (dnsres1),
- command=('dnsrecord_add', [dnszone1, dnsres1], {'kxrecord': u'1 foo-1.example.com' }),
+ command=('dnsrecord_add', [dnszone1, dnsres1], {'kxrecord': u'1 foo-1' }),
expected={
'value': dnsres1,
'summary': None,
@@ -636,7 +643,7 @@ class test_dns(Declarative):
'idnsname': [dnsres1],
'arecord': [u'10.10.0.1'],
'cnamerecord': [u'foo-1.example.com.'],
- 'kxrecord': [u'1 foo-1.example.com'],
+ 'kxrecord': [u'1 foo-1'],
},
},
),