Handle compact services on multiple lines
This patch adds logic to handle compact service metadata that has been split into multiple lines to avoid hitting the metadata size limit. Co-Authored-By: Grzegorz Grasza <xek@redhat.com> Change-Id: Ida39f5768c67f982b2fe316f6fae4988a74c8534
This commit is contained in:
parent
609f6e2b2b
commit
2c0091d23d
|
@ -12,7 +12,6 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -26,7 +25,7 @@ from novajoin.glance import get_default_image_service
|
||||||
from novajoin.ipa import IPAClient
|
from novajoin.ipa import IPAClient
|
||||||
from novajoin import keystone_client
|
from novajoin import keystone_client
|
||||||
from novajoin.nova import get_instance
|
from novajoin.nova import get_instance
|
||||||
from novajoin.util import get_fqdn
|
from novajoin import util
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -200,7 +199,7 @@ class JoinController(Controller):
|
||||||
|
|
||||||
ipaotp = uuid.uuid4().hex
|
ipaotp = uuid.uuid4().hex
|
||||||
|
|
||||||
data['hostname'] = get_fqdn(hostname_short, project_name)
|
data['hostname'] = util.get_fqdn(hostname_short, project_name)
|
||||||
_, realm = self.ipaclient.get_host_and_realm()
|
_, realm = self.ipaclient.get_host_and_realm()
|
||||||
data['krb_realm'] = realm
|
data['krb_realm'] = realm
|
||||||
|
|
||||||
|
@ -220,10 +219,11 @@ class JoinController(Controller):
|
||||||
if key.startswith('managed_service_')]
|
if key.startswith('managed_service_')]
|
||||||
if managed_services:
|
if managed_services:
|
||||||
self.handle_services(data['hostname'], managed_services)
|
self.handle_services(data['hostname'], managed_services)
|
||||||
# compact json format
|
|
||||||
if 'compact_services' in metadata:
|
compact_services = util.get_compact_services(metadata)
|
||||||
self.handle_compact_services(hostname_short,
|
if compact_services:
|
||||||
metadata.get('compact_services'))
|
self.handle_compact_services(hostname_short, compact_services)
|
||||||
|
|
||||||
self.ipaclient.flush_batch_operation()
|
self.ipaclient.flush_batch_operation()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -250,13 +250,13 @@ class JoinController(Controller):
|
||||||
|
|
||||||
self.ipaclient.service_add_host(principal, base_host)
|
self.ipaclient.service_add_host(principal, base_host)
|
||||||
|
|
||||||
def handle_compact_services(self, base_host_short, service_repr_json):
|
def handle_compact_services(self, base_host_short, service_repr):
|
||||||
"""Make any host/principal assignments passed from metadata
|
"""Make any host/principal assignments passed from metadata
|
||||||
|
|
||||||
This takes a representation of the services and networks where the
|
This takes a dictionary representation of the services and networks
|
||||||
services are listening on, and forms appropriate hostnames/service
|
where the services are listening on, and forms appropriate
|
||||||
principals based on this information. The representation looks as the
|
hostnames/service principals based on this information.
|
||||||
following:
|
The dictionary representation looks as the following:
|
||||||
|
|
||||||
{
|
{
|
||||||
"service1": [
|
"service1": [
|
||||||
|
@ -286,15 +286,14 @@ class JoinController(Controller):
|
||||||
"""
|
"""
|
||||||
LOG.debug("In handle compact services")
|
LOG.debug("In handle compact services")
|
||||||
|
|
||||||
service_repr = json.loads(service_repr_json)
|
|
||||||
hosts_found = list()
|
hosts_found = list()
|
||||||
services_found = list()
|
services_found = list()
|
||||||
base_host = get_fqdn(base_host_short)
|
base_host = util.get_fqdn(base_host_short)
|
||||||
|
|
||||||
for service_name, net_list in service_repr.items():
|
for service_name, net_list in service_repr.items():
|
||||||
for network in net_list:
|
for network in net_list:
|
||||||
host_short = "%s.%s" % (base_host_short, network)
|
host_short = "%s.%s" % (base_host_short, network)
|
||||||
principal_host = get_fqdn(host_short)
|
principal_host = util.get_fqdn(host_short)
|
||||||
principal = "%s/%s" % (service_name, principal_host)
|
principal = "%s/%s" % (service_name, principal_host)
|
||||||
|
|
||||||
# add host if not present
|
# add host if not present
|
||||||
|
|
|
@ -17,13 +17,16 @@
|
||||||
# notification_topic = notifications
|
# notification_topic = notifications
|
||||||
# notify_on_state_change = vm_state
|
# notify_on_state_change = vm_state
|
||||||
|
|
||||||
import json
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import glanceclient as glance_client
|
import glanceclient as glance_client
|
||||||
from neutronclient.v2_0 import client as neutron_client
|
from neutronclient.v2_0 import client as neutron_client
|
||||||
from novaclient import client as nova_client
|
from novaclient import client as nova_client
|
||||||
|
from oslo_log import log as logging
|
||||||
|
import oslo_messaging
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from novajoin import config
|
from novajoin import config
|
||||||
from novajoin import exception
|
from novajoin import exception
|
||||||
from novajoin.ipa import IPAClient
|
from novajoin.ipa import IPAClient
|
||||||
|
@ -31,11 +34,7 @@ from novajoin import join
|
||||||
from novajoin.keystone_client import get_session
|
from novajoin.keystone_client import get_session
|
||||||
from novajoin.keystone_client import register_keystoneauth_opts
|
from novajoin.keystone_client import register_keystoneauth_opts
|
||||||
from novajoin.nova import get_instance
|
from novajoin.nova import get_instance
|
||||||
from novajoin.util import get_domain
|
from novajoin import util
|
||||||
from novajoin.util import get_fqdn
|
|
||||||
from oslo_log import log as logging
|
|
||||||
import oslo_messaging
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
|
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
@ -169,10 +168,12 @@ class NotificationEndpoint(object):
|
||||||
if key.startswith('managed_service_')]
|
if key.startswith('managed_service_')]
|
||||||
if managed_services:
|
if managed_services:
|
||||||
join_controller.handle_services(hostname, managed_services)
|
join_controller.handle_services(hostname, managed_services)
|
||||||
# compact json format
|
|
||||||
if 'compact_services' in payload_metadata:
|
compact_services = util.get_compact_services(payload_metadata)
|
||||||
|
if compact_services:
|
||||||
join_controller.handle_compact_services(
|
join_controller.handle_compact_services(
|
||||||
hostname_short, payload_metadata.get('compact_services'))
|
hostname_short, compact_services)
|
||||||
|
|
||||||
ipa.flush_batch_operation()
|
ipa.flush_batch_operation()
|
||||||
|
|
||||||
@event_handlers('compute.instance.delete.end')
|
@event_handlers('compute.instance.delete.end')
|
||||||
|
@ -262,16 +263,16 @@ class NotificationEndpoint(object):
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'compact_services' in metadata:
|
compact_services = util.get_compact_services(metadata)
|
||||||
|
if compact_services:
|
||||||
self.handle_compact_services(ipa, hostname_short,
|
self.handle_compact_services(ipa, hostname_short,
|
||||||
metadata.get('compact_services'))
|
compact_services)
|
||||||
managed_services = [metadata[key] for key in metadata.keys()
|
managed_services = [metadata[key] for key in metadata.keys()
|
||||||
if key.startswith('managed_service_')]
|
if key.startswith('managed_service_')]
|
||||||
if managed_services:
|
if managed_services:
|
||||||
self.handle_managed_services(ipa, managed_services)
|
self.handle_managed_services(ipa, managed_services)
|
||||||
|
|
||||||
def handle_compact_services(self, ipa, host_short,
|
def handle_compact_services(self, ipa, host_short, service_repr):
|
||||||
service_repr_json):
|
|
||||||
"""Reconstructs and removes subhosts for compact services.
|
"""Reconstructs and removes subhosts for compact services.
|
||||||
|
|
||||||
Data looks like this:
|
Data looks like this:
|
||||||
|
@ -286,14 +287,13 @@ class NotificationEndpoint(object):
|
||||||
integrity.
|
integrity.
|
||||||
"""
|
"""
|
||||||
LOG.debug("In handle compact services")
|
LOG.debug("In handle compact services")
|
||||||
service_repr = json.loads(service_repr_json)
|
|
||||||
hosts_found = list()
|
hosts_found = list()
|
||||||
|
|
||||||
ipa.start_batch_operation()
|
ipa.start_batch_operation()
|
||||||
for service_name, net_list in service_repr.items():
|
for service_name, net_list in service_repr.items():
|
||||||
for network in net_list:
|
for network in net_list:
|
||||||
host = "%s.%s" % (host_short, network)
|
host = "%s.%s" % (host_short, network)
|
||||||
principal_host = get_fqdn(host)
|
principal_host = util.get_fqdn(host)
|
||||||
|
|
||||||
# remove host
|
# remove host
|
||||||
if principal_host not in hosts_found:
|
if principal_host not in hosts_found:
|
||||||
|
@ -330,7 +330,7 @@ class NotificationEndpoint(object):
|
||||||
def _generate_hostname(self, hostname):
|
def _generate_hostname(self, hostname):
|
||||||
# FIXME: Don't re-calculate the hostname, fetch it from somewhere
|
# FIXME: Don't re-calculate the hostname, fetch it from somewhere
|
||||||
project = 'foo'
|
project = 'foo'
|
||||||
domain = get_domain()
|
domain = util.get_domain()
|
||||||
if CONF.project_subdomain:
|
if CONF.project_subdomain:
|
||||||
host = '%s.%s.%s' % (hostname, project, domain)
|
host = '%s.%s.%s' % (hostname, project, domain)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright 2018 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Unit Tests for util functions
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from novajoin import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtil(testtools.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestUtil, self).setUp()
|
||||||
|
|
||||||
|
def test_get_compact_services(self):
|
||||||
|
result = {"http": ["internalapi", "ctlplane", "storage"],
|
||||||
|
"rabbitmq": ["internalapi", "ctlplane"]}
|
||||||
|
old_metadata = {"compact_services": json.dumps(result)}
|
||||||
|
new_metadata = {
|
||||||
|
"compact_service_http": json.dumps(result['http']),
|
||||||
|
"compact_service_rabbitmq": json.dumps(result['rabbitmq'])}
|
||||||
|
|
||||||
|
self.assertDictEqual(util.get_compact_services(old_metadata), result)
|
||||||
|
|
||||||
|
self.assertDictEqual(util.get_compact_services(new_metadata), result)
|
||||||
|
|
||||||
|
def test_get_compact_services_empty(self):
|
||||||
|
self.assertIsNone(util.get_compact_services({}))
|
|
@ -14,10 +14,13 @@
|
||||||
|
|
||||||
"""Utility functions shared between notify and server"""
|
"""Utility functions shared between notify and server"""
|
||||||
|
|
||||||
from novajoin.errors import ConfigurationError
|
import json
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
import six
|
||||||
|
|
||||||
|
from novajoin.errors import ConfigurationError
|
||||||
from novajoin.ipa import ipalib_imported
|
from novajoin.ipa import ipalib_imported
|
||||||
if ipalib_imported:
|
if ipalib_imported:
|
||||||
from ipalib import api
|
from ipalib import api
|
||||||
|
@ -54,3 +57,32 @@ def get_fqdn(hostname, project_name=None):
|
||||||
return '%s.%s.%s' % (hostname, project_name, domain)
|
return '%s.%s.%s' % (hostname, project_name, domain)
|
||||||
else:
|
else:
|
||||||
return '%s.%s' % (hostname, domain)
|
return '%s.%s' % (hostname, domain)
|
||||||
|
|
||||||
|
|
||||||
|
def get_compact_services(metadata):
|
||||||
|
"""Retrieve and convert the compact_services from instance metadata.
|
||||||
|
|
||||||
|
This converts the new compact services format to the old/internal one.
|
||||||
|
The old format looks like:
|
||||||
|
|
||||||
|
"compact_services": {
|
||||||
|
"http": ["internalapi", "ctlplane", "storage"],
|
||||||
|
"rabbitmq": ["internalapi", "ctlplane"]
|
||||||
|
}
|
||||||
|
|
||||||
|
The new format contains service names inside the primary key:
|
||||||
|
|
||||||
|
"compact_services_http": ["internalapi", "ctlplane", "storage"],
|
||||||
|
"compact_services_rabbitmq": ["internalapi", "ctlplane"]
|
||||||
|
"""
|
||||||
|
# compact key-per-service
|
||||||
|
compact_services = {key.split('_', 2)[-1]: json.loads(value)
|
||||||
|
for key, value in six.iteritems(metadata)
|
||||||
|
if key.startswith('compact_service_')}
|
||||||
|
if compact_services:
|
||||||
|
return compact_services
|
||||||
|
# legacy compact json format
|
||||||
|
if 'compact_services' in metadata:
|
||||||
|
return json.loads(metadata['compact_services'])
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
Loading…
Reference in New Issue