Commit 7a478559 by Owo Sugiana

Tambah VPN Checker

1 parent 68afdec7
1.0.0 30-1-2026
---------------
- Tambah VPN Checker
- Beralih dari setup.py ke pyproject.toml
0.9.1 26-7-2024 0.9.1 26-7-2024
--------------- ---------------
- Status database mirror kini berdasarkan keberadaan file *.signal. - Status database mirror kini berdasarkan keberadaan file *.signal.
......
import sys
import os
import signal
import logging
from time import sleep
from subprocess import (
Popen,
PIPE,
)
def is_live(pid):
try:
os.kill(pid, 0)
except OSError:
return
for i in range(3):
p1 = Popen(['ps', 'ax'], stdout=PIPE)
regex = '^{}{} '.format(' '*i, pid)
p2 = Popen(['grep', regex], stdin=p1.stdout, stdout=PIPE)
p3 = Popen(['grep', '-v', 'grep'], stdin=p2.stdout, stdout=PIPE)
result = p3.communicate()
found = result[0]
if found:
return True
def read_pid_file(filename):
try:
f = open(filename)
s = f.read()
f.close()
s = s.split()
s = s[0]
pid = int(s)
return pid
except IOError:
return
except ValueError:
return
except IndexError:
return
def write_pid_file(filename):
pid = os.getpid()
f = open(filename, 'w')
f.write(str(pid))
f.close()
return pid
def stop_daemon(pid_file):
pid = read_pid_file(pid_file)
if not pid:
sys.exit()
if not is_live(pid):
sys.exit()
print('kill {} by signal'.format(pid))
os.kill(pid, signal.SIGTERM)
i = 0
while i < 5:
sleep(1)
i += 1
if not is_live(pid):
sys.exit()
print('kill {p} by force'.format(p=pid))
os.kill(pid, signal.SIGKILL)
sys.exit()
log_format = '%(asctime)s %(levelname)s %(name)s %(message)s'
formatter = logging.Formatter(log_format)
def create_log(log_file):
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(formatter)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
log = logging.getLogger(sys.argv[0])
log.setLevel(logging.DEBUG)
log.addHandler(file_handler)
log.addHandler(console_handler)
return log
[build-system]
requires = ['setuptools >= 64']
[project]
name = 'maintenance'
version = '1.0.0'
dependencies = [
'sqlalchemy',
'zope.sqlalchemy',
'psycopg2-binary',
'opensipkd-hitung @ git+https://git.opensipkd.com/sugiana/opensipkd-hitung.git',
]
requires-python = '>= 3.9'
authors = [
{name='Owo Sugiana', email='sugiana@gmail.com'},
]
description = 'Backup PostgreSQL database, perbarui jam, dll'
readme = 'README.rst'
classifiers = [
'Programming Language :: Python :: 3',
'Operating System :: Debian',
]
[tool.setuptools.packages.find]
include = [
'maintenance', 'maintenance.*',
'log_table', 'log_table.*',
'log2db', 'log2db.*',
'mirror_status', 'mirror_status.*',
'vpn_check', 'vpn_check.*'
]
[project.scripts]
maintenance = 'maintenance:main'
log_table_init_db = 'log_table.init_db:main'
log_table_trigger = 'log_table.set_trigger:main'
log2db_init = 'log2db:init'
log2db = 'log2db:main'
mirror_status = 'mirror_status:main'
vpn_init_db = 'vpn_check:init_db'
vpn_init_ip = 'vpn_check:init_ip'
vpn_check = 'vpn_check:main'
import os
from setuptools import (
setup,
find_packages,
)
requires = [
'sqlalchemy',
'psycopg2-binary',
'opensipkd-hitung @ '
'git+https://git.opensipkd.com/sugiana/opensipkd-hitung.git',
]
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.rst')) as f:
README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
line = CHANGES.splitlines()[0]
version = line.split()[0]
setup(
name='maintenance',
version=version,
description='Backup PostgreSQL database, perbarui jam, dll',
long_description=README + '\n\n' + CHANGES,
classifiers=[
'Programming Language :: Python',
],
author='Owo Sugiana',
author_email='sugiana@gmail.com',
url='https://git.opensipkd.com/sugiana/maintenance',
keywords='postgresql',
install_requires=requires,
packages=find_packages(),
include_package_data=True,
zip_safe=False,
entry_points={
'console_scripts': [
'maintenance = maintenance:main',
'log_table_init_db = log_table.init_db:main',
'log_table_trigger = log_table.set_trigger:main',
'log2db_init = log2db:init',
'log2db = log2db:main',
'mirror_status = mirror_status:main',
]
},
)
[main]
sqlalchemy.url = postgresql://user:pass@localhost/openvpn
pid_file = /home/sugiana/tmp/vpn-check.pid
[formatter_simple]
format = %(asctime)s %(levelname)s %(message)s
[handler_console]
class = StreamHandler
stream = sys.stdout
formatter = simple
[handler_file]
class = FileHandler
filename = /home/sugiana/log/vpn-check.log
formatter = simple
[logger_root]
handlers = console, file
level = INFO
import sys
import os
from configparser import ConfigParser
from logging import getLogger
from datetime import datetime
from subprocess import (
Popen,
PIPE,
)
from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from zope.sqlalchemy import register
import transaction
from maintenance.logger import setup_logging
from maintenance.daemon import (
read_pid_file,
write_pid_file,
is_live,
)
from .models import (
Base,
VPN,
)
def out(s):
print(s)
sys.exit()
def init_db(argv=sys.argv[1:]):
conf_file = argv[0]
conf = ConfigParser()
conf.read(conf_file)
cf = dict(conf.items('main'))
engine = engine_from_config(cf, 'sqlalchemy.')
Base.metadata.create_all(engine)
def get_ipp_file():
files = ['/etc/openvpn/ipp.txt', '/var/log/openvpn/ipp.txt']
for filename in files:
if os.path.exists(filename):
return filename
def get_db_session(engine):
factory = sessionmaker(bind=engine)
db_session = factory()
register(db_session)
return db_session
def ping(ip: str) -> bool:
cmd1 = ['ping', '-c1', '-w', '2', ip]
cmd2 = ['grep', 'received']
cmd3 = ['cut', '-f4', '-d', ' ']
p1 = Popen(cmd1, stdout=PIPE)
p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd3, stdin=p2.stdout, stdout=PIPE)
stdout, stderr = p3.communicate()
n = stdout.decode('utf-8').rstrip()
return n == '1'
def init_ip(argv=sys.argv[1:]):
conf_file = argv[0]
setup_logging(conf_file)
log = getLogger()
conf = ConfigParser()
conf.read(conf_file)
cf = dict(conf.items('main'))
engine = engine_from_config(cf, 'sqlalchemy.')
ipp_file = get_ipp_file()
db_session = get_db_session(engine)
base_q = db_session.query(VPN)
with open(ipp_file) as f:
for line in f.readlines():
nama, ip = line.strip().split(',')[:2]
i = int(ip.split('.')[-1])
i += 2
ip = '.'.join(ip.split('.')[:-1]) + '.%d' % i
q = base_q.filter_by(ip=ip)
row = q.first()
if row:
continue
terhubung = ping(ip)
row = VPN(nama=nama, ip=ip, terhubung=terhubung)
with transaction.manager:
db_session.add(row)
log.info(
f'{nama} {ip} sudah ditambahkan, konektivitas {terhubung}')
def main(argv=sys.argv[1:]):
conf_file = argv[0]
conf = ConfigParser()
conf.read(conf_file)
cf = dict(conf.items('main'))
pid_file = cf['pid_file']
pid = read_pid_file(pid_file)
if pid and is_live(pid):
out(f'PID saya {pid} masih aktif.')
write_pid_file(pid_file)
setup_logging(conf_file)
log = getLogger()
engine = engine_from_config(cf, 'sqlalchemy.')
factory = sessionmaker(bind=engine)
db_session = factory()
register(db_session)
q = db_session.query(VPN)
for row in q.order_by(VPN.ip):
status_lalu = row.terhubung
cmd1 = ['ping', '-c1', '-w', '2', row.ip]
cmd2 = ['grep', 'received']
cmd3 = ['cut', '-f4', '-d', ' ']
p1 = Popen(cmd1, stdout=PIPE)
p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd3, stdin=p2.stdout, stdout=PIPE)
stdout, stderr = p3.communicate()
n = stdout.decode('utf-8').rstrip()
status_kini = n == '1'
status_kini = ping(row.ip)
if status_lalu == status_kini:
continue
msg = f'{row.nama} {row.ip} konektivitas: {status_lalu} -> '\
f'{status_kini}'
log.info(msg)
row.terhubung = status_kini
row.perubahan = datetime.now()
with transaction.manager:
db_session.add(row)
from sqlalchemy import (
Column,
Text,
String,
DateTime,
Boolean,
func,
)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class VPN(Base):
__tablename__ = 'openvpn'
nama = Column(Text, primary_key=True)
ip = Column(String(15), nullable=False, unique=True)
terhubung = Column(Boolean, nullable=False, server_default='false')
perubahan = Column(
DateTime(timezone=True), nullable=False, server_default=func.now())
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!