Allow Juju AZ context information to be used

The change adds an option to the charm to use JUJU_AVAILABILITY_ZONE
environment variable set by Juju for the hook environment based on the
underlying provider's availability zone information for a given machine.

This information is used to configure default_availability_zone for nova
and availability_zone for subordinate networking charms.

Change-Id: Idc7112e7fe7b76d15cf9c4896b702b8ffd8c0e8e
Closes-Bug: #1796068
This commit is contained in:
Dmitrii Shcherbakov 2018-10-04 19:22:43 +03:00
parent e5a8c29f4f
commit 4fb1243100
8 changed files with 201 additions and 5 deletions

View File

@ -43,6 +43,37 @@ different cephx keys and user names.
See LP Bug [#1671422](https://bugs.launchpad.net/charm-cinder-ceph/+bug/1671422)
for more information.
Availability Zones
==================
There are two options to provide default_availability_zone config
for nova nodes:
- default-availability-zone
- customize-failure-domain
The order of precedence is as follows:
1. Information from a Juju provider (JUJU_AVAILABILITY_ZONE)
if customize-failure-domain is set to True and Juju
has set the JUJU_AVAILABILITY_ZONE to a non-empty value;
2. The value of default-availability-zone will be used
if customize-failure-domain is set to True but no
JUJU_AVAILABILITY_ZONE is provided via hook
context by the Juju provider;
3. Otherwise, the value of default-availability-zone
charm option will be used.
The default_availability_zone in Nova affects scheduling if a
given Nova node was not placed into an aggregate with an
availability zone present as a property by an operator. Using
customize-failure-domain is recommended as it provides AZ-aware
scheduling out of the box if an operator specifies an AZ during
instance creation.
These options also affect the AZ propagated down to networking
subordinates which is useful for AZ-aware Neutron agent scheduling.
NFV support
===========

View File

@ -373,11 +373,24 @@ options:
.
This option determines the availability zone to be used when it is not
specified in the VM creation request. If this option is not set, the
default availability zone 'nova' is used.
default availability zone 'nova' is used. If customize-failure-domain
is set to True, it will override this option only if an AZ is set by
the Juju provider. If JUJU_AVAILABILITY_ZONE is not set, the value
specified by this option will be used regardless of
customize-failure-domain's setting.
.
NOTE: Availability zones must be created manually using the
'openstack aggregate create' command.
.
customize-failure-domain:
type: boolean
default: False
description: |
Juju propagates availability zone information to charms from the
underlying machine provider such as MAAS and this option allows the
charm to use JUJU_AVAILABILITY_ZONE to set default_availability_zone for
Nova nodes. This option overrides the default-availability-zone charm
config setting only when the Juju provider sets JUJU_AVAILABILITY_ZONE.
resume-guests-state-on-host-boot:
type: boolean
default: False

View File

@ -51,6 +51,7 @@ from charmhelpers.contrib.network.ip import (
get_relation_ip,
)
# This is just a label and it must be consistent across
# nova-compute nodes to support live migration.
CEPH_SECRET_UUID = '514c9fca-8cbe-11e2-9c52-3bc8c7819472'
@ -86,6 +87,11 @@ def _network_manager():
return manager()
def _get_availability_zone():
from nova_compute_utils import get_availability_zone as get_az
return get_az()
def _neutron_security_groups():
'''
Inspects current cloud-compute relation and determine if nova-c-c has
@ -722,7 +728,5 @@ class NovaComputeAvailabilityZoneContext(context.OSContextGenerator):
def __call__(self):
ctxt = {}
if config('default-availability-zone'):
ctxt['default_availability_zone'] = config(
'default-availability-zone')
ctxt['default_availability_zone'] = _get_availability_zone()
return ctxt

View File

@ -109,6 +109,7 @@ from nova_compute_utils import (
configure_local_ephemeral_storage,
pause_unit_helper,
resume_unit_helper,
get_availability_zone,
)
from charmhelpers.contrib.network.ip import (
@ -464,7 +465,7 @@ def update_nrpe_config():
def neutron_plugin_joined(relid=None, remote_restart=False):
rel_settings = {
'hugepage_number': get_hugepage_number(),
'default_availability_zone': config('default-availability-zone')
'default_availability_zone': get_availability_zone()
}
if remote_restart:
rel_settings['restart-trigger'] = str(uuid.uuid4())

View File

@ -907,3 +907,10 @@ def configure_local_ephemeral_storage():
# storage is never reconfigured by mistake, losing instance disks
db.set('storage-configured', True)
db.flush()
def get_availability_zone():
use_juju_az = config('customize-failure-domain')
juju_az = os.environ.get('JUJU_AVAILABILITY_ZONE')
return (juju_az if use_juju_az and juju_az
else config('default-availability-zone'))

View File

@ -563,3 +563,68 @@ class SerialConsoleContextTests(CharmTestCase):
'host_uuid': self.host_uuid,
'force_raw_images': True,
'reserved_host_memory': 512}, libvirt())
class NovaComputeAvailabilityZoneContextTests(CharmTestCase):
def setUp(self):
super(NovaComputeAvailabilityZoneContextTests,
self).setUp(context, TO_PATCH)
self.os_release.return_value = 'kilo'
@patch('nova_compute_utils.config')
@patch('os.environ.get')
def test_availability_zone_no_juju_with_env(self, mock_get,
mock_config):
def environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': 'az1',
}[key]
mock_get.side_effect = environ_get_side_effect
def config_side_effect(key):
return {
'customize-failure-domain': False,
'default-availability-zone': 'nova',
}[key]
mock_config.side_effect = config_side_effect
az_context = context.NovaComputeAvailabilityZoneContext()
self.assertEqual(
{'default_availability_zone': 'nova'}, az_context())
@patch('nova_compute_utils.config')
@patch('os.environ.get')
def test_availability_zone_no_juju_no_env(self, mock_get,
mock_config):
def environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': '',
}[key]
mock_get.side_effect = environ_get_side_effect
def config_side_effect(key):
return {
'customize-failure-domain': False,
'default-availability-zone': 'nova',
}[key]
mock_config.side_effect = config_side_effect
az_context = context.NovaComputeAvailabilityZoneContext()
self.assertEqual(
{'default_availability_zone': 'nova'}, az_context())
@patch('os.environ.get')
def test_availability_zone_juju(self, mock_get):
def environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': 'az1',
}[key]
mock_get.side_effect = environ_get_side_effect
self.config.side_effect = self.test_config.get
self.test_config.set('customize-failure-domain', True)
az_context = context.NovaComputeAvailabilityZoneContext()
self.assertEqual(
{'default_availability_zone': 'az1'}, az_context())

View File

@ -542,6 +542,29 @@ class NovaComputeRelationsTests(CharmTestCase):
**expect_rel_settings
)
@patch('os.environ.get')
@patch.object(hooks, 'get_hugepage_number')
def test_neutron_plugin_joined_relid_juju_az(self,
get_hugepage_number,
mock_env_get):
self.test_config.set('customize-failure-domain', True)
def environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': 'az1',
}[key]
mock_env_get.side_effect = environ_get_side_effect
get_hugepage_number.return_value = None
hooks.neutron_plugin_joined(relid='relid23')
expect_rel_settings = {
'hugepage_number': None,
'default_availability_zone': 'az1',
}
self.relation_set.assert_called_with(
relation_id='relid23',
**expect_rel_settings
)
@patch.object(hooks, 'get_hugepage_number')
def test_neutron_plugin_joined_huge(self, get_hugepage_number):
get_hugepage_number.return_value = 12

View File

@ -952,3 +952,55 @@ class NovaComputeUtilsTests(CharmTestCase):
priority=80
)
self.is_block_device.assert_not_called()
@patch.object(utils.os.environ, 'get')
def test_get_az_customize_with_env(self, os_environ_get_mock):
self.test_config.set('customize-failure-domain', True)
self.test_config.set('default-availability-zone', 'nova')
def os_environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': 'az1',
}[key]
os_environ_get_mock.side_effect = os_environ_get_side_effect
az = utils.get_availability_zone()
self.assertEqual('az1', az)
@patch.object(utils.os.environ, 'get')
def test_get_az_customize_without_env(self, os_environ_get_mock):
self.test_config.set('customize-failure-domain', True)
self.test_config.set('default-availability-zone', 'mynova')
def os_environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': '',
}[key]
os_environ_get_mock.side_effect = os_environ_get_side_effect
az = utils.get_availability_zone()
self.assertEqual('mynova', az)
@patch.object(utils.os.environ, 'get')
def test_get_az_no_customize_without_env(self, os_environ_get_mock):
self.test_config.set('customize-failure-domain', False)
self.test_config.set('default-availability-zone', 'nova')
def os_environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': '',
}[key]
os_environ_get_mock.side_effect = os_environ_get_side_effect
az = utils.get_availability_zone()
self.assertEqual('nova', az)
@patch.object(utils.os.environ, 'get')
def test_get_az_no_customize_with_env(self, os_environ_get_mock):
self.test_config.set('customize-failure-domain', False)
self.test_config.set('default-availability-zone', 'nova')
def os_environ_get_side_effect(key):
return {
'JUJU_AVAILABILITY_ZONE': 'az1',
}[key]
os_environ_get_mock.side_effect = os_environ_get_side_effect
az = utils.get_availability_zone()
self.assertEqual('nova', az)