Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F38742265
dynamic_dns.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
dynamic_dns.py
View Options
#!/usr/bin/env python3
#
# Copyright (C) 2018 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import
os
import
sys
import
jinja2
from
vyos.config
import
Config
from
vyos
import
ConfigError
config_file
=
r'/etc/ddclient.conf'
cache_file
=
r'/var/cache/ddclient/ddclient.cache'
config_tmpl
=
"""
### Autogenerated by dynamic_dns.py ###
daemon=1m
syslog=yes
ssl=yes
pid=/var/run/ddclient/ddclient.pid
cache=/var/cache/ddclient/ddclient.cache
{% for interface in interfaces -%}
#
# ddclient configuration for interface "{{ interface.interface }}":
#
{% if interface.web_url -%}
use=web, web='{{ interface.web_url}}' {%- if interface.web_skip %}, web-skip='{{ interface.web_skip }}'{% endif %}
{% else -%}
use=if, if={{ interface.interface }}
{% endif -%}
{% for rfc in interface.rfc2136 -%}
{% for record in rfc.record %}
# RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }}
server={{ rfc.server }}
protocol=nsupdate
password={{ rfc.keyfile }}
ttl={{ rfc.ttl }}
zone={{ rfc.zone }}
{{ record }}
{% endfor -%}
{% endfor -%}
{% for srv in interface.service %}
{% for host in srv.host %}
# DynDNS provider configuration for {{ host }}
protocol={{ srv.protocol }}
max-interval=28d
login={{ srv.login }}
password='{{ srv.password }}'
{% if srv.server -%}
server={{ srv.server }}
{% endif -%}
{{ host }}
{% endfor %}
{% endfor %}
{% endfor %}
"""
# Mapping of service name to service protocol
default_service_protocol
=
{
'afraid'
:
'freedns'
,
'changeip'
:
'changeip'
,
'cloudflare'
:
'cloudflare'
,
'dnspark'
:
'dnspark'
,
'dslreports'
:
'dslreports1'
,
'dyndns'
:
'dyndns2'
,
'easydns'
:
'easydns'
,
'namecheap'
:
'namecheap'
,
'noip'
:
'noip'
,
'sitelutions'
:
'sitelutions'
,
'zoneedit'
:
'zoneedit1'
}
default_config_data
=
{
'interfaces'
:
[],
}
def
get_config
():
dyndns
=
default_config_data
conf
=
Config
()
if
not
conf
.
exists
(
'service dns dynamic'
):
return
None
else
:
conf
.
set_level
(
'service dns dynamic'
)
for
interface
in
conf
.
list_nodes
(
'interface'
):
node
=
{
'interface'
:
interface
,
'rfc2136'
:
[],
'service'
:
[],
'web_skip'
:
''
,
'web_url'
:
''
}
# set config level to e.g. "service dns dynamic interface eth0"
conf
.
set_level
(
'service dns dynamic interface {0}'
.
format
(
interface
))
# Handle RFC2136 - Dynamic Updates in the Domain Name System
for
rfc2136
in
conf
.
list_nodes
(
'rfc2136'
):
rfc
=
{
'name'
:
rfc2136
,
'keyfile'
:
''
,
'record'
:
[],
'server'
:
''
,
'ttl'
:
'600'
,
'zone'
:
''
}
if
conf
.
exists
(
'rfc2136 {0} key'
.
format
(
rfc2136
)):
rfc
[
'keyfile'
]
=
conf
.
return_value
(
'rfc2136 {0} key'
.
format
(
rfc2136
))
if
conf
.
exists
(
'rfc2136 {0} record'
.
format
(
rfc2136
)):
rfc
[
'record'
]
=
conf
.
return_values
(
'rfc2136 {0} record'
.
format
(
rfc2136
))
if
conf
.
exists
(
'rfc2136 {0} server'
.
format
(
rfc2136
)):
rfc
[
'server'
]
=
conf
.
return_value
(
'rfc2136 {0} server'
.
format
(
rfc2136
))
if
conf
.
exists
(
'rfc2136 {0} ttl'
.
format
(
rfc2136
)):
rfc
[
'ttl'
]
=
conf
.
return_value
(
'rfc2136 {0} ttl'
.
format
(
rfc2136
))
if
conf
.
exists
(
'rfc2136 {0} zone'
.
format
(
rfc2136
)):
rfc
[
'zone'
]
=
conf
.
return_value
(
'rfc2136 {0} zone'
.
format
(
rfc2136
))
node
[
'rfc2136'
]
.
append
(
rfc
)
# Handle DynDNS service providers
for
service
in
conf
.
list_nodes
(
'service'
):
srv
=
{
'provider'
:
service
,
'host'
:
[],
'login'
:
''
,
'password'
:
''
,
'protocol'
:
''
,
'server'
:
''
,
'custom'
:
False
}
# preload protocol from default service mapping
if
service
in
default_service_protocol
.
keys
():
srv
[
'protocol'
]
=
default_service_protocol
[
service
]
else
:
srv
[
'custom'
]
=
True
if
conf
.
exists
(
'service {0} login'
.
format
(
service
)):
srv
[
'login'
]
=
conf
.
return_value
(
'service {0} login'
.
format
(
service
))
if
conf
.
exists
(
'service {0} host-name'
.
format
(
service
)):
srv
[
'host'
]
=
conf
.
return_values
(
'service {0} host-name'
.
format
(
service
))
if
conf
.
exists
(
'service {0} protocol'
.
format
(
service
)):
srv
[
'protocol'
]
=
conf
.
return_value
(
'service {0} protocol'
.
format
(
service
))
if
conf
.
exists
(
'service {0} password'
.
format
(
service
)):
srv
[
'password'
]
=
conf
.
return_value
(
'service {0} password'
.
format
(
service
))
if
conf
.
exists
(
'service {0} server'
.
format
(
service
)):
srv
[
'server'
]
=
conf
.
return_value
(
'service {0} server'
.
format
(
service
))
node
[
'service'
]
.
append
(
srv
)
# Additional settings in CLI
if
conf
.
exists
(
'use-web skip'
):
node
[
'web_skip'
]
=
conf
.
return_value
(
'use-web skip'
)
if
conf
.
exists
(
'use-web url'
):
node
[
'web_url'
]
=
conf
.
return_value
(
'use-web url'
)
dyndns
[
'interfaces'
]
.
append
(
node
)
return
dyndns
def
verify
(
dyndns
):
# bail out early - looks like removal from running config
if
dyndns
is
None
:
return
None
# A 'node' corresponds to an interface
for
node
in
dyndns
[
'interfaces'
]:
# RFC2136 - configuration validation
for
rfc2136
in
node
[
'rfc2136'
]:
if
not
rfc2136
[
'record'
]:
raise
ConfigError
(
'Set key for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
rfc2136
[
'name'
],
node
[
'interface'
]))
if
not
rfc2136
[
'zone'
]:
raise
ConfigError
(
'Set zone for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
rfc2136
[
'name'
],
node
[
'interface'
]))
if
not
rfc2136
[
'keyfile'
]:
raise
ConfigError
(
'Set keyfile for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
rfc2136
[
'name'
],
node
[
'interface'
]))
else
:
if
not
os
.
path
.
isfile
(
rfc2136
[
'keyfile'
]):
raise
ConfigError
(
'Keyfile for service "{0}" to send DDNS updates for interface "{1}" does not exist'
.
format
(
rfc2136
[
'name'
],
node
[
'interface'
]))
if
not
rfc2136
[
'server'
]:
raise
ConfigError
(
'Set server for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
rfc2136
[
'name'
],
node
[
'interface'
]))
# DynDNS service provider - configuration validation
for
service
in
node
[
'service'
]:
if
not
service
[
'host'
]:
raise
ConfigError
(
'Set host-name for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
service
[
'provider'
],
node
[
'interface'
]))
if
not
service
[
'login'
]:
raise
ConfigError
(
'Set login for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
service
[
'provider'
],
node
[
'interface'
]))
if
not
service
[
'password'
]:
raise
ConfigError
(
'Set password for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
service
[
'provider'
],
node
[
'interface'
]))
if
service
[
'custom'
]
is
True
:
if
not
service
[
'protocol'
]:
raise
ConfigError
(
'Set protocol for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
service
[
'provider'
],
node
[
'interface'
]))
if
not
service
[
'server'
]:
raise
ConfigError
(
'Set server for service "{0}" to send DDNS updates for interface "{1}"'
.
format
(
service
[
'provider'
],
node
[
'interface'
]))
return
None
def
generate
(
dyndns
):
# bail out early - looks like removal from running config
if
dyndns
is
None
:
return
None
tmpl
=
jinja2
.
Template
(
config_tmpl
)
config_text
=
tmpl
.
render
(
dyndns
)
with
open
(
config_file
,
'w'
)
as
f
:
f
.
write
(
config_text
)
return
None
def
apply
(
dyndns
):
if
os
.
path
.
exists
(
cache_file
):
os
.
unlink
(
cache_file
)
if
dyndns
is
None
:
os
.
system
(
'/etc/init.d/ddclient stop'
)
else
:
os
.
system
(
'/etc/init.d/ddclient restart'
)
return
None
if
__name__
==
'__main__'
:
try
:
c
=
get_config
()
verify
(
c
)
generate
(
c
)
apply
(
c
)
except
ConfigError
as
e
:
print
(
e
)
sys
.
exit
(
1
)
File Metadata
Details
Attached
Mime Type
text/x-script.python
Expires
Mon, Dec 15, 9:09 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3080978
Default Alt Text
dynamic_dns.py (8 KB)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment