Add debug messages
We are having a hard time keeping track of which operations correspond to which request. This patch adds the ability to track operations in the notifier with the message_id of the notification being processed. This message_id (which is generated by oslo is a uuid For the server, we could also set the message_id to the request_id of the python-requests object received, but this is already logged as part of the server logs. Change-Id: Ie8b885a2b5cba6684e92c49eed4a99d24621402e
This commit is contained in:
parent
9750c363f6
commit
ade787b90c
175
novajoin/ipa.py
175
novajoin/ipa.py
|
@ -62,6 +62,7 @@ class IPANovaJoinBase(object):
|
|||
self.ntries = CONF.connect_retries
|
||||
self.initial_backoff = CONF.connect_backoff
|
||||
self.ccache = "MEMORY:" + str(uuid.uuid4())
|
||||
LOG.debug("cache: %s", self.ccache)
|
||||
os.environ['KRB5CCNAME'] = self.ccache
|
||||
if self._ipa_client_configured() and not api.isdone('finalize'):
|
||||
(hostname, realm) = self.get_host_and_realm()
|
||||
|
@ -126,23 +127,24 @@ class IPANovaJoinBase(object):
|
|||
|
||||
return (hostname, realm)
|
||||
|
||||
def __backoff(self):
|
||||
LOG.debug("Backing off %s seconds", self.backoff)
|
||||
def __backoff(self, message_id):
|
||||
LOG.debug("[%s] Backing off %s seconds", message_id, self.backoff)
|
||||
time.sleep(self.backoff)
|
||||
if self.backoff < 512:
|
||||
self.backoff = self.backoff * 2
|
||||
|
||||
def __reset_backoff(self):
|
||||
def __reset_backoff(self, message_id):
|
||||
if self.backoff > self.initial_backoff:
|
||||
LOG.debug("Resetting backoff to %d", self.initial_backoff)
|
||||
LOG.debug("[%s] Resetting backoff to %d",
|
||||
message_id, self.initial_backoff)
|
||||
self.backoff = self.initial_backoff
|
||||
|
||||
def __get_connection(self):
|
||||
def __get_connection(self, message_id):
|
||||
"""Make a connection to IPA or raise an error."""
|
||||
tries = 0
|
||||
|
||||
while (tries <= self.ntries):
|
||||
LOG.debug("Attempt %d of %d", tries, self.ntries)
|
||||
LOG.debug("[%s] Attempt %d of %d", message_id, tries, self.ntries)
|
||||
if api.Backend.rpcclient.isconnected():
|
||||
api.Backend.rpcclient.disconnect()
|
||||
try:
|
||||
|
@ -154,7 +156,7 @@ class IPANovaJoinBase(object):
|
|||
errors.TicketExpired,
|
||||
errors.KerberosError) as e:
|
||||
tries += 1
|
||||
LOG.debug("kinit again: %s", e)
|
||||
LOG.debug("[%s] kinit again: %s", message_id, e)
|
||||
# pylint: disable=no-member
|
||||
try:
|
||||
kinit_keytab(str('nova/%s@%s' %
|
||||
|
@ -162,35 +164,37 @@ class IPANovaJoinBase(object):
|
|||
CONF.keytab,
|
||||
self.ccache)
|
||||
except GSSError as e:
|
||||
LOG.debug("kinit failed: %s", e)
|
||||
LOG.debug("[%s] kinit failed: %s", message_id, e)
|
||||
if self.backoff:
|
||||
self.__backoff()
|
||||
self.__backoff(message_id)
|
||||
except errors.NetworkError:
|
||||
tries += 1
|
||||
if self.backoff:
|
||||
self.__backoff()
|
||||
self.__backoff(message_id)
|
||||
except http_client.ResponseNotReady:
|
||||
# NOTE(xek): This means that the server closed the socket,
|
||||
# so keep-alive ended and we can't use that connection.
|
||||
api.Backend.rpcclient.disconnect()
|
||||
tries += 1
|
||||
if self.backoff:
|
||||
self.__backoff()
|
||||
self.__backoff(message_id)
|
||||
else:
|
||||
# successful connection
|
||||
self.__reset_backoff()
|
||||
self.__reset_backoff(message_id)
|
||||
return
|
||||
|
||||
LOG.error("Failed to connect to IPA after %d attempts", self.ntries)
|
||||
LOG.error("[%s] Failed to connect to IPA after %d attempts",
|
||||
message_id, self.ntries)
|
||||
raise exception.IPAConnectionError(tries=self.ntries)
|
||||
|
||||
def start_batch_operation(self):
|
||||
def start_batch_operation(self, message_id=0):
|
||||
"""Start a batch operation.
|
||||
|
||||
IPA method calls will be collected in a batch job
|
||||
and submitted to IPA once all the operations have collected
|
||||
by a call to _flush_batch_operation().
|
||||
"""
|
||||
LOG.debug("[%s] start batch operation", message_id)
|
||||
self.batch_args = list()
|
||||
|
||||
def _add_batch_operation(self, command, *args, **kw):
|
||||
|
@ -200,21 +204,21 @@ class IPANovaJoinBase(object):
|
|||
"params": [args, kw],
|
||||
})
|
||||
|
||||
def flush_batch_operation(self):
|
||||
def flush_batch_operation(self, message_id=0):
|
||||
"""Make an IPA batch call."""
|
||||
LOG.debug("flush_batch_operation")
|
||||
LOG.debug("[%] flush_batch_operation", message_id)
|
||||
if not self.batch_args:
|
||||
return None
|
||||
|
||||
kw = {}
|
||||
LOG.debug(self.batch_args)
|
||||
LOG.debug("[%s] %s", message_id, self.batch_args)
|
||||
|
||||
return self._call_ipa('batch', *self.batch_args, **kw)
|
||||
return self._call_ipa(message_id, 'batch', *self.batch_args, **kw)
|
||||
|
||||
def _call_ipa(self, command, *args, **kw):
|
||||
def _call_ipa(self, message_id, command, *args, **kw):
|
||||
"""Make an IPA call."""
|
||||
if not api.Backend.rpcclient.isconnected():
|
||||
self.__get_connection()
|
||||
self.__get_connection(message_id)
|
||||
if 'version' not in kw:
|
||||
kw['version'] = u'2.146' # IPA v4.2.0 for compatibility
|
||||
|
||||
|
@ -226,11 +230,11 @@ class IPANovaJoinBase(object):
|
|||
except (errors.CCacheError,
|
||||
errors.TicketExpired,
|
||||
errors.KerberosError):
|
||||
LOG.debug("Refresh authentication")
|
||||
self.__get_connection()
|
||||
LOG.debug("[%s] Refresh authentication", message_id)
|
||||
self.__get_connection(message_id)
|
||||
except errors.NetworkError:
|
||||
if self.backoff:
|
||||
self.__backoff()
|
||||
self.__backoff(message_id)
|
||||
else:
|
||||
raise
|
||||
except http_client.ResponseNotReady:
|
||||
|
@ -238,7 +242,7 @@ class IPANovaJoinBase(object):
|
|||
# so keep-alive ended and we can't use that connection.
|
||||
api.Backend.rpcclient.disconnect()
|
||||
if self.backoff:
|
||||
self.__backoff()
|
||||
self.__backoff(message_id)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
@ -259,7 +263,8 @@ class IPAClient(IPANovaJoinBase):
|
|||
host_cache = cachetools.TTLCache(maxsize=512, ttl=30)
|
||||
service_cache = cachetools.TTLCache(maxsize=512, ttl=30)
|
||||
|
||||
def add_host(self, hostname, ipaotp, metadata=None, image_metadata=None):
|
||||
def add_host(self, hostname, ipaotp, metadata=None, image_metadata=None,
|
||||
message_id=0):
|
||||
"""Add a host to IPA.
|
||||
|
||||
If requested in the metadata, add a host to IPA. The assumption
|
||||
|
@ -270,10 +275,10 @@ class IPAClient(IPANovaJoinBase):
|
|||
and if that fails due to NotFound the host is added.
|
||||
"""
|
||||
|
||||
LOG.debug('Adding ' + hostname + ' to IPA.')
|
||||
LOG.debug("[%s] Adding %s to IPA.", message_id, hostname)
|
||||
|
||||
if not self._ipa_client_configured():
|
||||
LOG.debug('IPA is not configured')
|
||||
LOG.debug('[%s] IPA is not configured', message_id)
|
||||
return False
|
||||
|
||||
# There's no use in doing any operations if ipalib hasn't been
|
||||
|
@ -287,7 +292,7 @@ class IPAClient(IPANovaJoinBase):
|
|||
image_metadata = {}
|
||||
|
||||
if hostname in self.host_cache:
|
||||
LOG.debug('Host ' + hostname + ' found in cache.')
|
||||
LOG.debug("[%s] Host %s found in cache.", message_id, hostname)
|
||||
return self.host_cache[hostname]
|
||||
|
||||
params = [hostname]
|
||||
|
@ -316,11 +321,11 @@ class IPAClient(IPANovaJoinBase):
|
|||
}
|
||||
|
||||
try:
|
||||
self._call_ipa('host_mod', *params, **modargs)
|
||||
self._call_ipa(message_id, 'host_mod', *params, **modargs)
|
||||
self.host_cache[hostname] = six.text_type(ipaotp)
|
||||
except errors.NotFound:
|
||||
try:
|
||||
self._call_ipa('host_add', *params, **hostargs)
|
||||
self._call_ipa(message_id, 'host_add', *params, **hostargs)
|
||||
self.host_cache[hostname] = six.text_type(ipaotp)
|
||||
except errors.DuplicateEntry:
|
||||
# We have no idea what the OTP is for the existing host.
|
||||
|
@ -337,30 +342,30 @@ class IPAClient(IPANovaJoinBase):
|
|||
|
||||
return self.host_cache.get(hostname, False)
|
||||
|
||||
def add_subhost(self, hostname):
|
||||
def add_subhost(self, hostname, message_id=0):
|
||||
"""Add a subhost to IPA.
|
||||
|
||||
Servers can have multiple network interfaces, and therefore can
|
||||
have multiple aliases. Moreover, they can part of a service using
|
||||
a virtual host (VIP). These aliases are denoted 'subhosts',
|
||||
"""
|
||||
LOG.debug('Adding subhost: ' + hostname)
|
||||
LOG.debug('[%s] Adding subhost: %s', message_id, hostname)
|
||||
if hostname not in self.host_cache:
|
||||
params = [hostname]
|
||||
hostargs = {'force': True}
|
||||
self._add_batch_operation('host_add', *params, **hostargs)
|
||||
self.host_cache[hostname] = True
|
||||
else:
|
||||
LOG.debug('subhost ' + hostname + ' found in cache.')
|
||||
LOG.debug("[%s] subhost %s found in cache.", message_id, hostname)
|
||||
|
||||
def delete_subhost(self, hostname, batch=True):
|
||||
def delete_subhost(self, hostname, batch=True, message_id=0):
|
||||
"""Delete a subhost from IPA.
|
||||
|
||||
Servers can have multiple network interfaces, and therefore can
|
||||
have multiple aliases. Moreover, they can part of a service using
|
||||
a virtual host (VIP). These aliases are denoted 'subhosts',
|
||||
"""
|
||||
LOG.debug('Deleting subhost: ' + hostname)
|
||||
LOG.debug(" [%s] Deleting subhost: %s", message_id, hostname)
|
||||
host_params = [hostname]
|
||||
|
||||
(hn, domain) = self.split_hostname(hostname)
|
||||
|
@ -381,19 +386,20 @@ class IPAClient(IPANovaJoinBase):
|
|||
else:
|
||||
if hostname in self.host_cache:
|
||||
del self.host_cache[hostname]
|
||||
self._call_ipa('host_del', *host_params, **host_kw)
|
||||
self._call_ipa(message_id, 'host_del', *host_params, **host_kw)
|
||||
try:
|
||||
self._call_ipa('dnsrecord_del', *dns_params, **dns_kw)
|
||||
self._call_ipa(message_id, 'dnsrecord_del',
|
||||
*dns_params, **dns_kw)
|
||||
except (errors.NotFound, errors.ACIError):
|
||||
# Ignore DNS deletion errors
|
||||
pass
|
||||
|
||||
def delete_host(self, hostname, metadata=None):
|
||||
def delete_host(self, hostname, metadata=None, message_id=0):
|
||||
"""Delete a host from IPA and remove all related DNS entries."""
|
||||
LOG.debug('Deleting ' + hostname + ' from IPA.')
|
||||
LOG.debug("[%s] Deleting %s from IPA", message_id, hostname)
|
||||
|
||||
if not self._ipa_client_configured():
|
||||
LOG.debug('IPA is not configured')
|
||||
LOG.debug('[%s] IPA is not configured', message_id)
|
||||
return
|
||||
|
||||
if metadata is None:
|
||||
|
@ -409,7 +415,7 @@ class IPAClient(IPANovaJoinBase):
|
|||
try:
|
||||
if hostname in self.host_cache:
|
||||
del self.host_cache[hostname]
|
||||
self._call_ipa('host_del', *params, **kw)
|
||||
self._call_ipa(message_id, 'host_del', *params, **kw)
|
||||
except (errors.NotFound, errors.ACIError):
|
||||
# Trying to delete a host that doesn't exist will raise an ACIError
|
||||
# to hide whether the entry exists or not
|
||||
|
@ -422,27 +428,28 @@ class IPAClient(IPANovaJoinBase):
|
|||
dns_kw = {'del_all': True, }
|
||||
|
||||
try:
|
||||
self._call_ipa('dnsrecord_del', *dns_params, **dns_kw)
|
||||
self._call_ipa(message_id, 'dnsrecord_del', *dns_params, **dns_kw)
|
||||
except (errors.NotFound, errors.ACIError):
|
||||
# Ignore DNS deletion errors
|
||||
pass
|
||||
|
||||
def add_service(self, principal):
|
||||
def add_service(self, principal, message_id=0):
|
||||
if principal not in self.service_cache:
|
||||
try:
|
||||
(service, hostname, realm) = self.split_principal(principal)
|
||||
except errors.MalformedServicePrincipal as e:
|
||||
LOG.error("Unable to split principal %s: %s", principal, e)
|
||||
LOG.error("[%s] Unable to split principal %s: %s",
|
||||
message_id, principal, e)
|
||||
raise
|
||||
LOG.debug('Adding service: ' + principal)
|
||||
LOG.debug("[%s] Adding service: %s", message_id, principal)
|
||||
params = [principal]
|
||||
service_args = {'force': True}
|
||||
self._add_batch_operation('service_add', *params, **service_args)
|
||||
self.service_cache[principal] = [hostname]
|
||||
else:
|
||||
LOG.debug('Service ' + principal + ' found in cache.')
|
||||
LOG.debug("[%s] Service %s found in cache", message_id, principal)
|
||||
|
||||
def service_add_host(self, service_principal, host):
|
||||
def service_add_host(self, service_principal, host, message_id=0):
|
||||
"""Add a host to a service.
|
||||
|
||||
In IPA there is a relationship between a host and the services for
|
||||
|
@ -454,8 +461,8 @@ class IPAClient(IPANovaJoinBase):
|
|||
in IPA this is done using the service-add-host API.
|
||||
"""
|
||||
if host not in self.service_cache.get(service_principal, []):
|
||||
LOG.debug('Adding principal ' + service_principal +
|
||||
' to host ' + host)
|
||||
LOG.debug("[%s] Adding principal %s to host %s",
|
||||
message_id, service_principal, host)
|
||||
params = [service_principal]
|
||||
service_args = {'host': (host,)}
|
||||
self._add_batch_operation('service_add_host', *params,
|
||||
|
@ -463,17 +470,19 @@ class IPAClient(IPANovaJoinBase):
|
|||
self.service_cache[service_principal] = self.service_cache.get(
|
||||
service_principal, []) + [host]
|
||||
else:
|
||||
LOG.debug('Host ' + host + ' managing ' + service_principal +
|
||||
' found in cache.')
|
||||
LOG.debug("[%s] Host %s managing %s found in cache",
|
||||
message_id, host, service_principal)
|
||||
|
||||
def service_has_hosts(self, service_principal):
|
||||
def service_has_hosts(self, service_principal, message_id=0):
|
||||
"""Return True if hosts other than parent manages this service"""
|
||||
|
||||
LOG.debug('Checking if principal ' + service_principal + ' has hosts')
|
||||
LOG.debug("[%s] Checking if principal %s has hosts",
|
||||
message_id, service_principal)
|
||||
params = [service_principal]
|
||||
service_args = {}
|
||||
try:
|
||||
result = self._call_ipa('service_show', *params, **service_args)
|
||||
result = self._call_ipa(message_id, 'service_show',
|
||||
*params, **service_args)
|
||||
except errors.NotFound:
|
||||
raise KeyError
|
||||
serviceresult = result['result']
|
||||
|
@ -483,7 +492,8 @@ class IPAClient(IPANovaJoinBase):
|
|||
service_principal
|
||||
)
|
||||
except errors.MalformedServicePrincipal as e:
|
||||
LOG.error("Unable to split principal %s: %s", service_principal, e)
|
||||
LOG.error("[%s] Unable to split principal %s: %s",
|
||||
message_id, service_principal, e)
|
||||
raise
|
||||
|
||||
for candidate in serviceresult.get('managedby_host', []):
|
||||
|
@ -491,28 +501,30 @@ class IPAClient(IPANovaJoinBase):
|
|||
return True
|
||||
return False
|
||||
|
||||
def host_get_services(self, service_host):
|
||||
def host_get_services(self, service_host, message_id=0):
|
||||
"""Return list of services this host manages"""
|
||||
LOG.debug('Checking host ' + service_host + ' services')
|
||||
LOG.debug("[%s] Checking host %s services", message_id, service_host)
|
||||
params = []
|
||||
service_args = {'man_by_host': six.text_type(service_host)}
|
||||
result = self._call_ipa('service_find', *params, **service_args)
|
||||
result = self._call_ipa(message_id, 'service_find',
|
||||
*params, **service_args)
|
||||
return [service['krbprincipalname'][0] for service in result['result']]
|
||||
|
||||
def host_has_services(self, service_host):
|
||||
def host_has_services(self, service_host, message_id=0):
|
||||
"""Return True if this host manages any services"""
|
||||
return len(self.host_get_services(service_host)) > 0
|
||||
return len(self.host_get_services(service_host, message_id)) > 0
|
||||
|
||||
def find_host(self, hostname):
|
||||
def find_host(self, hostname, message_id=0):
|
||||
"""Return True if this host exists"""
|
||||
LOG.debug('Checking if host ' + hostname + ' exists')
|
||||
LOG.debug("[%s] Checking if host %s exists", message_id, hostname)
|
||||
params = []
|
||||
service_args = {'fqdn': six.text_type(hostname)}
|
||||
result = self._call_ipa('host_find', *params, **service_args)
|
||||
result = self._call_ipa(message_id, 'host_find',
|
||||
*params, **service_args)
|
||||
return result['count'] > 0
|
||||
|
||||
def delete_service(self, principal, batch=True):
|
||||
LOG.debug('Deleting service: ' + principal)
|
||||
def delete_service(self, principal, batch=True, message_id=0):
|
||||
LOG.debug("[%s] Deleting service: %s", message_id, principal)
|
||||
params = [principal]
|
||||
service_args = {}
|
||||
if batch:
|
||||
|
@ -522,14 +534,15 @@ class IPAClient(IPANovaJoinBase):
|
|||
else:
|
||||
if principal in self.service_cache:
|
||||
del self.service_cache[principal]
|
||||
return self._call_ipa('service_del', *params, **service_args)
|
||||
return self._call_ipa(message_id, 'service_del',
|
||||
*params, **service_args)
|
||||
|
||||
def add_ip(self, hostname, floating_ip):
|
||||
def add_ip(self, hostname, floating_ip, message_id=0):
|
||||
"""Add a floating IP to a given hostname."""
|
||||
LOG.debug('In add_ip')
|
||||
LOG.debug("[%s] In add_ip", message_id)
|
||||
|
||||
if not self._ipa_client_configured():
|
||||
LOG.debug('IPA is not configured')
|
||||
LOG.debug('[%s] IPA is not configured', message_id)
|
||||
return
|
||||
|
||||
params = [six.text_type(get_domain() + '.'),
|
||||
|
@ -537,35 +550,39 @@ class IPAClient(IPANovaJoinBase):
|
|||
kw = {'a_part_ip_address': six.text_type(floating_ip)}
|
||||
|
||||
try:
|
||||
self._call_ipa('dnsrecord_add', *params, **kw)
|
||||
self._call_ipa(message_id, 'dnsrecord_add', *params, **kw)
|
||||
except (errors.DuplicateEntry, errors.ValidationError):
|
||||
pass
|
||||
|
||||
def find_record(self, floating_ip):
|
||||
def find_record(self, floating_ip, message_id=0):
|
||||
"""Find DNS A record for floating IP address"""
|
||||
LOG.debug('looking up host for floating ip' + floating_ip)
|
||||
LOG.debug("[%s] looking up host for floating ip %s",
|
||||
message_id, floating_ip)
|
||||
params = [six.text_type(get_domain() + '.')]
|
||||
service_args = {'arecord': six.text_type(floating_ip)}
|
||||
result = self._call_ipa('dnsrecord_find', *params, **service_args)
|
||||
result = self._call_ipa(message_id, 'dnsrecord_find',
|
||||
*params, **service_args)
|
||||
if result['count'] == 0:
|
||||
return
|
||||
assert(result['count'] == 1)
|
||||
return result['result'][0]['idnsname'][0].to_unicode()
|
||||
|
||||
def remove_ip(self, floating_ip):
|
||||
def remove_ip(self, floating_ip, message_id=0):
|
||||
"""Remove a floating IP from a given hostname."""
|
||||
LOG.debug('In remove_ip')
|
||||
LOG.debug("[%s] In remove_ip", message_id)
|
||||
|
||||
if not self._ipa_client_configured():
|
||||
LOG.debug('IPA is not configured')
|
||||
LOG.debug("[%s] IPA is not configured", message_id)
|
||||
return
|
||||
|
||||
hostname = self.find_record(floating_ip)
|
||||
hostname = self.find_record(floating_ip, message_id)
|
||||
if not hostname:
|
||||
LOG.debug('floating IP record not found')
|
||||
LOG.debug("[%s] floating IP record not found for %s",
|
||||
message_id, floating_ip)
|
||||
return
|
||||
|
||||
params = [six.text_type(get_domain() + '.'), hostname]
|
||||
service_args = {'arecord': six.text_type(floating_ip)}
|
||||
|
||||
self._call_ipa('dnsrecord_del', *params, **service_args)
|
||||
self._call_ipa(message_id, 'dnsrecord_del',
|
||||
*params, **service_args)
|
||||
|
|
|
@ -108,6 +108,12 @@ class JoinController(Controller):
|
|||
|
||||
Options passed in but as yet-unused are and user-data.
|
||||
"""
|
||||
|
||||
# Set message id to zero for now.
|
||||
# We could set it to the request_id in the python-request,
|
||||
# but this is already logged as part of the server logs.
|
||||
message_id = 0
|
||||
|
||||
if not body:
|
||||
LOG.error('No body in create request')
|
||||
raise base.Fault(webob.exc.HTTPBadRequest())
|
||||
|
@ -191,7 +197,8 @@ class JoinController(Controller):
|
|||
|
||||
try:
|
||||
data['ipaotp'] = self.ipaclient.add_host(data['hostname'], ipaotp,
|
||||
metadata, image_metadata)
|
||||
metadata, image_metadata,
|
||||
message_id)
|
||||
if not data['ipaotp']:
|
||||
# OTP was not added to host, don't return one
|
||||
del data['ipaotp']
|
||||
|
@ -199,24 +206,26 @@ class JoinController(Controller):
|
|||
LOG.error('adding host failed %s', e)
|
||||
LOG.error(traceback.format_exc())
|
||||
|
||||
self.ipaclient.start_batch_operation()
|
||||
self.ipaclient.start_batch_operation(message_id)
|
||||
# key-per-service
|
||||
managed_services = [metadata[key] for key in metadata.keys()
|
||||
if key.startswith('managed_service_')]
|
||||
if managed_services:
|
||||
self.add_managed_services(data['hostname'], managed_services)
|
||||
self.add_managed_services(
|
||||
data['hostname'], managed_services, message_id)
|
||||
|
||||
compact_services = util.get_compact_services(metadata)
|
||||
if compact_services:
|
||||
self.add_compact_services(hostname_short, compact_services)
|
||||
self.add_compact_services(
|
||||
hostname_short, compact_services, message_id)
|
||||
|
||||
self.ipaclient.flush_batch_operation()
|
||||
self.ipaclient.flush_batch_operation(message_id)
|
||||
|
||||
return data
|
||||
|
||||
def add_managed_services(self, base_host, services):
|
||||
def add_managed_services(self, base_host, services, message_id=0):
|
||||
"""Make any host/principal assignments passed into metadata."""
|
||||
LOG.debug("In add_managed_services")
|
||||
LOG.debug("[%s] In add_managed_services", message_id)
|
||||
|
||||
hosts_found = list()
|
||||
services_found = list()
|
||||
|
@ -226,17 +235,18 @@ class JoinController(Controller):
|
|||
|
||||
# add host if not present
|
||||
if principal_host not in hosts_found:
|
||||
self.ipaclient.add_subhost(principal_host)
|
||||
self.ipaclient.add_subhost(principal_host, message_id)
|
||||
hosts_found.append(principal_host)
|
||||
|
||||
# add service if not present
|
||||
if principal not in services_found:
|
||||
self.ipaclient.add_service(principal)
|
||||
self.ipaclient.add_service(principal, message_id)
|
||||
services_found.append(principal)
|
||||
|
||||
self.ipaclient.service_add_host(principal, base_host)
|
||||
self.ipaclient.service_add_host(principal, base_host, message_id)
|
||||
|
||||
def add_compact_services(self, base_host_short, service_repr):
|
||||
def add_compact_services(self, base_host_short, service_repr,
|
||||
message_id=0):
|
||||
"""Make any host/principal assignments passed from metadata
|
||||
|
||||
This takes a dictionary representation of the services and networks
|
||||
|
@ -270,7 +280,7 @@ class JoinController(Controller):
|
|||
This attempts to do a more compact representation since the nova
|
||||
metadta entries have a limit of 255 characters.
|
||||
"""
|
||||
LOG.debug("In add_compact_services")
|
||||
LOG.debug("[%s] In add_compact_services", message_id)
|
||||
|
||||
hosts_found = list()
|
||||
services_found = list()
|
||||
|
@ -284,12 +294,13 @@ class JoinController(Controller):
|
|||
|
||||
# add host if not present
|
||||
if principal_host not in hosts_found:
|
||||
self.ipaclient.add_subhost(principal_host)
|
||||
self.ipaclient.add_subhost(principal_host, message_id)
|
||||
hosts_found.append(principal_host)
|
||||
|
||||
# add service if not present
|
||||
if principal not in services_found:
|
||||
self.ipaclient.add_service(principal)
|
||||
self.ipaclient.add_service(principal, message_id)
|
||||
services_found.append(principal)
|
||||
|
||||
self.ipaclient.service_add_host(principal, base_host)
|
||||
self.ipaclient.service_add_host(
|
||||
principal, base_host, message_id)
|
||||
|
|
|
@ -65,9 +65,10 @@ class Registry(dict):
|
|||
def __call__(self, name, version=None, service='nova'):
|
||||
def register_event(fun):
|
||||
if version:
|
||||
def check_event(sself, payload):
|
||||
def check_event(sself, payload, message_id):
|
||||
self.check_version(payload, version, service)
|
||||
return fun(sself, payload[service + '_object.data'])
|
||||
return fun(sself, payload[service + '_object.data'],
|
||||
message_id)
|
||||
self[name] = check_event
|
||||
return check_event
|
||||
self[name] = fun
|
||||
|
@ -124,19 +125,22 @@ class NotificationEndpoint(object):
|
|||
LOG.debug("publisher: %s, event: %s, metadata: %s", publisher_id,
|
||||
event_type, metadata)
|
||||
|
||||
message_id = metadata['message_id']
|
||||
|
||||
event_handler = self.event_handlers.get(
|
||||
event_type, lambda payload: LOG.error("Status update or unknown"))
|
||||
# run event handler for received notification type
|
||||
event_handler(self, payload)
|
||||
event_handler(self, payload, message_id)
|
||||
|
||||
@event_handlers('compute.instance.create.end')
|
||||
def compute_instance_create(self, payload):
|
||||
def compute_instance_create(self, payload, message_id):
|
||||
hostname = self._generate_hostname(payload.get('hostname'))
|
||||
instance_id = payload['instance_id']
|
||||
LOG.info("Add new host %s (%s)", instance_id, hostname)
|
||||
LOG.info("[%s] Add new host %s (%s)",
|
||||
message_id, instance_id, hostname)
|
||||
|
||||
@event_handlers('compute.instance.update')
|
||||
def compute_instance_update(self, payload):
|
||||
def compute_instance_update(self, payload, message_id):
|
||||
ipa = ipaclient()
|
||||
join_controller = join.JoinController(ipa)
|
||||
hostname_short = payload['hostname']
|
||||
|
@ -149,33 +153,37 @@ class NotificationEndpoint(object):
|
|||
enroll = payload_metadata.get('ipa_enroll', '')
|
||||
image_enroll = image_metadata.get('ipa_enroll', '')
|
||||
if enroll.lower() != 'true' and image_enroll.lower() != 'true':
|
||||
LOG.info('IPA enrollment not requested, skipping update of %s',
|
||||
hostname)
|
||||
LOG.info(
|
||||
'[%s] IPA enrollment not requested, skipping update of %s',
|
||||
message_id, hostname)
|
||||
return
|
||||
# Ensure this instance exists in nova
|
||||
instance = get_instance(instance_id)
|
||||
if instance is None:
|
||||
msg = 'No such instance-id, %s' % instance_id
|
||||
msg = '[%s] No such instance-id, %s' % (message_id, instance_id)
|
||||
LOG.error(msg)
|
||||
return
|
||||
|
||||
ipa.start_batch_operation()
|
||||
LOG.info("[%s] compute instance update for %s", message_id, hostname)
|
||||
|
||||
ipa.start_batch_operation(message_id)
|
||||
# key-per-service
|
||||
managed_services = [
|
||||
payload_metadata[key] for key in payload_metadata.keys()
|
||||
if key.startswith('managed_service_')]
|
||||
if managed_services:
|
||||
join_controller.add_managed_services(hostname, managed_services)
|
||||
join_controller.add_managed_services(
|
||||
hostname, managed_services, message_id)
|
||||
|
||||
compact_services = util.get_compact_services(payload_metadata)
|
||||
if compact_services:
|
||||
join_controller.add_compact_services(
|
||||
hostname_short, compact_services)
|
||||
hostname_short, compact_services, message_id)
|
||||
|
||||
ipa.flush_batch_operation()
|
||||
ipa.flush_batch_operation(message_id)
|
||||
|
||||
@event_handlers('compute.instance.delete.end')
|
||||
def compute_instance_delete(self, payload):
|
||||
def compute_instance_delete(self, payload, message_id):
|
||||
hostname_short = payload['hostname']
|
||||
instance_id = payload['instance_id']
|
||||
payload_metadata = payload['metadata']
|
||||
|
@ -187,49 +195,51 @@ class NotificationEndpoint(object):
|
|||
image_enroll = image_metadata.get('ipa_enroll', '')
|
||||
|
||||
if enroll.lower() != 'true' and image_enroll.lower() != 'true':
|
||||
LOG.info('IPA enrollment not requested, skipping delete of %s',
|
||||
hostname)
|
||||
LOG.info(
|
||||
'[%s] IPA enrollment not requested, skipping delete of %s',
|
||||
message_id, hostname)
|
||||
return
|
||||
|
||||
LOG.info("Delete host %s (%s)", instance_id, hostname)
|
||||
LOG.info("[%s] Delete host %s (%s)", message_id, instance_id, hostname)
|
||||
try:
|
||||
ipa = ipaclient()
|
||||
ipa.delete_host(hostname, {})
|
||||
ipa.delete_host(hostname, {}, message_id)
|
||||
self.delete_subhosts(ipa, hostname_short, payload_metadata)
|
||||
except exception.IPAConnectionError:
|
||||
LOG.error("IPA Connection Error when deleting host %s (%s). "
|
||||
LOG.error("[%s] IPA Connection Error when deleting host %s (%s). "
|
||||
"Manual cleanup may be required in the IPA server.",
|
||||
instance_id, hostname)
|
||||
message_id, instance_id, hostname)
|
||||
|
||||
@event_handlers('network.floating_ip.associate')
|
||||
def floaitng_ip_associate(self, payload):
|
||||
def floaitng_ip_associate(self, payload, message_id):
|
||||
floating_ip = payload['floating_ip']
|
||||
LOG.info("Associate floating IP %s" % floating_ip)
|
||||
LOG.info("[%s] Associate floating IP %s" % (message_id, floating_ip))
|
||||
ipa = ipaclient()
|
||||
nova = novaclient()
|
||||
server = nova.servers.get(payload['instance_id'])
|
||||
if server:
|
||||
ipa.add_ip(server.name, floating_ip)
|
||||
ipa.add_ip(server.name, floating_ip, message_id)
|
||||
else:
|
||||
LOG.error("Could not resolve %s into a hostname",
|
||||
payload['instance_id'])
|
||||
LOG.error("[%s] Could not resolve %s into a hostname",
|
||||
message_id, payload['instance_id'])
|
||||
|
||||
@event_handlers('network.floating_ip.disassociate')
|
||||
def floating_ip_disassociate(self, payload):
|
||||
def floating_ip_disassociate(self, payload, message_id):
|
||||
floating_ip = payload['floating_ip']
|
||||
LOG.info("Disassociate floating IP %s" % floating_ip)
|
||||
LOG.info("[%s] Disassociate floating IP %s", message_id, floating_ip)
|
||||
ipa = ipaclient()
|
||||
ipa.remove_ip(floating_ip)
|
||||
ipa.remove_ip(floating_ip, message_id)
|
||||
|
||||
@event_handlers('floatingip.update.end')
|
||||
def floating_ip_update(self, payload):
|
||||
def floating_ip_update(self, payload, message_id):
|
||||
"""Neutron event"""
|
||||
floatingip = payload['floatingip']
|
||||
floating_ip = floatingip['floating_ip_address']
|
||||
port_id = floatingip['port_id']
|
||||
ipa = ipaclient()
|
||||
if port_id:
|
||||
LOG.info("Neutron floating IP associate: %s" % floating_ip)
|
||||
LOG.info("[%s] Neutron floating IP associate: %s",
|
||||
message_id, floating_ip)
|
||||
nova = novaclient()
|
||||
neutron = neutronclient()
|
||||
search_opts = {'id': port_id}
|
||||
|
@ -239,14 +249,16 @@ class NotificationEndpoint(object):
|
|||
if device_id:
|
||||
server = nova.servers.get(device_id)
|
||||
if server:
|
||||
ipa.add_ip(server.name, floating_ip)
|
||||
ipa.add_ip(server.name, floating_ip, message_id)
|
||||
else:
|
||||
LOG.error("Expected 1 port, got %d", len(ports))
|
||||
LOG.error("[%s] Expected 1 port, got %d",
|
||||
message_id, len(ports))
|
||||
else:
|
||||
LOG.info("Neutron floating IP disassociate: %s" % floating_ip)
|
||||
ipa.remove_ip(floating_ip)
|
||||
LOG.info("[%s] Neutron floating IP disassociate: %s",
|
||||
message_id, floating_ip)
|
||||
ipa.remove_ip(floating_ip, message_id)
|
||||
|
||||
def delete_subhosts(self, ipa, hostname_short, metadata):
|
||||
def delete_subhosts(self, ipa, hostname_short, metadata, message_id):
|
||||
"""Delete subhosts and remove VIPs if possible.
|
||||
|
||||
Servers can have multiple network interfaces, and therefore can
|
||||
|
@ -269,13 +281,15 @@ class NotificationEndpoint(object):
|
|||
compact_services = util.get_compact_services(metadata)
|
||||
if compact_services:
|
||||
self.delete_compact_services(ipa, hostname_short,
|
||||
compact_services)
|
||||
compact_services,
|
||||
message_id)
|
||||
managed_services = [metadata[key] for key in metadata.keys()
|
||||
if key.startswith('managed_service_')]
|
||||
if managed_services:
|
||||
self.delete_managed_services(ipa, managed_services)
|
||||
self.delete_managed_services(ipa, managed_services, message_id)
|
||||
|
||||
def delete_compact_services(self, ipa, host_short, service_repr):
|
||||
def delete_compact_services(self, ipa, host_short, service_repr,
|
||||
message_id):
|
||||
"""Reconstructs and removes subhosts for compact services.
|
||||
|
||||
Data looks like this:
|
||||
|
@ -289,10 +303,10 @@ class NotificationEndpoint(object):
|
|||
services to be automatically deleted through IPA referential
|
||||
integrity.
|
||||
"""
|
||||
LOG.debug("In delete compact services")
|
||||
LOG.debug("[%s] In delete compact services", message_id)
|
||||
hosts_found = list()
|
||||
|
||||
ipa.start_batch_operation()
|
||||
ipa.start_batch_operation(message_id)
|
||||
for service_name, net_list in service_repr.items():
|
||||
for network in net_list:
|
||||
host = "%s.%s" % (host_short, network)
|
||||
|
@ -302,15 +316,15 @@ class NotificationEndpoint(object):
|
|||
if principal_host not in hosts_found:
|
||||
ipa.delete_subhost(principal_host)
|
||||
hosts_found.append(principal_host)
|
||||
ipa.flush_batch_operation()
|
||||
ipa.flush_batch_operation(message_id)
|
||||
|
||||
def delete_managed_services(self, ipa, services):
|
||||
def delete_managed_services(self, ipa, services, message_id):
|
||||
"""Delete any managed services if possible.
|
||||
|
||||
Checks to see if the managed service subhost has no managed hosts
|
||||
associations and if so, deletes the host.
|
||||
"""
|
||||
LOG.debug("In delete_managed_services")
|
||||
LOG.debug("[%s] In delete_managed_services", message_id)
|
||||
hosts_deleted = list()
|
||||
services_deleted = list()
|
||||
|
||||
|
@ -353,15 +367,15 @@ class VersionedNotificationEndpoint(NotificationEndpoint):
|
|||
event_handlers = Registry(NotificationEndpoint.event_handlers)
|
||||
|
||||
@event_handlers('instance.create.end', '1.10')
|
||||
def instance_create(self, payload):
|
||||
def instance_create(self, payload, message_id):
|
||||
newpayload = {
|
||||
'hostname': payload['host_name'],
|
||||
'instance_id': payload['uuid'],
|
||||
}
|
||||
self.compute_instance_create(newpayload)
|
||||
self.compute_instance_create(newpayload, message_id)
|
||||
|
||||
@event_handlers('instance.update', '1.8')
|
||||
def instance_update(self, payload):
|
||||
def instance_update(self, payload, message_id):
|
||||
glance = glanceclient()
|
||||
newpayload = {
|
||||
'hostname': payload['host_name'],
|
||||
|
@ -369,10 +383,10 @@ class VersionedNotificationEndpoint(NotificationEndpoint):
|
|||
'metadata': payload['metadata'],
|
||||
'image_meta': glance.images.get(payload['image_uuid'])
|
||||
}
|
||||
self.compute_instance_update(newpayload)
|
||||
self.compute_instance_update(newpayload, message_id)
|
||||
|
||||
@event_handlers('instance.delete.end', '1.7')
|
||||
def instance_delete(self, payload):
|
||||
def instance_delete(self, payload, message_id):
|
||||
glance = glanceclient()
|
||||
newpayload = {
|
||||
'hostname': payload['host_name'],
|
||||
|
@ -380,7 +394,7 @@ class VersionedNotificationEndpoint(NotificationEndpoint):
|
|||
'metadata': payload['metadata'],
|
||||
'image_meta': glance.images.get(payload['image_uuid'])
|
||||
}
|
||||
self.compute_instance_delete(newpayload)
|
||||
self.compute_instance_delete(newpayload, message_id)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
Loading…
Reference in New Issue