Andrew Bogott has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/332646 )
Change subject: Designate: Rename the nova_ldap sink handler to wmf_sink ...................................................................... Designate: Rename the nova_ldap sink handler to wmf_sink Soon this won't handle ldap at all, so let's get an accurate name in place. Change-Id: I3b179652f8659f2536e6d665113f8ac49f1f5cb7 --- D modules/openstack/files/liberty/designate/nova_ldap.egg-info/entry_points.txt A modules/openstack/files/liberty/designate/wmf_sink.egg-info/entry_points.txt R modules/openstack/files/liberty/designate/wmf_sink/__init__.py R modules/openstack/files/liberty/designate/wmf_sink/base.py R modules/openstack/files/liberty/designate/wmf_sink/wmfsink.py D modules/openstack/files/mitaka/designate/nova_ldap.egg-info/entry_points.txt D modules/openstack/files/mitaka/designate/nova_ldap/base.py A modules/openstack/files/mitaka/designate/wmf_sink.egg-info/entry_points.txt R modules/openstack/files/mitaka/designate/wmf_sink/__init__.py C modules/openstack/files/mitaka/designate/wmf_sink/base.py R modules/openstack/files/mitaka/designate/wmf_sink/novaldap.py C modules/openstack/files/mitaka/designate/wmf_sink/wmfsink.py M modules/openstack/manifests/designate/service.pp M modules/openstack/templates/liberty/designate/designate.conf.erb 14 files changed, 27 insertions(+), 303 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/puppet refs/changes/46/332646/1 diff --git a/modules/openstack/files/liberty/designate/nova_ldap.egg-info/entry_points.txt b/modules/openstack/files/liberty/designate/nova_ldap.egg-info/entry_points.txt deleted file mode 100644 index a65d78d..0000000 --- a/modules/openstack/files/liberty/designate/nova_ldap.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[designate.notification.handler] -nova_ldap = nova_ldap.novaldap:NovaFixedLdapHandler diff --git a/modules/openstack/files/liberty/designate/wmf_sink.egg-info/entry_points.txt b/modules/openstack/files/liberty/designate/wmf_sink.egg-info/entry_points.txt new file mode 100644 index 0000000..f3c3cf8 --- /dev/null +++ b/modules/openstack/files/liberty/designate/wmf_sink.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[designate.notification.handler] +wmf_sink = wmf_sink.wmfsink:NovaFixedWMFHandler diff --git a/modules/openstack/files/liberty/designate/nova_ldap/__init__.py b/modules/openstack/files/liberty/designate/wmf_sink/__init__.py similarity index 100% rename from modules/openstack/files/liberty/designate/nova_ldap/__init__.py rename to modules/openstack/files/liberty/designate/wmf_sink/__init__.py diff --git a/modules/openstack/files/liberty/designate/nova_ldap/base.py b/modules/openstack/files/liberty/designate/wmf_sink/base.py similarity index 99% rename from modules/openstack/files/liberty/designate/nova_ldap/base.py rename to modules/openstack/files/liberty/designate/wmf_sink/base.py index e0db247..12cc9ba 100644 --- a/modules/openstack/files/liberty/designate/nova_ldap/base.py +++ b/modules/openstack/files/liberty/designate/wmf_sink/base.py @@ -32,7 +32,7 @@ central_api = central_rpcapi.CentralAPI() -class BaseAddressLdapHandler(BaseAddressHandler): +class BaseAddressWMFHandler(BaseAddressHandler): @staticmethod def _get_ip_data(addr_dict): ip = addr_dict['address'] diff --git a/modules/openstack/files/liberty/designate/nova_ldap/novaldap.py b/modules/openstack/files/liberty/designate/wmf_sink/wmfsink.py similarity index 88% rename from modules/openstack/files/liberty/designate/nova_ldap/novaldap.py rename to modules/openstack/files/liberty/designate/wmf_sink/wmfsink.py index d4aea83..9b476e6 100644 --- a/modules/openstack/files/liberty/designate/nova_ldap/novaldap.py +++ b/modules/openstack/files/liberty/designate/wmf_sink/wmfsink.py @@ -18,14 +18,14 @@ # in the designate source at designate/notification_handler/nova.py from oslo_config import cfg -from nova_ldap.base import BaseAddressLdapHandler +from wmf_sink.base import BaseAddressWMFHandler from oslo_log import log as logging LOG = logging.getLogger(__name__) cfg.CONF.register_group(cfg.OptGroup( - name='handler:nova_ldap', - title="Configuration for Nova Ldap Handler (WMF-specific transitional)" + name='handler:wmf_sink', + title="Configuration for WMF-specific event handling)" )) cfg.CONF.register_opts([ @@ -51,12 +51,12 @@ cfg.StrOpt('keystone_auth_pass', default=None), cfg.StrOpt('keystone_auth_project', default=None), cfg.StrOpt('keystone_auth_url', default=None), -], group='handler:nova_ldap') +], group='handler:wmf_sink') -class NovaFixedLdapHandler(BaseAddressLdapHandler): +class NovaFixedWMFHandler(BaseAddressWMFHandler): """ Handler for Nova's notifications """ - __plugin_name__ = 'nova_ldap' + __plugin_name__ = 'wmf_sink' def get_exchange_topics(self): exchange = cfg.CONF[self.name].control_exchange @@ -72,7 +72,7 @@ ] def process_notification(self, context, event_type, payload): - LOG.debug('NovaLdapHandler received notification - %s' % event_type) + LOG.debug('received notification - %s' % event_type) if event_type == 'compute.instance.create.end': self._create(payload['fixed_ips'], payload, diff --git a/modules/openstack/files/mitaka/designate/nova_ldap.egg-info/entry_points.txt b/modules/openstack/files/mitaka/designate/nova_ldap.egg-info/entry_points.txt deleted file mode 100644 index a65d78d..0000000 --- a/modules/openstack/files/mitaka/designate/nova_ldap.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[designate.notification.handler] -nova_ldap = nova_ldap.novaldap:NovaFixedLdapHandler diff --git a/modules/openstack/files/mitaka/designate/nova_ldap/base.py b/modules/openstack/files/mitaka/designate/nova_ldap/base.py deleted file mode 100644 index 7ae9a46..0000000 --- a/modules/openstack/files/mitaka/designate/nova_ldap/base.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright 2015 Andrew Bogott for the Wikimedia Foundation -# All Rights Reserved. -# -# 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 oslo_config import cfg -from designate.central import rpcapi as central_rpcapi -from designate.notification_handler.base import BaseAddressHandler -from keystoneclient.auth.identity import v3 -from keystoneclient import client -from keystoneclient import exceptions as keystoneexceptions -from keystoneclient.v3 import projects -from keystoneclient import session -from oslo_log import log as logging - -import ldap -import ldap.modlist -import pipes -import subprocess - -LOG = logging.getLogger(__name__) -central_api = central_rpcapi.CentralAPI() - - -class BaseAddressLdapHandler(BaseAddressHandler): - @staticmethod - def _get_ip_data(addr_dict): - ip = addr_dict['address'] - version = addr_dict['version'] - - data = { - 'ip_version': version, - } - - # TODO(endre): Add v6 support - if version == 4: - data['ip_address'] = ip.replace('.', '-') - ip_data = ip.split(".") - for i in [0, 1, 2, 3]: - data["octet%s" % i] = ip_data[i] - return data - - @staticmethod - def _getLdapInfo(attr, conffile="/etc/ldap.conf"): - try: - f = open(conffile) - except IOError: - if conffile == "/etc/ldap.conf": - # fallback to /etc/ldap/ldap.conf, which will likely - # have less information - f = open("/etc/ldap/ldap.conf") - for line in f: - if line.strip() == "": - continue - if line.split()[0].lower() == attr.lower(): - return line.split(None, 1)[1].strip() - break - - def _openLdap(self): - ldapHost = self._getLdapInfo("uri") - sslType = self._getLdapInfo("ssl") - - binddn = cfg.CONF[self.name].get('ldapusername') - bindpw = cfg.CONF[self.name].get('ldappassword') - ds = ldap.initialize(ldapHost) - ds.protocol_version = ldap.VERSION3 - if sslType == "start_tls": - ds.start_tls_s() - - try: - ds.simple_bind_s(binddn, bindpw) - return ds - except ldap.CONSTRAINT_VIOLATION: - LOG.debug("LDAP bind failure: Too many failed attempts.\n") - except ldap.INVALID_DN_SYNTAX: - LOG.debug("LDAP bind failure: The bind DN is incorrect... \n") - except ldap.NO_SUCH_OBJECT: - LOG.debug("LDAP bind failure: " - "Unable to locate the bind DN account.\n") - except ldap.UNWILLING_TO_PERFORM as msg: - LOG.debug("LDAP bind failure: " - "The LDAP server was unwilling to perform the action" - " requested.\nError was: %s\n" % msg[0]["info"]) - except ldap.INVALID_CREDENTIALS: - LOG.debug("LDAP bind failure: Password incorrect.\n") - - return None - - def _create(self, addresses, extra, managed=True, - resource_type=None, resource_id=None): - """ - Create a a record from addresses - - :param addresses: Address objects like - {'version': 4, 'ip': '10.0.0.1'} - :param extra: Extra data to use when formatting the record - :param managed: Is it a managed resource - :param resource_type: The managed resource type - :param resource_id: The managed resource ID - """ - ds = self._openLdap() - if not ds: - return - - LOG.debug('Using DomainID: %s' % cfg.CONF[self.name].domain_id) - domain = self.get_domain(cfg.CONF[self.name].domain_id) - LOG.debug('Domain: %r' % domain) - - data = extra.copy() - LOG.debug('Event data: %s' % data) - data['domain'] = domain['name'] - - project_name = self._resolve_project_name(data['tenant_id']) - data['project_name'] = project_name - - # Just one ldap entry per host, please. - addr = addresses[0] - - event_data = data.copy() - event_data.update(self._get_ip_data(addr)) - dc = "%(hostname)s.%(project_name)s.%(domain)s" % event_data - # ldap doesn't like trailing .s - dc = dc.rstrip('.').encode('utf8') - dn = "dc=%s,ou=hosts,dc=wikimedia,dc=org" % dc - - hostEntry = {} - hostEntry['objectClass'] = ['domainrelatedobject', - 'dnsdomain', - 'puppetclient', - 'domain', - 'dcobject', - 'top'] - hostEntry['l'] = 'eqiad' - hostEntry['dc'] = dc - hostEntry['aRecord'] = addr['address'].encode('utf8') - hostEntry['puppetClass'] = [] - hostEntry['puppetVar'] = [] - for cls in cfg.CONF[self.name].get('puppetdefaultclasses'): - hostEntry['puppetClass'].append(cls) - for var in cfg.CONF[self.name].get('puppetdefaultvars'): - hostEntry['puppetVar'].append(var) - hostEntry['associatedDomain'] = [] - hostEntry['puppetVar'].append('instanceproject=%s' % - event_data['project_name'].encode( - 'utf8')) - hostEntry['puppetVar'].append('instancename=%s' % - event_data['hostname'].encode( - 'utf8')) - - for fmt in cfg.CONF[self.name].get('format'): - hostEntry['associatedDomain'].append( - (fmt % event_data).rstrip('.').encode('utf8')) - - if managed: - LOG.debug('Creating ldap record') - - modlist = ldap.modlist.addModlist(hostEntry) - try: - ds.add_s(dn, modlist) - except ldap.LDAPError as e: - LOG.debug('Ldap exception %s' % e) - - ds.unbind() - - def _delete(self, extra, managed=True, resource_id=None, - resource_type='instance', criterion={}): - """ - Handle a generic delete of a fixed ip within a domain - - :param criterion: Criterion to search and destroy records - """ - ds = self._openLdap() - if not ds: - return - - LOG.debug('Delete using DomainID: %s' % cfg.CONF[self.name].domain_id) - domain = self.get_domain(cfg.CONF[self.name].domain_id) - LOG.debug('Domain: %r' % domain) - - data = extra.copy() - LOG.debug('Event data: %s' % data) - data['domain'] = domain['name'] - - project_name = self._resolve_project_name(data['tenant_id']) - data['project_name'] = project_name - - event_data = data.copy() - - dc = "%(hostname)s.%(project_name)s.%(domain)s" % event_data - dc = dc.rstrip('.').encode('utf8') - dn = "dc=%s,ou=hosts,dc=wikimedia,dc=org" % dc - - LOG.debug('Deleting ldap record: %s' % dn) - try: - ds.delete_s(dn) - except ldap.NO_SUCH_OBJECT: - LOG.debug('Warning: %s not found in ldap. Not deleted.' % dn) - - ds.unbind() - - # WMF-specific add-on: Clean salt and puppet keys for deleted - # instance - if (cfg.CONF[self.name].puppet_key_format and - cfg.CONF[self.name].puppet_master_host): - puppetkey = cfg.CONF[self.name].puppet_key_format % event_data - puppetkey = puppetkey.rstrip('.').encode('utf8') - LOG.debug('Cleaning puppet key %s' % puppetkey) - self._run_remote_command(cfg.CONF[self.name].puppet_master_host, - cfg.CONF[self.name].certmanager_user, - 'sudo puppet cert clean %s' % - pipes.quote(puppetkey)) - - if (cfg.CONF[self.name].salt_key_format and - cfg.CONF[self.name].salt_master_host): - saltkey = cfg.CONF[self.name].salt_key_format % event_data - saltkey = saltkey.rstrip('.').encode('utf8') - LOG.debug('Cleaning salt key %s' % saltkey) - self._run_remote_command(cfg.CONF[self.name].salt_master_host, - cfg.CONF[self.name].certmanager_user, - 'sudo salt-key -y -d %s' % - pipes.quote(saltkey)) - - @staticmethod - def _run_remote_command(server, username, command): - ssh_command = ['/usr/bin/ssh', '-l%s' % username, server, command] - - p = subprocess.Popen(ssh_command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (out, error) = p.communicate() - rcode = p.wait() - return out, error, rcode - - if rcode: - LOG.warning('Remote call %s to server %s failed: \n%s\n%s' % - (command, server, out, error)) - return False - return True - - def _resolve_project_name(self, tenant_id): - try: - username = cfg.CONF[self.name].keystone_auth_name - passwd = cfg.CONF[self.name].keystone_auth_pass - project = cfg.CONF[self.name].keystone_auth_project - url = cfg.CONF[self.name].keystone_auth_url - except KeyError: - LOG.debug('Missing a config setting for keystone auth.') - return - - try: - auth = v3.Password(auth_url=url, - user_id=username, - password=passwd, - project_id=project) - sess = session.Session(auth=auth) - keystone = client.Client(session=sess, auth_url=url) - except keystoneexceptions.AuthorizationFailure: - LOG.debug('Keystone client auth failed.') - return - projectmanager = projects.ProjectManager(keystone) - proj = projectmanager.get(tenant_id) - if proj: - LOG.debug('Resolved project id %s as %s' % (tenant_id, proj.name)) - return proj.name - else: - return 'unknown' diff --git a/modules/openstack/files/mitaka/designate/wmf_sink.egg-info/entry_points.txt b/modules/openstack/files/mitaka/designate/wmf_sink.egg-info/entry_points.txt new file mode 100644 index 0000000..f3c3cf8 --- /dev/null +++ b/modules/openstack/files/mitaka/designate/wmf_sink.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[designate.notification.handler] +wmf_sink = wmf_sink.wmfsink:NovaFixedWMFHandler diff --git a/modules/openstack/files/mitaka/designate/nova_ldap/__init__.py b/modules/openstack/files/mitaka/designate/wmf_sink/__init__.py similarity index 100% rename from modules/openstack/files/mitaka/designate/nova_ldap/__init__.py rename to modules/openstack/files/mitaka/designate/wmf_sink/__init__.py diff --git a/modules/openstack/files/liberty/designate/nova_ldap/base.py b/modules/openstack/files/mitaka/designate/wmf_sink/base.py similarity index 99% copy from modules/openstack/files/liberty/designate/nova_ldap/base.py copy to modules/openstack/files/mitaka/designate/wmf_sink/base.py index e0db247..12cc9ba 100644 --- a/modules/openstack/files/liberty/designate/nova_ldap/base.py +++ b/modules/openstack/files/mitaka/designate/wmf_sink/base.py @@ -32,7 +32,7 @@ central_api = central_rpcapi.CentralAPI() -class BaseAddressLdapHandler(BaseAddressHandler): +class BaseAddressWMFHandler(BaseAddressHandler): @staticmethod def _get_ip_data(addr_dict): ip = addr_dict['address'] diff --git a/modules/openstack/files/mitaka/designate/nova_ldap/novaldap.py b/modules/openstack/files/mitaka/designate/wmf_sink/novaldap.py similarity index 100% rename from modules/openstack/files/mitaka/designate/nova_ldap/novaldap.py rename to modules/openstack/files/mitaka/designate/wmf_sink/novaldap.py diff --git a/modules/openstack/files/liberty/designate/nova_ldap/novaldap.py b/modules/openstack/files/mitaka/designate/wmf_sink/wmfsink.py similarity index 88% copy from modules/openstack/files/liberty/designate/nova_ldap/novaldap.py copy to modules/openstack/files/mitaka/designate/wmf_sink/wmfsink.py index d4aea83..9b476e6 100644 --- a/modules/openstack/files/liberty/designate/nova_ldap/novaldap.py +++ b/modules/openstack/files/mitaka/designate/wmf_sink/wmfsink.py @@ -18,14 +18,14 @@ # in the designate source at designate/notification_handler/nova.py from oslo_config import cfg -from nova_ldap.base import BaseAddressLdapHandler +from wmf_sink.base import BaseAddressWMFHandler from oslo_log import log as logging LOG = logging.getLogger(__name__) cfg.CONF.register_group(cfg.OptGroup( - name='handler:nova_ldap', - title="Configuration for Nova Ldap Handler (WMF-specific transitional)" + name='handler:wmf_sink', + title="Configuration for WMF-specific event handling)" )) cfg.CONF.register_opts([ @@ -51,12 +51,12 @@ cfg.StrOpt('keystone_auth_pass', default=None), cfg.StrOpt('keystone_auth_project', default=None), cfg.StrOpt('keystone_auth_url', default=None), -], group='handler:nova_ldap') +], group='handler:wmf_sink') -class NovaFixedLdapHandler(BaseAddressLdapHandler): +class NovaFixedWMFHandler(BaseAddressWMFHandler): """ Handler for Nova's notifications """ - __plugin_name__ = 'nova_ldap' + __plugin_name__ = 'wmf_sink' def get_exchange_topics(self): exchange = cfg.CONF[self.name].control_exchange @@ -72,7 +72,7 @@ ] def process_notification(self, context, event_type, payload): - LOG.debug('NovaLdapHandler received notification - %s' % event_type) + LOG.debug('received notification - %s' % event_type) if event_type == 'compute.instance.create.end': self._create(payload['fixed_ips'], payload, diff --git a/modules/openstack/manifests/designate/service.pp b/modules/openstack/manifests/designate/service.pp index 3c80dce..e5fa5fe 100644 --- a/modules/openstack/manifests/designate/service.pp +++ b/modules/openstack/manifests/designate/service.pp @@ -30,15 +30,15 @@ 'python-novaclient' ) - file { '/usr/lib/python2.7/dist-packages/nova_ldap': - source => "puppet:///modules/openstack/${::openstack::version}/designate/nova_ldap", + file { '/usr/lib/python2.7/dist-packages/wmf_sink': + source => "puppet:///modules/openstack/${::openstack::version}/designate/wmf_sink", owner => 'root', group => 'root', mode => '0644', recurse => true, } - file { '/usr/lib/python2.7/dist-packages/nova_ldap.egg-info': - source => "puppet:///modules/openstack/${::openstack::version}/designate/nova_ldap.egg-info", + file { '/usr/lib/python2.7/dist-packages/wmf_sink.egg-info': + source => "puppet:///modules/openstack/${::openstack::version}/designate/wmf_sink.egg-info", owner => 'root', group => 'root', mode => '0644', diff --git a/modules/openstack/templates/liberty/designate/designate.conf.erb b/modules/openstack/templates/liberty/designate/designate.conf.erb index 2cdd845..f9076f8 100644 --- a/modules/openstack/templates/liberty/designate/designate.conf.erb +++ b/modules/openstack/templates/liberty/designate/designate.conf.erb @@ -145,7 +145,7 @@ # List of notification handlers to enable, configuration of these needs to # correspond to a [handler:my_driver] section below or else in the config # Can be one or more of : nova_fixed, neutron_floatingip -enabled_notification_handlers = nova_fixed_multi, nova_ldap +enabled_notification_handlers = nova_fixed_multi, wmf_sink #----------------------- # mDNS Service @@ -297,9 +297,9 @@ keystone_auth_url = "http://<%= @designateconfig['controller_hostname'] %>:35357/v3" #----------------------- -# Nova Fixed Ldap Handler +# WMF-specific handler for Fixed IPs #----------------------- -[handler:nova_ldap] +[handler:wmf_sink] # Domain ID of domain to create records in. For a pre-existing domain, in this case eqiad.wmflabs domain_id = '<%= @designateconfig["domain_id_internal_forward"] %>' notification_topics = monitor -- To view, visit https://gerrit.wikimedia.org/r/332646 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I3b179652f8659f2536e6d665113f8ac49f1f5cb7 Gerrit-PatchSet: 1 Gerrit-Project: operations/puppet Gerrit-Branch: production Gerrit-Owner: Andrew Bogott <abog...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits