Switch bind9 agent to a driver based implementation.
Fixes bug #1074091 Fixes bug #1077023 Change-Id: I2d3077fcc38c33a0a4916c935ffad6ab63f73e7b
This commit is contained in:
parent
e8e457db4e
commit
e199113a96
@ -31,7 +31,7 @@ NOTE: This is probably incomplete!
|
||||
1. Open 3 consoles/screen sessions for each command:
|
||||
* `./bin/moniker-api`
|
||||
* `./bin/moniker-central`
|
||||
* `./bin/moniker-agent-bind9`
|
||||
* `./bin/moniker-agent`
|
||||
1. Make use of the API..
|
||||
|
||||
# TODOs:
|
||||
|
@ -19,13 +19,13 @@ import eventlet
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import service
|
||||
from moniker import utils
|
||||
from moniker.agent import bind9 as bind9_service
|
||||
from moniker.agent import service as agent_service
|
||||
|
||||
eventlet.monkey_patch()
|
||||
|
||||
utils.read_config('moniker-agent-bind9', sys.argv[1:])
|
||||
utils.read_config('moniker-agent', sys.argv[1:])
|
||||
|
||||
logging.setup('moniker')
|
||||
|
||||
launcher = service.launch(bind9_service.Service())
|
||||
launcher = service.launch(agent_service.Service())
|
||||
launcher.wait()
|
@ -8,8 +8,8 @@ debug = False
|
||||
# Top-level directory for maintaining moniker's state
|
||||
state_path = ./var/
|
||||
|
||||
#
|
||||
templates_path = ./templates/
|
||||
# Driver used for backend communication (e.g. bind9, powerdns)
|
||||
#backend_driver=bind9
|
||||
|
||||
# There has to be a better way to set these defaults
|
||||
allowed_rpc_exception_modules = moniker.exceptions, moniker.openstack.common.exception
|
@ -30,6 +30,4 @@ cfg.CONF.register_opts([
|
||||
cfg.StrOpt('agent-topic', default='agent', help='Agent Topic'),
|
||||
cfg.StrOpt('state-path', default='$pybasedir',
|
||||
help='Top-level directory for maintaining moniker\'s state'),
|
||||
cfg.StrOpt('templates-path', default='$pybasedir/templates',
|
||||
help='Templates Path'),
|
||||
])
|
||||
|
40
moniker/agent/service.py
Normal file
40
moniker/agent/service.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common.rpc import service as rpc_service
|
||||
from moniker import backend
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Service(rpc_service.Service):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.update(
|
||||
host=cfg.CONF.host,
|
||||
topic=cfg.CONF.agent_topic,
|
||||
manager=backend.get_backend()
|
||||
)
|
||||
|
||||
super(Service, self).__init__(*args, **kwargs)
|
||||
|
||||
def start(self):
|
||||
self.manager.start()
|
||||
super(Service, self).start()
|
||||
|
||||
def stop(self):
|
||||
super(Service, self).stop()
|
||||
self.manager.stop()
|
33
moniker/backend/__init__.py
Normal file
33
moniker/backend/__init__.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from stevedore import driver
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
BACKEND_NAMESPACE = 'moniker.backend'
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('backend-driver', default='bind9',
|
||||
help='The backend driver to use')
|
||||
])
|
||||
|
||||
|
||||
def get_backend():
|
||||
mgr = driver.DriverManager(BACKEND_NAMESPACE, cfg.CONF.backend_driver,
|
||||
invoke_on_load=True)
|
||||
return mgr.driver
|
62
moniker/backend/base.py
Normal file
62
moniker/backend/base.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import abc
|
||||
from moniker.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Backend(object):
|
||||
""" Base class for backend implementations """
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@staticmethod
|
||||
def register_opts(conf):
|
||||
""" Register configuration options """
|
||||
|
||||
def start(self):
|
||||
""" Hook for any necessary startup code """
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
""" Hook for any necessary shutdown code """
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_domain(self, context, domain):
|
||||
""" Create a DNS domain """
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_domain(self, context, domain):
|
||||
""" Update a DNS domain """
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_domain(self, context, domain):
|
||||
""" Delete a DNS domain """
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_record(self, context, domain, record):
|
||||
""" Create a DNS record """
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_record(self, context, domain, record):
|
||||
""" Update a DNS record """
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_record(self, context, domain, record):
|
||||
""" Delete a DNS record """
|
@ -15,32 +15,30 @@
|
||||
# under the License.
|
||||
import os
|
||||
import subprocess
|
||||
from jinja2 import Template
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common.rpc import service as rpc_service
|
||||
from moniker.openstack.common.context import get_admin_context
|
||||
from moniker import utils
|
||||
from moniker.backend import base
|
||||
from moniker.central import api as central_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('rndc-path', default='/usr/sbin/rndc', help='RNDC Path'),
|
||||
cfg.StrOpt('rndc-host', default='127.0.0.1', help='RNDC Host'),
|
||||
cfg.IntOpt('rndc-port', default=953, help='RNDC Port'),
|
||||
cfg.StrOpt('rndc-config-file', default=None, help='RNDC Config File'),
|
||||
cfg.StrOpt('rndc-key-file', default=None, help='RNDC Key File'),
|
||||
])
|
||||
|
||||
class Bind9Backend(base.Backend):
|
||||
def register_opts(self, conf):
|
||||
conf.register_opts([
|
||||
cfg.StrOpt('rndc-path', default='/usr/sbin/rndc',
|
||||
help='RNDC Path'),
|
||||
cfg.StrOpt('rndc-host', default='127.0.0.1', help='RNDC Host'),
|
||||
cfg.IntOpt('rndc-port', default=953, help='RNDC Port'),
|
||||
cfg.StrOpt('rndc-config-file', default=None,
|
||||
help='RNDC Config File'),
|
||||
cfg.StrOpt('rndc-key-file', default=None, help='RNDC Key File'),
|
||||
])
|
||||
|
||||
class Service(rpc_service.Service):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.update(
|
||||
host=cfg.CONF.host,
|
||||
topic=cfg.CONF.agent_topic
|
||||
)
|
||||
|
||||
super(Service, self).__init__(*args, **kwargs)
|
||||
def start(self):
|
||||
super(Bind9Backend, self).start()
|
||||
|
||||
# TODO: This is a hack to ensure the data dir is 100% up to date
|
||||
admin_context = get_admin_context()
|
||||
@ -86,9 +84,6 @@ class Service(rpc_service.Service):
|
||||
|
||||
domains = central_api.get_domains(admin_context)
|
||||
|
||||
template_path = os.path.join(os.path.abspath(
|
||||
cfg.CONF.templates_path), 'bind9-config.jinja2')
|
||||
|
||||
output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path),
|
||||
'bind9')
|
||||
|
||||
@ -98,8 +93,12 @@ class Service(rpc_service.Service):
|
||||
|
||||
output_path = os.path.join(output_folder, 'zones.config')
|
||||
|
||||
self._render_template(template_path, output_path, domains=domains,
|
||||
state_path=os.path.abspath(cfg.CONF.state_path))
|
||||
abs_state_path = os.path.abspath(cfg.CONF.state_path)
|
||||
|
||||
utils.render_template_to_file('bind9-config.jinja2',
|
||||
output_path,
|
||||
domains=domains,
|
||||
state_path=abs_state_path)
|
||||
|
||||
def _sync_domain(self, domain):
|
||||
""" Sync a single domain's zone file """
|
||||
@ -117,20 +116,16 @@ class Service(rpc_service.Service):
|
||||
|
||||
records = central_api.get_records(admin_context, domain['id'])
|
||||
|
||||
template_path = os.path.join(os.path.abspath(
|
||||
cfg.CONF.templates_path), 'bind9-zone.jinja2')
|
||||
|
||||
output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path),
|
||||
'bind9')
|
||||
|
||||
# Create the output folder tree if necessary
|
||||
if not os.path.exists(output_folder):
|
||||
os.makedirs(output_folder)
|
||||
|
||||
output_path = os.path.join(output_folder, '%s.zone' % domain['id'])
|
||||
|
||||
self._render_template(template_path, output_path, servers=servers,
|
||||
domain=domain, records=records)
|
||||
utils.render_template_to_file('bind9-zone.jinja2',
|
||||
output_path,
|
||||
servers=servers,
|
||||
domain=domain,
|
||||
records=records)
|
||||
|
||||
self._sync_domains()
|
||||
|
||||
@ -145,20 +140,10 @@ class Service(rpc_service.Service):
|
||||
rndc_call.extend(['-c', cfg.CONF.rndc_config_file])
|
||||
|
||||
if cfg.CONF.rndc_key_file:
|
||||
rndc_call.extend(['-k', c.cfg.CONF.rndc_key_file])
|
||||
rndc_call.extend(['-k', cfg.CONF.rndc_key_file])
|
||||
|
||||
rndc_call.extend(['reload', domain['name']])
|
||||
|
||||
LOG.warn(rndc_call)
|
||||
|
||||
subprocess.call(rndc_call)
|
||||
|
||||
def _render_template(self, template_path, output_path, **template_context):
|
||||
# TODO: Handle failures...
|
||||
with open(template_path) as template_fh:
|
||||
template = Template(template_fh.read())
|
||||
|
||||
content = template.render(**template_context)
|
||||
|
||||
with open(output_path, 'w') as output_fh:
|
||||
output_fh.write(content)
|
55
moniker/backend/impl_fake.py
Normal file
55
moniker/backend/impl_fake.py
Normal file
@ -0,0 +1,55 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.backend import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeBackend(base.Backend):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FakeBackend, self).__init__(*args, **kwargs)
|
||||
|
||||
self.create_domain_calls = []
|
||||
self.update_domain_calls = []
|
||||
self.delete_domain_calls = []
|
||||
self.create_record_calls = []
|
||||
self.update_record_calls = []
|
||||
self.delete_record_calls = []
|
||||
|
||||
def create_domain(self, context, domain):
|
||||
LOG.info('Create Domain %r' % domain)
|
||||
self.create_domain_calls.append((context, domain))
|
||||
|
||||
def update_domain(self, context, domain):
|
||||
LOG.debug('Update Domain %r' % domain)
|
||||
self.update_domain_calls.append((context, domain))
|
||||
|
||||
def delete_domain(self, context, domain):
|
||||
LOG.debug('Delete Domain %r' % domain)
|
||||
self.delete_domain_calls.append((context, domain))
|
||||
|
||||
def create_record(self, context, domain, record):
|
||||
LOG.debug('Create Record %r / %r' % (domain, record))
|
||||
self.create_record_calls.append((context, domain, record))
|
||||
|
||||
def update_record(self, context, domain, record):
|
||||
LOG.debug('Update Record %r / %r' % (domain, record))
|
||||
self.update_record_calls.append((context, domain, record))
|
||||
|
||||
def delete_record(self, context, domain, record):
|
||||
LOG.debug('Delete Record %r / %r' % (domain, record))
|
||||
self.delete_record_calls.append((context, domain, record))
|
@ -73,3 +73,7 @@ class DomainNotFound(NotFound):
|
||||
|
||||
class RecordNotFound(NotFound):
|
||||
pass
|
||||
|
||||
|
||||
class TemplateNotFound(NotFound):
|
||||
pass
|
||||
|
@ -29,9 +29,7 @@ class TestCase(unittest2.TestCase):
|
||||
super(TestCase, self).setUp()
|
||||
|
||||
self.mox = mox.Mox()
|
||||
self.config(database_connection='sqlite://',
|
||||
rpc_backend='moniker.openstack.common.rpc.impl_fake',
|
||||
notification_driver=[])
|
||||
self.config(**self.get_config_overrides())
|
||||
storage.setup_schema()
|
||||
|
||||
self.admin_context = self.get_admin_context()
|
||||
@ -48,6 +46,14 @@ class TestCase(unittest2.TestCase):
|
||||
for k, v in kwargs.iteritems():
|
||||
cfg.CONF.set_override(k, v, group)
|
||||
|
||||
def get_config_overrides(self):
|
||||
return dict(
|
||||
database_connection='sqlite://',
|
||||
rpc_backend='moniker.openstack.common.rpc.impl_fake',
|
||||
notification_driver=[],
|
||||
backend_driver='fake'
|
||||
)
|
||||
|
||||
def get_central_service(self):
|
||||
return central_service.Service()
|
||||
|
||||
|
36
moniker/tests/test_backend/__init__.py
Normal file
36
moniker/tests/test_backend/__init__.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.tests import TestCase
|
||||
from moniker import backend
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BackendDriverTestCase(TestCase):
|
||||
__test__ = False
|
||||
|
||||
def get_backend_driver(self):
|
||||
return backend.get_backend()
|
||||
|
||||
def setUp(self):
|
||||
super(BackendDriverTestCase, self).setUp()
|
||||
self.backend = self.get_backend_driver()
|
||||
|
||||
def test_dummy(self):
|
||||
# Right now we just check that we can instantiate the driver via the
|
||||
# setUp above. Proper tests TODO.
|
||||
pass
|
32
moniker/tests/test_backend/test_bind9.py
Normal file
32
moniker/tests/test_backend/test_bind9.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.tests.test_backend import BackendDriverTestCase
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Bind9BackendDriverTestCase(BackendDriverTestCase):
|
||||
__test__ = True
|
||||
|
||||
def get_config_overrides(self):
|
||||
overrides = BackendDriverTestCase.get_config_overrides(self)
|
||||
|
||||
overrides.update(
|
||||
backend_driver='bind9'
|
||||
)
|
||||
|
||||
return overrides
|
32
moniker/tests/test_backend/test_fake.py
Normal file
32
moniker/tests/test_backend/test_fake.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.tests.test_backend import BackendDriverTestCase
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeBackendDriverTestCase(BackendDriverTestCase):
|
||||
__test__ = True
|
||||
|
||||
def get_config_overrides(self):
|
||||
overrides = BackendDriverTestCase.get_config_overrides(self)
|
||||
|
||||
overrides.update(
|
||||
backend_driver='fake'
|
||||
)
|
||||
|
||||
return overrides
|
59
moniker/tests/test_utils.py
Normal file
59
moniker/tests/test_utils.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import os
|
||||
import tempfile
|
||||
from jinja2 import Template
|
||||
from moniker.tests import TestCase
|
||||
from moniker import exceptions
|
||||
from moniker import utils
|
||||
|
||||
|
||||
class TestUtils(TestCase):
|
||||
def test_load_template(self):
|
||||
name = 'bind9-config.jinja2'
|
||||
|
||||
template = utils.load_template(name)
|
||||
|
||||
self.assertIsInstance(template, Template)
|
||||
|
||||
def test_load_template_missing(self):
|
||||
name = 'invalid.jinja2'
|
||||
|
||||
with self.assertRaises(exceptions.TemplateNotFound):
|
||||
utils.load_template(name)
|
||||
|
||||
def test_render_template(self):
|
||||
template = Template("Hello {{name}}")
|
||||
|
||||
result = utils.render_template(template, name="World")
|
||||
|
||||
self.assertEqual('Hello World', result)
|
||||
|
||||
def render_template_to_file(self):
|
||||
output_path = tempfile.mktemp()
|
||||
|
||||
template = Template("Hello {{name}}")
|
||||
|
||||
utils.render_template_to_file(template, output_path=output_path,
|
||||
name="World")
|
||||
|
||||
self.assertTrue(os.path.exists(output_path))
|
||||
|
||||
try:
|
||||
with open(output_path, 'r') as fh:
|
||||
self.assertEqual('Hello World', fh.read())
|
||||
finally:
|
||||
os.unlink(output_path)
|
@ -14,6 +14,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import os
|
||||
import pkg_resources
|
||||
from jinja2 import Template
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common.notifier import api as notifier_api
|
||||
@ -62,3 +64,37 @@ def read_config(prog, argv=None):
|
||||
|
||||
cfg.CONF(argv, project='moniker', prog=prog,
|
||||
default_config_files=config_files)
|
||||
|
||||
|
||||
def load_template(template_name):
|
||||
template_path = os.path.join('resources', 'templates', template_name)
|
||||
|
||||
if not pkg_resources.resource_exists('moniker', template_path):
|
||||
raise exceptions.TemplateNotFound('Could not find the requested '
|
||||
'template: %s' % template_name)
|
||||
|
||||
template_string = pkg_resources.resource_string('moniker', template_path)
|
||||
|
||||
return Template(template_string)
|
||||
|
||||
|
||||
def render_template(template, **template_context):
|
||||
if not isinstance(template, Template):
|
||||
template = load_template(template)
|
||||
|
||||
return template.render(**template_context)
|
||||
|
||||
|
||||
def render_template_to_file(template_name, output_path, makedirs=True,
|
||||
**template_context):
|
||||
output_folder = os.path.dirname(output_path)
|
||||
|
||||
# Create the output folder tree if necessary
|
||||
if makedirs and not os.path.exists(output_folder):
|
||||
os.makedirs(output_folder)
|
||||
|
||||
# Render the template
|
||||
content = render_template(template_name, **template_context)
|
||||
|
||||
with open(output_path, 'w') as output_fh:
|
||||
output_fh.write(content)
|
||||
|
6
setup.py
6
setup.py
@ -51,7 +51,7 @@ setup(
|
||||
scripts=[
|
||||
'bin/moniker-central',
|
||||
'bin/moniker-api',
|
||||
'bin/moniker-agent-bind9',
|
||||
'bin/moniker-agent',
|
||||
],
|
||||
cmdclass=common_setup.get_cmdclass(),
|
||||
entry_points=textwrap.dedent("""
|
||||
@ -63,6 +63,10 @@ setup(
|
||||
[moniker.notification.handler]
|
||||
nova = moniker.notification_handler.nova:NovaHandler
|
||||
|
||||
[moniker.backend]
|
||||
bind9 = moniker.backend.impl_bind9:Bind9Backend
|
||||
fake = moniker.backend.impl_fake:FakeBackend
|
||||
|
||||
[moniker.cli]
|
||||
database init = moniker.cli.database:InitCommand
|
||||
database sync = moniker.cli.database:SyncCommand
|
||||
|
2
tox.ini
2
tox.ini
@ -21,4 +21,4 @@ sitepackages = False
|
||||
commands = nosetests --no-path-adjustment --with-coverage --cover-erase --cover-package=moniker --cover-inclusive []
|
||||
|
||||
[testenv:pep8]
|
||||
commands = pep8 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack moniker setup.py bin/moniker-api bin/moniker-central bin/moniker-agent-bind9
|
||||
commands = pep8 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack moniker setup.py bin/moniker-api bin/moniker-central bin/moniker-agent
|
||||
|
Loading…
x
Reference in New Issue
Block a user