Use hosts official name for FQDN
The current implementations use of a specific interface to build FQDN from has the undesired side effect of the ``nova-compute`` and ``neutron-openvswitch`` charms ending up with using different hostnames in some situations. It may also lead to use of a identifier that is mutable throughout the lifetime of a deployment. Use of a specific interface was chosen due to ``socket.getfqdn()`` not giving reliable results (https://bugs.python.org/issue5004). This patch gets the FQDN by mimickingthe behaviour of a call to ``hostname -f`` with fallback to shortname on failure. Add relevant update from c-h. Needed-By: Ic8f8742261b773484687985aa0a366391cd2737a Change-Id: I82db81937e5a46dc6bd222b7160ca1fa5b190c10 Closes-Bug: #1839300
This commit is contained in:
parent
b4e6d2c73b
commit
1869bfbc97
@ -2177,9 +2177,66 @@ class LogrotateContext(OSContextGenerator):
|
|||||||
class HostInfoContext(OSContextGenerator):
|
class HostInfoContext(OSContextGenerator):
|
||||||
"""Context to provide host information."""
|
"""Context to provide host information."""
|
||||||
|
|
||||||
|
def __init__(self, use_fqdn_hint_cb=None):
|
||||||
|
"""Initialize HostInfoContext
|
||||||
|
|
||||||
|
:param use_fqdn_hint_cb: Callback whose return value used to populate
|
||||||
|
`use_fqdn_hint`
|
||||||
|
:type use_fqdn_hint_cb: Callable[[], bool]
|
||||||
|
"""
|
||||||
|
# Store callback used to get hint for whether FQDN should be used
|
||||||
|
|
||||||
|
# Depending on the workload a charm manages, the use of FQDN vs.
|
||||||
|
# shortname may be a deploy-time decision, i.e. behaviour can not
|
||||||
|
# change on charm upgrade or post-deployment configuration change.
|
||||||
|
|
||||||
|
# The hint is passed on as a flag in the context to allow the decision
|
||||||
|
# to be made in the Jinja2 configuration template.
|
||||||
|
self.use_fqdn_hint_cb = use_fqdn_hint_cb
|
||||||
|
|
||||||
|
def _get_canonical_name(self, name=None):
|
||||||
|
"""Get the official FQDN of the host
|
||||||
|
|
||||||
|
The implementation of ``socket.getfqdn()`` in the standard Python
|
||||||
|
library does not exhaust all methods of getting the official name
|
||||||
|
of a host ref Python issue https://bugs.python.org/issue5004
|
||||||
|
|
||||||
|
This function mimics the behaviour of a call to ``hostname -f`` to
|
||||||
|
get the official FQDN but returns an empty string if it is
|
||||||
|
unsuccessful.
|
||||||
|
|
||||||
|
:param name: Shortname to get FQDN on
|
||||||
|
:type name: Optional[str]
|
||||||
|
:returns: The official FQDN for host or empty string ('')
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
name = name or socket.gethostname()
|
||||||
|
fqdn = ''
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
exc = socket.error
|
||||||
|
else:
|
||||||
|
exc = OSError
|
||||||
|
|
||||||
|
try:
|
||||||
|
addrs = socket.getaddrinfo(
|
||||||
|
name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME)
|
||||||
|
except exc:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for addr in addrs:
|
||||||
|
if addr[3]:
|
||||||
|
if '.' in addr[3]:
|
||||||
|
fqdn = addr[3]
|
||||||
|
break
|
||||||
|
return fqdn
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
|
name = socket.gethostname()
|
||||||
ctxt = {
|
ctxt = {
|
||||||
'host_fqdn': socket.getfqdn(),
|
'host_fqdn': self._get_canonical_name(name) or name,
|
||||||
'host': socket.gethostname(),
|
'host': name,
|
||||||
|
'use_fqdn_hint': (
|
||||||
|
self.use_fqdn_hint_cb() if self.use_fqdn_hint_cb else False)
|
||||||
}
|
}
|
||||||
return ctxt
|
return ctxt
|
||||||
|
@ -16,7 +16,6 @@ import json
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from charmhelpers.core.unitdata import kv
|
from charmhelpers.core.unitdata import kv
|
||||||
@ -795,23 +794,6 @@ class HostIPContext(context.OSContextGenerator):
|
|||||||
# NOTE: do not format this even for ipv6 (see bug 1499656)
|
# NOTE: do not format this even for ipv6 (see bug 1499656)
|
||||||
ctxt['host_ip'] = host_ip
|
ctxt['host_ip'] = host_ip
|
||||||
|
|
||||||
# the contents of the Nova ``host`` configuration option is
|
|
||||||
# referenced throughout a OpenStack deployment, an example being
|
|
||||||
# Neutron port bindings. It's value should not change after a
|
|
||||||
# individual units initial deployment.
|
|
||||||
#
|
|
||||||
# We do want to migrate to using FQDNs so we enable this for new
|
|
||||||
# installations.
|
|
||||||
db = kv()
|
|
||||||
if db.get('nova-compute-charm-use-fqdn', False):
|
|
||||||
fqdn = socket.getfqdn(host_ip)
|
|
||||||
if '.' in fqdn:
|
|
||||||
# only populate the value if getfqdn() is able to find an
|
|
||||||
# actual FQDN for this host. If not, we revert back to
|
|
||||||
# not setting the configuration option and use Nova's
|
|
||||||
# default behaviour.
|
|
||||||
ctxt['host'] = fqdn
|
|
||||||
|
|
||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ from nova_compute_utils import (
|
|||||||
get_availability_zone,
|
get_availability_zone,
|
||||||
remove_old_packages,
|
remove_old_packages,
|
||||||
MULTIPATH_PACKAGES,
|
MULTIPATH_PACKAGES,
|
||||||
|
USE_FQDN_KEY,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.network.ip import (
|
from charmhelpers.contrib.network.ip import (
|
||||||
@ -163,7 +164,7 @@ def install():
|
|||||||
release = os_release('nova-common')
|
release = os_release('nova-common')
|
||||||
if CompareOpenStackReleases(release) >= 'stein':
|
if CompareOpenStackReleases(release) >= 'stein':
|
||||||
db = kv()
|
db = kv()
|
||||||
db.set('nova-compute-charm-use-fqdn', True)
|
db.set(USE_FQDN_KEY, True)
|
||||||
db.flush()
|
db.flush()
|
||||||
|
|
||||||
install_vaultlocker()
|
install_vaultlocker()
|
||||||
|
@ -180,6 +180,19 @@ NOVA_NETWORK_AA_PROFILE_PATH = ('/etc/apparmor.d/{}'
|
|||||||
|
|
||||||
LIBVIRT_TYPES = ['kvm', 'qemu', 'lxc']
|
LIBVIRT_TYPES = ['kvm', 'qemu', 'lxc']
|
||||||
|
|
||||||
|
USE_FQDN_KEY = 'nova-compute-charm-use-fqdn'
|
||||||
|
|
||||||
|
|
||||||
|
def use_fqdn_hint():
|
||||||
|
"""Hint for whether FQDN should be used for agent registration
|
||||||
|
|
||||||
|
:returns: True or False
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
db = kv()
|
||||||
|
return db.get(USE_FQDN_KEY, False)
|
||||||
|
|
||||||
|
|
||||||
BASE_RESOURCE_MAP = {
|
BASE_RESOURCE_MAP = {
|
||||||
NOVA_CONF: {
|
NOVA_CONF: {
|
||||||
'services': ['nova-compute'],
|
'services': ['nova-compute'],
|
||||||
@ -213,7 +226,9 @@ BASE_RESOURCE_MAP = {
|
|||||||
vaultlocker.VaultKVContext(
|
vaultlocker.VaultKVContext(
|
||||||
vaultlocker.VAULTLOCKER_BACKEND),
|
vaultlocker.VAULTLOCKER_BACKEND),
|
||||||
context.IdentityCredentialsContext(
|
context.IdentityCredentialsContext(
|
||||||
rel_name='cloud-credentials')],
|
rel_name='cloud-credentials'),
|
||||||
|
context.HostInfoContext(use_fqdn_hint_cb=use_fqdn_hint),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
VENDORDATA_FILE: {
|
VENDORDATA_FILE: {
|
||||||
'services': [],
|
'services': [],
|
||||||
|
@ -17,8 +17,8 @@ enabled_apis=osapi_compute,metadata
|
|||||||
my_ip = {{ host_ip }}
|
my_ip = {{ host_ip }}
|
||||||
force_raw_images = {{ force_raw_images }}
|
force_raw_images = {{ force_raw_images }}
|
||||||
|
|
||||||
{% if host -%}
|
{% if use_fqdn_hint and host_fqdn -%}
|
||||||
host = {{ host }}
|
host = {{ host_fqdn }}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{% if debug -%}
|
{% if debug -%}
|
||||||
|
@ -20,8 +20,8 @@ enabled_apis=osapi_compute,metadata
|
|||||||
my_ip = {{ host_ip }}
|
my_ip = {{ host_ip }}
|
||||||
force_raw_images = {{ force_raw_images }}
|
force_raw_images = {{ force_raw_images }}
|
||||||
|
|
||||||
{% if host -%}
|
{% if use_fqdn_hint and host_fqdn -%}
|
||||||
host = {{ host }}
|
host = {{ host_fqdn }}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{% if debug -%}
|
{% if debug -%}
|
||||||
|
@ -19,6 +19,7 @@ os.environ['JUJU_UNIT_NAME'] = 'nova_compute'
|
|||||||
|
|
||||||
with patch('charmhelpers.core.hookenv.config') as config:
|
with patch('charmhelpers.core.hookenv.config') as config:
|
||||||
config.return_value = 'nova'
|
config.return_value = 'nova'
|
||||||
|
with patch('charmhelpers.contrib.openstack.context.HostInfoContext'):
|
||||||
import nova_compute_utils as utils # noqa
|
import nova_compute_utils as utils # noqa
|
||||||
|
|
||||||
with patch('nova_compute_utils.restart_map'):
|
with patch('nova_compute_utils.restart_map'):
|
||||||
|
@ -656,25 +656,14 @@ class NovaComputeContextTests(CharmTestCase):
|
|||||||
libvirt = context.NovaComputeLibvirtContext()
|
libvirt = context.NovaComputeLibvirtContext()
|
||||||
self.assertFalse('cpu-mode' in libvirt())
|
self.assertFalse('cpu-mode' in libvirt())
|
||||||
|
|
||||||
@patch.object(context.socket, 'getfqdn')
|
|
||||||
@patch('subprocess.call')
|
@patch('subprocess.call')
|
||||||
def test_host_IP_context(self, _call, _getfqdn):
|
def test_host_IP_context(self, _call):
|
||||||
self.log = fake_log
|
self.log = fake_log
|
||||||
self.get_relation_ip.return_value = '172.24.0.79'
|
self.get_relation_ip.return_value = '172.24.0.79'
|
||||||
self.kv.return_value = FakeUnitdata()
|
|
||||||
host_ip = context.HostIPContext()
|
host_ip = context.HostIPContext()
|
||||||
self.assertEqual({'host_ip': '172.24.0.79'}, host_ip())
|
self.assertEqual({'host_ip': '172.24.0.79'}, host_ip())
|
||||||
self.get_relation_ip.assert_called_with('cloud-compute',
|
self.get_relation_ip.assert_called_with('cloud-compute',
|
||||||
cidr_network=None)
|
cidr_network=None)
|
||||||
self.kv.return_value = FakeUnitdata(
|
|
||||||
**{'nova-compute-charm-use-fqdn': True})
|
|
||||||
_getfqdn.return_value = 'some'
|
|
||||||
host_ip = context.HostIPContext()
|
|
||||||
self.assertEqual({'host_ip': '172.24.0.79'}, host_ip())
|
|
||||||
_getfqdn.return_value = 'some.hostname'
|
|
||||||
host_ip = context.HostIPContext()
|
|
||||||
self.assertDictEqual({'host': 'some.hostname',
|
|
||||||
'host_ip': '172.24.0.79'}, host_ip())
|
|
||||||
|
|
||||||
@patch('subprocess.call')
|
@patch('subprocess.call')
|
||||||
def test_host_IP_context_ipv6(self, _call):
|
def test_host_IP_context_ipv6(self, _call):
|
||||||
|
@ -129,7 +129,7 @@ class NovaComputeRelationsTests(CharmTestCase):
|
|||||||
kv = MagicMock()
|
kv = MagicMock()
|
||||||
_kv.return_value = kv
|
_kv.return_value = kv
|
||||||
hooks.install()
|
hooks.install()
|
||||||
kv.set.assert_called_once_with('nova-compute-charm-use-fqdn', True)
|
kv.set.assert_called_once_with(hooks.USE_FQDN_KEY, True)
|
||||||
kv.flush.assert_called_once_with()
|
kv.flush.assert_called_once_with()
|
||||||
|
|
||||||
@patch.object(hooks, 'ceph_changed')
|
@patch.object(hooks, 'ceph_changed')
|
||||||
|
@ -1134,3 +1134,10 @@ class NovaComputeUtilsTests(CharmTestCase):
|
|||||||
_hook_name.return_value = "post-series-upgrade"
|
_hook_name.return_value = "post-series-upgrade"
|
||||||
self.assertEqual(_full,
|
self.assertEqual(_full,
|
||||||
utils.services_to_pause_or_resume())
|
utils.services_to_pause_or_resume())
|
||||||
|
|
||||||
|
@patch.object(utils, 'kv')
|
||||||
|
def test_use_fqdn_hint(self, _kv):
|
||||||
|
_kv().get.return_value = False
|
||||||
|
self.assertEquals(utils.use_fqdn_hint(), False)
|
||||||
|
_kv().get.return_value = True
|
||||||
|
self.assertEquals(utils.use_fqdn_hint(), True)
|
||||||
|
Loading…
Reference in New Issue
Block a user