pdnsbackup 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Denis Machard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pdnsbackup
3
+ Version: 0.0.2
4
+ Summary: Backup tool for PowerDNS database
5
+ Home-page: https://github.com/dmachard/python-pdnsbackup
6
+ Author: Denis MACHARD
7
+ Author-email: d.machard@gmail.com
8
+ Keywords: powerdns pdns database backup
9
+ Platform: any
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: aiomysql
17
+ Requires-Dist: dnspython
18
+
19
+ # What is this?
20
+
21
+ Backup tool for PowerDNS database.
22
+
23
+ ## Docker run
24
+
25
+ ```bash
26
+ sudo docker run -d --env-file ./.env --name=pdnsbackup dmachard/pdnsbackup:latest
27
+ ```
28
+
29
+ ## Environment variables
30
+
31
+ | Variables | Description |
32
+ | ------------- | ------------- |
33
+ | PDNSBACKUP_DEBUG | debug mode 1 or 0 |
34
+ | PDNSBACKUP_INTERVAL | delay between backup, default is 3600s |
35
+
36
+ | Variables | Description |
37
+ | ------------- | ------------- |
38
+ | PDNSBACKUP_GMYSQL_ENABLED | enable gmysql backend, default is 1 |
39
+ | PDNSBACKUP_GMYSQL_HOST | mysql port, default is 127.0.0.1 |
40
+ | PDNSBACKUP_GMYSQL_PORT | mysql port, default is 3306 |
41
+ | PDNSBACKUP_GMYSQL_SSL | enable ssl, default is 0 |
42
+ | PDNSBACKUP_GMYSQL_DBNAME | mysql database name |
43
+ | PDNSBACKUP_GMYSQL_USER | mysql user |
44
+ | PDNSBACKUP_GMYSQL_PASSWORD | mysql password |
45
+
46
+ | Variables | Description |
47
+ | ------------- | ------------- |
48
+ | PDNSBACKUP_FILE_ENABLED | enable backup to file, defaut is 1 |
49
+ | PDNSBACKUP_FILE_PATH_BIND | zone bind path |
50
+ | PDNSBACKUP_FILE_PATH_OUTPUT | output folder |
51
+
52
+ ## Run from source
53
+
54
+ Create a `.env` file to populate your variable
55
+
56
+ Create a virtualenv
57
+
58
+ ```bash
59
+ python -m venv venv
60
+ source venv/bin/activate
61
+ ```
62
+
63
+ Install requirements and run-it
64
+
65
+ ```bash
66
+ python3 -m pip install -r requirements.txt
67
+ python3 -c "import pdnsbackup; pdnsbackup.start_backup();"
68
+ ```
69
+
70
+ ## Build and run from docker image
71
+
72
+ ```bash
73
+ sudo docker build . --file Dockerfile -t pdnsbackup
74
+ ```
@@ -0,0 +1,56 @@
1
+ # What is this?
2
+
3
+ Backup tool for PowerDNS database.
4
+
5
+ ## Docker run
6
+
7
+ ```bash
8
+ sudo docker run -d --env-file ./.env --name=pdnsbackup dmachard/pdnsbackup:latest
9
+ ```
10
+
11
+ ## Environment variables
12
+
13
+ | Variables | Description |
14
+ | ------------- | ------------- |
15
+ | PDNSBACKUP_DEBUG | debug mode 1 or 0 |
16
+ | PDNSBACKUP_INTERVAL | delay between backup, default is 3600s |
17
+
18
+ | Variables | Description |
19
+ | ------------- | ------------- |
20
+ | PDNSBACKUP_GMYSQL_ENABLED | enable gmysql backend, default is 1 |
21
+ | PDNSBACKUP_GMYSQL_HOST | mysql port, default is 127.0.0.1 |
22
+ | PDNSBACKUP_GMYSQL_PORT | mysql port, default is 3306 |
23
+ | PDNSBACKUP_GMYSQL_SSL | enable ssl, default is 0 |
24
+ | PDNSBACKUP_GMYSQL_DBNAME | mysql database name |
25
+ | PDNSBACKUP_GMYSQL_USER | mysql user |
26
+ | PDNSBACKUP_GMYSQL_PASSWORD | mysql password |
27
+
28
+ | Variables | Description |
29
+ | ------------- | ------------- |
30
+ | PDNSBACKUP_FILE_ENABLED | enable backup to file, defaut is 1 |
31
+ | PDNSBACKUP_FILE_PATH_BIND | zone bind path |
32
+ | PDNSBACKUP_FILE_PATH_OUTPUT | output folder |
33
+
34
+ ## Run from source
35
+
36
+ Create a `.env` file to populate your variable
37
+
38
+ Create a virtualenv
39
+
40
+ ```bash
41
+ python -m venv venv
42
+ source venv/bin/activate
43
+ ```
44
+
45
+ Install requirements and run-it
46
+
47
+ ```bash
48
+ python3 -m pip install -r requirements.txt
49
+ python3 -c "import pdnsbackup; pdnsbackup.start_backup();"
50
+ ```
51
+
52
+ ## Build and run from docker image
53
+
54
+ ```bash
55
+ sudo docker build . --file Dockerfile -t pdnsbackup
56
+ ```
@@ -0,0 +1 @@
1
+ from pdnsbackup.backup import start_backup
@@ -0,0 +1,231 @@
1
+ import logging
2
+ import sys
3
+ import os
4
+ import asyncio
5
+ import aiomysql
6
+ import dns
7
+ import dns.zone
8
+ import signal
9
+
10
+ from pathlib import Path
11
+ from dotenv import load_dotenv
12
+
13
+ from pdnsbackup import webapi
14
+
15
+ loop = asyncio.get_event_loop()
16
+
17
+ logger = logging.getLogger("pdnsbackup")
18
+
19
+ default_named = """zone "%s" {
20
+ file "%sdb.%s";
21
+ type master;
22
+ };"""
23
+
24
+ shutdown_task = None
25
+
26
+ def start_shutdown_task(signal, loop, start_shutdown):
27
+ global shutdown_task
28
+ if not shutdown_task:
29
+ shutdown_task = asyncio.create_task(shutdown(signal, loop, start_shutdown))
30
+
31
+ async def shutdown(signal, loop, start_shutdown):
32
+ """perform graceful shutdown"""
33
+ logger.info("stopping program...")
34
+ start_shutdown.set()
35
+
36
+ current_task = asyncio.current_task()
37
+ tasks = [
38
+ task for task in asyncio.all_tasks()
39
+ if task is not current_task
40
+ ]
41
+
42
+ for task in tasks:
43
+ task.cancel()
44
+
45
+ # close the loop
46
+ loop.stop()
47
+
48
+ def setup_logger(debug):
49
+ loglevel = logging.DEBUG if debug else logging.INFO
50
+ logfmt = '%(asctime)s %(levelname)s %(message)s'
51
+
52
+ logger.setLevel(loglevel)
53
+ logger.propagate = False
54
+
55
+ lh = logging.StreamHandler(stream=sys.stdout )
56
+ lh.setLevel(loglevel)
57
+ lh.setFormatter(logging.Formatter(logfmt))
58
+
59
+ logger.addHandler(lh)
60
+
61
+ def setup_config(args):
62
+ pass
63
+
64
+ async def backup(cfg, start_shutdown):
65
+ """backup"""
66
+ while not start_shutdown.is_set():
67
+ conn = None
68
+ cursor = None
69
+ domains = []
70
+ records = []
71
+ zones_dns = []
72
+
73
+ # first step, fetch all data
74
+ logger.info("connecting to powerdns...")
75
+ try:
76
+ if cfg["gmysql_enable"]:
77
+ logger.debug("fetching data with gmysql backend")
78
+ conn = await aiomysql.connect( host=cfg['gmysql_host'], port=int(cfg['gmysql_port']),
79
+ user=cfg['gmysql_user'], password=cfg['gmysql_password'],
80
+ db=cfg['gmysql_dbname'], loop=loop )
81
+ logger.debug("succesfully connected to database")
82
+
83
+ # fetch all domains
84
+ cursor = await conn.cursor()
85
+ await cursor.execute("SELECT id,name FROM domains;")
86
+ rows = await cursor.fetchall()
87
+ domains = list(rows)
88
+
89
+ # fetch all records
90
+ await cursor.execute("SELECT domain_id, name, type, content, ttl FROM records")
91
+ rows = await cursor.fetchall()
92
+ records = list(rows)
93
+
94
+ logger.debug("%s domains and %s records retrieved..." % (len(domains),len(records)))
95
+ except Exception as e:
96
+ logger.error("fetcher - %s" % e)
97
+ finally:
98
+ await cursor.close()
99
+ conn.close()
100
+ logger.debug("database connection closed")
101
+
102
+ # second step, convert data to dns.zone
103
+
104
+ for zone_id, zone_name in domains:
105
+ try:
106
+ soa_record = []
107
+ for domain_id, rr_name, rr_type, rr_data, rr_ttl in records:
108
+ if domain_id == zone_id and rr_type in ["SOA", "NS"]:
109
+ soa_record.append( (rr_name, rr_type, rr_data, rr_ttl) )
110
+
111
+ # init zone
112
+ zone_dns = dns.zone.Zone(".")
113
+ for rr_name, rr_type, rr_data, rr_ttl in soa_record:
114
+ rrtype = dns.rdatatype.from_text(rr_type)
115
+ rr = zone_dns.get_rdataset(rr_name, rrtype, create=True)
116
+ rr.ttl = rr_ttl
117
+ rr.add(dns.rdata.from_text(dns.rdataclass.IN, rrtype, rr_data))
118
+
119
+ # add records
120
+ for domain_id, rr_name, rr_type, rr_data, rr_ttl in records:
121
+ if domain_id == zone_id and rr_type not in ["SOA", "NS"]:
122
+ rrtype = dns.rdatatype.from_text(rr_type)
123
+ rr = zone_dns.get_rdataset(rr_name, rrtype, create=True)
124
+ rr.ttl = rr_ttl
125
+ rr.add(dns.rdata.from_text(dns.rdataclass.IN, rrtype, rr_data))
126
+
127
+ # append the zone
128
+ zones_dns.append( (zone_name, zone_dns))
129
+ except Exception as e:
130
+ logger.error("convert - %s" % e)
131
+
132
+ # third step, export zones
133
+ logger.info("%s zones to export..." % len(zones_dns))
134
+ try:
135
+ # export to file
136
+ if cfg["file_enable"]:
137
+ namedconf = []
138
+ for zname, zdns in zones_dns:
139
+ filename = f"{cfg['file_path_output']}/db.{zname}"
140
+ with open(filename, "w") as zone_file:
141
+ zone_file.write(zdns.to_text())
142
+ logger.debug(f">> file {filename} created")
143
+
144
+ namedconf.append( default_named % (zname, cfg["file_path_bind"], zname) )
145
+
146
+ with open(f"{cfg['file_path_output']}/named.conf", "w") as bind_file:
147
+ bind_file.write( "\n\n".join(namedconf) )
148
+ logger.info(f"bind configuration created")
149
+
150
+ except Exception as e:
151
+ logger.error("export - %s" % e)
152
+
153
+ logger.debug("sleeping for %s seconds" % cfg["interval"])
154
+ await asyncio.sleep(cfg["interval"])
155
+
156
+ def start_backup(cfg=None):
157
+ debug = False
158
+ # load env from file
159
+ load_dotenv()
160
+
161
+ appcfg = {}
162
+ appcfg["interval"] = 3600
163
+
164
+ appcfg["web_listen_ip"] = "127.0.0.1"
165
+ appcfg["web_listen_port"] = "8080"
166
+
167
+ appcfg["gmysql_enable"] = True
168
+ appcfg["gmysql_host"] = "127.0.0.1"
169
+ appcfg["gmysql_port"] = "3306"
170
+ appcfg["gmysql_ssl"] = False
171
+ appcfg["gmysql_dbname"] = "pdns"
172
+ appcfg["gmysql_user"] = "pdns"
173
+ appcfg["gmysql_password"] = "pdns"
174
+
175
+ appcfg["file_enable"] = True
176
+ appcfg["file_path_bind"] = "/var/lib/powerdns/"
177
+ appcfg["file_path_output"] = "/tmp/"
178
+
179
+ if cfg is not None: appcfg=cfg
180
+
181
+ # read environment variables
182
+ debug_env = os.getenv('PDNSBACKUP_DEBUG')
183
+ if debug_env is not None:
184
+ debug = bool( int(debug_env) )
185
+
186
+ # enable logger
187
+ setup_logger(debug=debug)
188
+
189
+ logger.debug("read env variables")
190
+
191
+ delay_env = os.getenv('PDNSBACKUP_INTERVAL')
192
+ if delay_env is not None:
193
+ appcfg["interval"] = int(delay_env)
194
+
195
+ gmysql_enable_env = os.getenv('PDNSBACKUP_GMYSQL_ENABLED')
196
+ if gmysql_enable_env is not None:
197
+ appcfg["gmysql_enable"] = int(gmysql_enable_env)
198
+
199
+ appcfg["gmysql_host"] = os.getenv('PDNSBACKUP_GMYSQL_HOST')
200
+ appcfg["gmysql_port"] = os.getenv('PDNSBACKUP_GMYSQL_PORT')
201
+
202
+ gmysql_ssl_env = os.getenv('PDNSBACKUP_GMYSQL_SSL')
203
+ if gmysql_ssl_env is not None:
204
+ appcfg["gmysql_ssl"] = int(gmysql_ssl_env)
205
+
206
+ appcfg["gmysql_dbname"] = os.getenv('PDNSBACKUP_GMYSQL_DBNAME')
207
+ appcfg["gmysql_user"] = os.getenv('PDNSBACKUP_GMYSQL_USER')
208
+ appcfg["gmysql_password"] = os.getenv('PDNSBACKUP_GMYSQL_PASSWORD')
209
+
210
+
211
+ file_enable_env = os.getenv('PDNSBACKUP_FILE_ENABLED')
212
+ if file_enable_env is not None:
213
+ appcfg["file_enable"] = int(file_enable_env)
214
+ appcfg["file_path_bind"] = os.getenv('PDNSBACKUP_FILE_PATH_BIND')
215
+ appcfg["file_path_output"] = os.getenv('PDNSBACKUP_FILE_PATH_OUTPUT')
216
+
217
+
218
+ # prepare shutdown handling
219
+ start_shutdown = asyncio.Event()
220
+ for sig in (signal.SIGHUP, signal.SIGTERM, signal.SIGINT):
221
+ loop.add_signal_handler(sig, lambda: start_shutdown_task(sig, loop, start_shutdown))
222
+
223
+ loop.create_task(backup(appcfg, start_shutdown))
224
+
225
+ api = webapi.setup(loop, cfg=appcfg)
226
+ if api is None: return
227
+ loop.create_task(api)
228
+
229
+ # Run the event loop, block until close is called
230
+ loop.run_forever()
231
+ logger.info("program terminated")
@@ -0,0 +1,25 @@
1
+ import logging
2
+ import aiohttp
3
+ import aiohttp.web
4
+
5
+ logger = logging.getLogger("pdnsbackup")
6
+
7
+ # init web router
8
+ router = aiohttp.web.RouteTableDef()
9
+
10
+ async def setup(loop, cfg):
11
+ """setup the web api"""
12
+ # init the web application
13
+ app = aiohttp.web.Application()
14
+ app.add_routes(router)
15
+ app["cfg"] = cfg
16
+
17
+ # run api server
18
+ listen_address = (cfg["web_listen_ip"], cfg["web_listen_port"])
19
+ try:
20
+ srv = await loop.create_server(app.make_handler(access_log=None), *listen_address)
21
+ except OSError as e:
22
+ logger.error( "webapi: %s" % e)
23
+ return None
24
+ logger.debug("webapi: listening on %s:%s" % listen_address )
25
+ return srv
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pdnsbackup
3
+ Version: 0.0.2
4
+ Summary: Backup tool for PowerDNS database
5
+ Home-page: https://github.com/dmachard/python-pdnsbackup
6
+ Author: Denis MACHARD
7
+ Author-email: d.machard@gmail.com
8
+ Keywords: powerdns pdns database backup
9
+ Platform: any
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: aiomysql
17
+ Requires-Dist: dnspython
18
+
19
+ # What is this?
20
+
21
+ Backup tool for PowerDNS database.
22
+
23
+ ## Docker run
24
+
25
+ ```bash
26
+ sudo docker run -d --env-file ./.env --name=pdnsbackup dmachard/pdnsbackup:latest
27
+ ```
28
+
29
+ ## Environment variables
30
+
31
+ | Variables | Description |
32
+ | ------------- | ------------- |
33
+ | PDNSBACKUP_DEBUG | debug mode 1 or 0 |
34
+ | PDNSBACKUP_INTERVAL | delay between backup, default is 3600s |
35
+
36
+ | Variables | Description |
37
+ | ------------- | ------------- |
38
+ | PDNSBACKUP_GMYSQL_ENABLED | enable gmysql backend, default is 1 |
39
+ | PDNSBACKUP_GMYSQL_HOST | mysql port, default is 127.0.0.1 |
40
+ | PDNSBACKUP_GMYSQL_PORT | mysql port, default is 3306 |
41
+ | PDNSBACKUP_GMYSQL_SSL | enable ssl, default is 0 |
42
+ | PDNSBACKUP_GMYSQL_DBNAME | mysql database name |
43
+ | PDNSBACKUP_GMYSQL_USER | mysql user |
44
+ | PDNSBACKUP_GMYSQL_PASSWORD | mysql password |
45
+
46
+ | Variables | Description |
47
+ | ------------- | ------------- |
48
+ | PDNSBACKUP_FILE_ENABLED | enable backup to file, defaut is 1 |
49
+ | PDNSBACKUP_FILE_PATH_BIND | zone bind path |
50
+ | PDNSBACKUP_FILE_PATH_OUTPUT | output folder |
51
+
52
+ ## Run from source
53
+
54
+ Create a `.env` file to populate your variable
55
+
56
+ Create a virtualenv
57
+
58
+ ```bash
59
+ python -m venv venv
60
+ source venv/bin/activate
61
+ ```
62
+
63
+ Install requirements and run-it
64
+
65
+ ```bash
66
+ python3 -m pip install -r requirements.txt
67
+ python3 -c "import pdnsbackup; pdnsbackup.start_backup();"
68
+ ```
69
+
70
+ ## Build and run from docker image
71
+
72
+ ```bash
73
+ sudo docker build . --file Dockerfile -t pdnsbackup
74
+ ```
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ pdnsbackup/__init__.py
5
+ pdnsbackup/backup.py
6
+ pdnsbackup/webapi.py
7
+ pdnsbackup.egg-info/PKG-INFO
8
+ pdnsbackup.egg-info/SOURCES.txt
9
+ pdnsbackup.egg-info/dependency_links.txt
10
+ pdnsbackup.egg-info/requires.txt
11
+ pdnsbackup.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ aiomysql
2
+ dnspython
@@ -0,0 +1 @@
1
+ pdnsbackup
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/python
2
+
3
+ import setuptools
4
+
5
+ with open("README.md", "r") as fh:
6
+ LONG_DESCRIPTION = fh.read()
7
+
8
+ KEYWORDS = ('powerdns pdns database backup')
9
+
10
+ setuptools.setup(
11
+ name="pdnsbackup",
12
+ version="0.0.2",
13
+ author="Denis MACHARD",
14
+ author_email="d.machard@gmail.com",
15
+ description="Backup tool for PowerDNS database",
16
+ long_description=LONG_DESCRIPTION,
17
+ long_description_content_type="text/markdown",
18
+ url="https://github.com/dmachard/python-pdnsbackup",
19
+ packages=['pdnsbackup'],
20
+ include_package_data=True,
21
+ platforms='any',
22
+ keywords=KEYWORDS,
23
+ classifiers=[
24
+ "License :: OSI Approved :: MIT License",
25
+ "Programming Language :: Python :: 3",
26
+ "Operating System :: OS Independent",
27
+ "Topic :: Software Development :: Libraries",
28
+ ],
29
+ install_requires=[
30
+ "aiomysql",
31
+ "dnspython",
32
+ ]
33
+ )