diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8c1cee --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +#lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# vim +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2ec2bb --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# OneDNS +Dyanmic DNS for OpenNebula + +## Usage + +``` +$ docker-compose -f one-dns.yaml up +$ python setup.py install +$ onedns --etcd-host=http://localhost:2379 --client-cert /path/to/ssl/cert +``` diff --git a/onedns/__init__.py b/onedns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/onedns/cli.py b/onedns/cli.py new file mode 100644 index 0000000..0b928f1 --- /dev/null +++ b/onedns/cli.py @@ -0,0 +1,29 @@ +import argparse + +from onedns import monitor +from onedns import logger + + +def main(): + logger.configure_onedns_logging() + parser = argparse.ArgumentParser(description='OneDNS - Dynamic DNS for OpenNebula') + parser.add_argument('--one-address', required=False, + help='ONE controller host address') + parser.add_argument('--one-secret', required=False, + help='ONE credentials to use (e.g. user:key)') + parser.add_argument('--one-proxy', required=False, + help='proxy host to use to connect to ONE controller') + parser.add_argument('--etcd-host', required=False, + help='etcd host to connect to') + parser.add_argument('--etcd-port', required=False, type=int, default=4001, + help='etcd port to connect to') + parser.add_argument('--etcd-cert', required=False, type=int, + help='path to etcd client ssl cert') + args = parser.parse_args() + args_dict = vars(args) + one_args = dict((i.replace('one_', ''), args_dict[i]) for i in args_dict.keys() if + i.startswith('one_')) + etcd_args = dict((i.replace('etcd_', ''), args_dict[i]) for i in args_dict.keys() if + i.startswith('etcd_')) + mon = monitor.OneMonitor(one_kwargs=one_args, etcd_kwargs=etcd_args) + mon.run() diff --git a/onedns/clients/__init__.py b/onedns/clients/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/onedns/clients/one.py b/onedns/clients/one.py new file mode 100644 index 0000000..349e187 --- /dev/null +++ b/onedns/clients/one.py @@ -0,0 +1,24 @@ +import re +import subprocess +from collections import namedtuple + +import oca + +RE_VALIDNAME = re.compile('[^\w\d.-]') + + +VM = namedtuple('VM', 'id, name, running, addr') + + +class OneClient(object): + """ + OpenNebula Python client + """ + def __init__(self, secret=None, address=None, proxy=None): + self._oca = oca.Client(secret=secret, address=address, proxy=proxy) + self._vm_pool = oca.VirtualMachinePool(self._oca) + + def vms(self): + #self._vm_pool.info(filter=-1) + self._vm_pool.info() + return self._vm_pool diff --git a/onedns/clients/skydns.py b/onedns/clients/skydns.py new file mode 100644 index 0000000..ba77ddc --- /dev/null +++ b/onedns/clients/skydns.py @@ -0,0 +1,11 @@ +import etcd + +from onedns.logger import log + + +class SkyDNSClient(object): + def __init__(self, etcd_kwargs={}): + self._etcd = etcd.Client(**etcd_kwargs) + + def register(self, vm): + log.info("Registering VM: {vm}".format(vm=vm)) diff --git a/onedns/logger.py b/onedns/logger.py new file mode 100644 index 0000000..34415dc --- /dev/null +++ b/onedns/logger.py @@ -0,0 +1,47 @@ +import os +import logging +import logging.handlers + + +LOG_FORMAT = ("%(asctime)s %(filename)s:%(lineno)d - %(levelname)s - " + "%(message)s") + + +class NullHandler(logging.Handler): + def emit(self, record): + pass + + +def get_onedns_logger(): + log = logging.getLogger('onedns') + log.addHandler(NullHandler()) + return log + + +log = get_onedns_logger() +console = logging.StreamHandler() +formatter = logging.Formatter(LOG_FORMAT) +console.setFormatter(formatter) + + +def configure_onedns_logging(use_syslog=False, syslog_device='/dev/log'): + """ + Configure logging for onedns *application* code + + By default onedns's logger has no formatters and a NullHandler so that + other developers using onedns as a library can configure logging as + they see fit. This method is used in onedns's application code (i.e. + the 'onedns' command) to toggle onedns's application specific + formatters/handlers + + use_syslog - enable logging all messages to syslog. currently only works if + /dev/log exists on the system (standard for most Linux distros) + """ + log.setLevel(logging.DEBUG) + console.setLevel(logging.INFO) + log.addHandler(console) + if use_syslog and os.path.exists(syslog_device): + log.debug("Logging to %s" % syslog_device) + syslog_handler = logging.handlers.SysLogHandler(address=syslog_device) + syslog_handler.setLevel(logging.DEBUG) + log.addHandler(syslog_handler) diff --git a/onedns/monitor.py b/onedns/monitor.py new file mode 100644 index 0000000..41ff590 --- /dev/null +++ b/onedns/monitor.py @@ -0,0 +1,24 @@ +import time + +from onedns.clients import one +from onedns.clients import skydns + + +class OneMonitor(object): + ''' + Reads events from OpenNebula and activates/deactivates VM domain names + ''' + + def __init__(self, one_kwargs={}, etcd_kwargs={}): + self._one = one.OneClient(**one_kwargs) + self._skydns = skydns.SkyDNSClient(etcd_kwargs=etcd_kwargs) + + def update(self): + for vm in self._one.vms(): + if hasattr(vm.template, 'nics'): + self._skydns.register(vm) + + def run(self, interval=10): + while True: + self.update() + time.sleep(interval) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b82a3ce --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +oca==4.10.0 +python-etcd==0.4.3 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..83e92a7 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import os + +from setuptools import setup, find_packages + +VERSION = 0.1 +version = os.path.join('onedns', '__init__.py') +execfile(version) + +README = open('README.md').read() + +setup( + name='onedns', + version=VERSION, + packages=find_packages(), + author='Justin Riley', + author_email='justin.t.riley@gmail.com', + url="https://github.com/fasrc/onedns", + description="Dynamic DNS for OpenNebula", + long_description=README, + install_requires=[ + "oca>=4.10.0", + "python-etcd>=0.4.3", + ], + entry_points=dict(console_scripts=['onedns = onedns.cli:main']), + zip_safe=False +)