a2e1666910892af24112dfc29498ea2ec6bf43e3
[oddments/ddns.git] / src / ddns / __init__.py
1 #!/usr/bin/python
2 ###############################################################################
3 #                                                                             #
4 # ddns - A dynamic DNS client for IPFire                                      #
5 # Copyright (C) 2012 IPFire development team                                  #
6 #                                                                             #
7 # This program is free software: you can redistribute it and/or modify        #
8 # it under the terms of the GNU General Public License as published by        #
9 # the Free Software Foundation, either version 3 of the License, or           #
10 # (at your option) any later version.                                         #
11 #                                                                             #
12 # This program is distributed in the hope that it will be useful,             #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
15 # GNU General Public License for more details.                                #
16 #                                                                             #
17 # You should have received a copy of the GNU General Public License           #
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
19 #                                                                             #
20 ###############################################################################
21
22 import logging
23 import logging.handlers
24 import ConfigParser
25
26 from i18n import _
27
28 logger = logging.getLogger("ddns.core")
29 logger.propagate = 1
30
31 from .providers import *
32 from .system import DDNSSystem
33
34 # Setup the logger.
35 def setup_logging():
36         rootlogger = logging.getLogger("ddns")
37         rootlogger.setLevel(logging.INFO)
38
39         # Setup a logger that logs to syslog.
40         handler = logging.handlers.SysLogHandler(address="/dev/log",
41                 facility=logging.handlers.SysLogHandler.LOG_DAEMON
42         )
43         handler.setLevel(logging.INFO)
44         rootlogger.addHandler(handler)
45
46         handler = logging.StreamHandler()
47         rootlogger.addHandler(handler)
48
49 setup_logging()
50
51 class DDNSCore(object):
52         def __init__(self, debug=False):
53                 # In debug mode, enable debug logging.
54                 if debug:
55                         rootlogger = logging.getLogger("ddns")
56                         rootlogger.setLevel(logging.DEBUG)
57                         for handler in rootlogger.handlers:
58                                 handler.setLevel(logging.DEBUG)
59
60                         logger.debug(_("Debugging mode enabled"))
61
62                 # Initialize the settings array.
63                 self.settings = {}
64
65                 # Dict with all providers, that are supported.
66                 self.providers = {}
67                 self.register_all_providers()
68
69                 # List of configuration entries.
70                 self.entries = []
71
72                 # Add the system class.
73                 self.system = DDNSSystem(self)
74
75         def register_provider(self, provider):
76                 """
77                         Registers a new provider.
78                 """
79                 assert issubclass(provider, DDNSProvider)
80
81                 if not all((provider.handle, provider.name, provider.website)):
82                         raise DDNSError(_("Provider is not properly configured"))
83
84                 assert not self.providers.has_key(provider.handle), \
85                         "Provider '%s' has already been registered" % provider.handle
86
87                 logger.debug("Registered new provider: %s (%s)" % (provider.name, provider.handle))
88                 self.providers[provider.handle] = provider
89
90         def register_all_providers(self):
91                 """
92                         Simply registers all providers.
93                 """
94                 for provider in (
95                         DDNSProviderAllInkl,
96                         DDNSProviderDHS,
97                         DDNSProviderDNSpark,
98                         DDNSProviderDtDNS,
99                         DDNSProviderDynDNS,
100                         DDNSProviderDynU,
101                         DDNSProviderEasyDNS,
102                         DDNSProviderFreeDNSAfraidOrg,
103                         DDNSProviderNamecheap,
104                         DDNSProviderNOIP,
105                         DDNSProviderLightningWireLabs,
106                         DDNSProviderOVH,
107                         DDNSProviderRegfish,
108                         DDNSProviderSelfhost,
109                         DDNSProviderSPDNS,
110                         DDNSProviderStrato,
111                         DDNSProviderTwoDNS,
112                         DDNSProviderUdmedia,
113                         DDNSProviderVariomedia,
114                         DDNSProviderZoneedit,
115                 ):
116                         self.register_provider(provider)
117
118         def get_provider_names(self):
119                 """
120                         Returns a list of names of all registered providers.
121                 """
122                 return sorted(self.providers.keys())
123
124         def load_configuration(self, filename):
125                 logger.debug(_("Loading configuration file %s") % filename)
126
127                 configs = ConfigParser.SafeConfigParser()
128                 configs.read([filename,])
129
130                 # First apply all global configuration settings.
131                 for k, v in configs.items("config"):
132                         self.settings[k] = v
133
134                 for entry in configs.sections():
135                         # Skip the special config section.
136                         if entry == "config":
137                                 continue
138
139                         settings = {}
140                         for k, v in configs.items(entry):
141                                 settings[k] = v
142                         settings["hostname"] = entry
143
144                         # Get the name of the provider.
145                         provider = settings.get("provider", None)
146                         if not provider:
147                                 logger.warning("Entry '%s' lacks a provider setting. Skipping." % entry)
148                                 continue
149
150                         # Try to find the provider with the wanted name.
151                         try:
152                                 provider = self.providers[provider]
153                         except KeyError:
154                                 logger.warning("Could not find provider '%s' for entry '%s'." % (provider, entry))
155                                 continue
156
157                         # Create an instance of the provider object with settings from the
158                         # configuration file.
159                         entry = provider(self, **settings)
160
161                         # Add new entry to list (if not already exists).
162                         if not entry in self.entries:
163                                 self.entries.append(entry)
164
165         def updateone(self, hostname, **kwargs):
166                 for entry in self.entries:
167                         if not entry.hostname == hostname:
168                                 continue
169
170                         return self._update(entry, **kwargs)
171
172                 raise DDNSHostNotFoundError(hostname)
173
174         def updateall(self, **kwargs):
175                 """
176                         Update all configured entries.
177                 """
178                 # If there are no entries, there is nothing to do.
179                 if not self.entries:
180                         logger.debug(_("Found no entries in the configuration file. Exiting."))
181                         return
182
183                 for entry in self.entries:
184                         self._update(entry, **kwargs)
185
186         def _update(self, entry, force=False):
187                 try:
188                         entry(force=force)
189
190                 except DDNSError, e:
191                         logger.error(_("Dynamic DNS update for %(hostname)s (%(provider)s) failed:") % \
192                                 { "hostname" : entry.hostname, "provider" : entry.name })
193                         logger.error("  %s: %s" % (e.__class__.__name__, e.reason))
194                         if e.message:
195                                 logger.error("  %s" % e.message)
196
197                 except Exception, e:
198                         logger.error(_("Dynamic DNS update for %(hostname)s (%(provider)s) throwed an unhandled exception:") % \
199                                 { "hostname" : entry.hostname, "provider" : entry.name }, exc_info=True)
200
201                 else:
202                         logger.info(_("Dynamic DNS update for %(hostname)s (%(provider)s) successful") % \
203                                 { "hostname" : entry.hostname, "provider" : entry.name })