Replace nova vendor metadata code

Two generic contexts to handle nova vendor metadata have
been implemented in charm-helpers. So, replace the existing
one here in order to simplify and unify the implementation
across all charms that handle vendor metadata.

Change-Id: I2a802c763f2f4403a6dfb17575aa742ca8072e96
Related-Bug: #1777714
This commit is contained in:
Rodrigo Barbieri 2019-05-17 10:43:36 -03:00
parent b4a468f9aa
commit 206970c6c1
11 changed files with 283 additions and 130 deletions

View File

@ -220,6 +220,8 @@ def process_certificates(service_name, relation_id, unit,
:type user: str :type user: str
:param group: (Optional) Group of certificate files. Defaults to 'root' :param group: (Optional) Group of certificate files. Defaults to 'root'
:type group: str :type group: str
:returns: True if certificates processed for local unit or False
:rtype: bool
""" """
data = relation_get(rid=relation_id, unit=unit) data = relation_get(rid=relation_id, unit=unit)
ssl_dir = os.path.join('/etc/apache2/ssl/', service_name) ssl_dir = os.path.join('/etc/apache2/ssl/', service_name)
@ -235,6 +237,8 @@ def process_certificates(service_name, relation_id, unit,
create_ip_cert_links( create_ip_cert_links(
ssl_dir, ssl_dir,
custom_hostname_link=custom_hostname_link) custom_hostname_link=custom_hostname_link)
return True
return False
def get_requests_for_local_unit(relation_name=None): def get_requests_for_local_unit(relation_name=None):

View File

@ -114,9 +114,15 @@ except ImportError:
apt_install('python3-psutil', fatal=True) apt_install('python3-psutil', fatal=True)
import psutil import psutil
if six.PY3:
json_error = json.decoder.JSONDecodeError
else:
json_error = ValueError
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
ADDRESS_TYPES = ['admin', 'internal', 'public'] ADDRESS_TYPES = ['admin', 'internal', 'public']
HAPROXY_RUN_DIR = '/var/run/haproxy/' HAPROXY_RUN_DIR = '/var/run/haproxy/'
DEFAULT_OSLO_MESSAGING_DRIVER = "messagingv2"
def ensure_packages(packages): def ensure_packages(packages):
@ -351,10 +357,70 @@ class IdentityServiceContext(OSContextGenerator):
return cachedir return cachedir
return None return None
def _get_pkg_name(self, python_name='keystonemiddleware'):
"""Get corresponding distro installed package for python
package name.
:param python_name: nameof the python package
:type: string
"""
pkg_names = map(lambda x: x + python_name, ('python3-', 'python-'))
for pkg in pkg_names:
if not filter_installed_packages((pkg,)):
return pkg
return None
def _get_keystone_authtoken_ctxt(self, ctxt, keystonemiddleware_os_rel):
"""Build Jinja2 context for full rendering of [keystone_authtoken]
section with variable names included. Re-constructed from former
template 'section-keystone-auth-mitaka'.
:param ctxt: Jinja2 context returned from self.__call__()
:type: dict
:param keystonemiddleware_os_rel: OpenStack release name of
keystonemiddleware package installed
"""
c = collections.OrderedDict((('auth_type', 'password'),))
# 'www_authenticate_uri' replaced 'auth_uri' since Stein,
# see keystonemiddleware upstream sources for more info
if CompareOpenStackReleases(keystonemiddleware_os_rel) >= 'stein':
c.update((
('www_authenticate_uri', "{}://{}:{}/v3".format(
ctxt.get('service_protocol', ''),
ctxt.get('service_host', ''),
ctxt.get('service_port', ''))),))
else:
c.update((
('auth_uri', "{}://{}:{}/v3".format(
ctxt.get('service_protocol', ''),
ctxt.get('service_host', ''),
ctxt.get('service_port', ''))),))
c.update((
('auth_url', "{}://{}:{}/v3".format(
ctxt.get('auth_protocol', ''),
ctxt.get('auth_host', ''),
ctxt.get('auth_port', ''))),
('project_domain_name', ctxt.get('admin_domain_name', '')),
('user_domain_name', ctxt.get('admin_domain_name', '')),
('project_name', ctxt.get('admin_tenant_name', '')),
('username', ctxt.get('admin_user', '')),
('password', ctxt.get('admin_password', '')),
('signing_dir', ctxt.get('signing_dir', '')),))
return c
def __call__(self): def __call__(self):
log('Generating template context for ' + self.rel_name, level=DEBUG) log('Generating template context for ' + self.rel_name, level=DEBUG)
ctxt = {} ctxt = {}
keystonemiddleware_os_release = None
if self._get_pkg_name():
keystonemiddleware_os_release = os_release(self._get_pkg_name())
cachedir = self._setup_pki_cache() cachedir = self._setup_pki_cache()
if cachedir: if cachedir:
ctxt['signing_dir'] = cachedir ctxt['signing_dir'] = cachedir
@ -385,6 +451,14 @@ class IdentityServiceContext(OSContextGenerator):
ctxt.update({'admin_domain_name': ctxt.update({'admin_domain_name':
rdata.get('service_domain')}) rdata.get('service_domain')})
# we keep all veriables in ctxt for compatibility and
# add nested dictionary for keystone_authtoken generic
# templating
if keystonemiddleware_os_release:
ctxt['keystone_authtoken'] = \
self._get_keystone_authtoken_ctxt(
ctxt, keystonemiddleware_os_release)
if self.context_complete(ctxt): if self.context_complete(ctxt):
# NOTE(jamespage) this is required for >= icehouse # NOTE(jamespage) this is required for >= icehouse
# so a missing value just indicates keystone needs # so a missing value just indicates keystone needs
@ -452,6 +526,80 @@ class IdentityCredentialsContext(IdentityServiceContext):
return {} return {}
class NovaVendorMetadataContext(OSContextGenerator):
"""Context used for configuring nova vendor metadata on nova.conf file."""
def __init__(self, os_release_pkg, interfaces=None):
"""Initialize the NovaVendorMetadataContext object.
:param os_release_pkg: the package name to extract the OpenStack
release codename from.
:type os_release_pkg: str
:param interfaces: list of string values to be used as the Context's
relation interfaces.
:type interfaces: List[str]
"""
self.os_release_pkg = os_release_pkg
if interfaces is not None:
self.interfaces = interfaces
def __call__(self):
cmp_os_release = CompareOpenStackReleases(
os_release(self.os_release_pkg))
ctxt = {}
vdata_providers = []
vdata = config('vendor-data')
vdata_url = config('vendor-data-url')
if vdata:
ctxt['vendor_data'] = True
# Mitaka does not support DynamicJSON
# so vendordata_providers is not needed
if cmp_os_release > 'mitaka':
vdata_providers.append('StaticJSON')
if vdata_url:
if cmp_os_release > 'mitaka':
ctxt['vendor_data_url'] = vdata_url
vdata_providers.append('DynamicJSON')
else:
log('Dynamic vendor data unsupported'
' for {}.'.format(cmp_os_release), level=ERROR)
if vdata_providers:
ctxt['vendordata_providers'] = ','.join(vdata_providers)
return ctxt
class NovaVendorMetadataJSONContext(OSContextGenerator):
"""Context used for writing nova vendor metadata json file."""
def __init__(self, os_release_pkg):
"""Initialize the NovaVendorMetadataJSONContext object.
:param os_release_pkg: the package name to extract the OpenStack
release codename from.
:type os_release_pkg: str
"""
self.os_release_pkg = os_release_pkg
def __call__(self):
ctxt = {'vendor_data_json': '{}'}
vdata = config('vendor-data')
if vdata:
try:
# validate the JSON. If invalid, we return empty.
json.loads(vdata)
except (TypeError, json_error) as e:
log('Error decoding vendor-data. {}'.format(e), level=ERROR)
else:
ctxt['vendor_data_json'] = vdata
return ctxt
class AMQPContext(OSContextGenerator): class AMQPContext(OSContextGenerator):
def __init__(self, ssl_dir=None, rel_name='amqp', relation_prefix=None, def __init__(self, ssl_dir=None, rel_name='amqp', relation_prefix=None,
@ -569,6 +717,19 @@ class AMQPContext(OSContextGenerator):
ctxt['oslo_messaging_flags'] = config_flags_parser( ctxt['oslo_messaging_flags'] = config_flags_parser(
oslo_messaging_flags) oslo_messaging_flags)
oslo_messaging_driver = conf.get(
'oslo-messaging-driver', DEFAULT_OSLO_MESSAGING_DRIVER)
if oslo_messaging_driver:
ctxt['oslo_messaging_driver'] = oslo_messaging_driver
notification_format = conf.get('notification-format', None)
if notification_format:
ctxt['notification_format'] = notification_format
send_notifications_to_logs = conf.get('send-notifications-to-logs', None)
if send_notifications_to_logs:
ctxt['send_notifications_to_logs'] = send_notifications_to_logs
if not self.complete: if not self.complete:
return {} return {}
@ -1110,7 +1271,9 @@ class NeutronPortContext(OSContextGenerator):
hwaddr_to_nic = {} hwaddr_to_nic = {}
hwaddr_to_ip = {} hwaddr_to_ip = {}
for nic in list_nics(): extant_nics = list_nics()
for nic in extant_nics:
# Ignore virtual interfaces (bond masters will be identified from # Ignore virtual interfaces (bond masters will be identified from
# their slaves) # their slaves)
if not is_phy_iface(nic): if not is_phy_iface(nic):
@ -1141,10 +1304,11 @@ class NeutronPortContext(OSContextGenerator):
# Entry is a MAC address for a valid interface that doesn't # Entry is a MAC address for a valid interface that doesn't
# have an IP address assigned yet. # have an IP address assigned yet.
resolved.append(hwaddr_to_nic[entry]) resolved.append(hwaddr_to_nic[entry])
else: elif entry in extant_nics:
# If the passed entry is not a MAC address, assume it's a valid # If the passed entry is not a MAC address and the interface
# interface, and that the user put it there on purpose (we can # exists, assume it's a valid interface, and that the user put
# trust it to be the real external network). # it there on purpose (we can trust it to be the real external
# network).
resolved.append(entry) resolved.append(entry)
# Ensure no duplicates # Ensure no duplicates
@ -1526,6 +1690,14 @@ class NeutronAPIContext(OSContextGenerator):
'rel_key': 'enable-nsg-logging', 'rel_key': 'enable-nsg-logging',
'default': False, 'default': False,
}, },
'global_physnet_mtu': {
'rel_key': 'global-physnet-mtu',
'default': 1500,
},
'physical_network_mtus': {
'rel_key': 'physical-network-mtus',
'default': None,
},
} }
ctxt = self.get_neutron_options({}) ctxt = self.get_neutron_options({})
for rid in relation_ids('neutron-plugin-api'): for rid in relation_ids('neutron-plugin-api'):
@ -1587,13 +1759,13 @@ class DataPortContext(NeutronPortContext):
def __call__(self): def __call__(self):
ports = config('data-port') ports = config('data-port')
if ports: if ports:
# Map of {port/mac:bridge} # Map of {bridge:port/mac}
portmap = parse_data_port_mappings(ports) portmap = parse_data_port_mappings(ports)
ports = portmap.keys() ports = portmap.keys()
# Resolve provided ports or mac addresses and filter out those # Resolve provided ports or mac addresses and filter out those
# already attached to a bridge. # already attached to a bridge.
resolved = self.resolve_ports(ports) resolved = self.resolve_ports(ports)
# FIXME: is this necessary? # Rebuild port index using resolved and filtered ports.
normalized = {get_nic_hwaddr(port): port for port in resolved normalized = {get_nic_hwaddr(port): port for port in resolved
if port not in ports} if port not in ports}
normalized.update({port: port for port in resolved normalized.update({port: port for port in resolved

View File

@ -0,0 +1,9 @@
{% if auth_host -%}
[keystone_authtoken]
{% for option_name, option_value in keystone_authtoken.items() -%}
{{ option_name }} = {{ option_value }}
{% endfor -%}
{% if use_memcache == true %}
memcached_servers = {{ memcache_url }}
{% endif -%}
{% endif -%}

View File

@ -1,11 +1,15 @@
{% if transport_url -%} {% if transport_url -%}
[oslo_messaging_notifications] [oslo_messaging_notifications]
driver = messagingv2 driver = {{ oslo_messaging_driver }}
transport_url = {{ transport_url }} transport_url = {{ transport_url }}
{% if send_notifications_to_logs %}
driver = log
{% endif %}
{% if notification_topics -%} {% if notification_topics -%}
topics = {{ notification_topics }} topics = {{ notification_topics }}
{% endif -%} {% endif -%}
{% if notification_format -%} {% if notification_format -%}
[notifications]
notification_format = {{ notification_format }} notification_format = {{ notification_format }}
{% endif -%} {% endif -%}
{% endif -%} {% endif -%}

View File

@ -0,0 +1 @@
{{ vendor_data_json }}

View File

@ -194,7 +194,7 @@ SWIFT_CODENAMES = OrderedDict([
('rocky', ('rocky',
['2.18.0', '2.19.0']), ['2.18.0', '2.19.0']),
('stein', ('stein',
['2.20.0']), ['2.20.0', '2.21.0']),
]) ])
# >= Liberty version->codename mapping # >= Liberty version->codename mapping

View File

@ -2,7 +2,6 @@
import os import os
import uuid import uuid
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
log, ERROR,
config, config,
relation_get, relation_get,
relation_ids, relation_ids,
@ -14,6 +13,8 @@ from charmhelpers.contrib.openstack.context import (
OSContextGenerator, OSContextGenerator,
NeutronAPIContext, NeutronAPIContext,
config_flags_parser, config_flags_parser,
NovaVendorMetadataContext,
NovaVendorMetadataJSONContext,
) )
from charmhelpers.contrib.openstack.utils import ( from charmhelpers.contrib.openstack.utils import (
os_release, os_release,
@ -163,32 +164,18 @@ class NeutronGatewayContext(NeutronAPIContext):
return ctxt return ctxt
class NovaMetadataContext(OSContextGenerator): class NovaMetadataContext(NovaVendorMetadataContext):
def __init__(self, rel_name='quantum-network-service'): def __init__(self, rel_name='quantum-network-service'):
super(NovaMetadataContext, self).__init__('neutron-common', [rel_name])
self.rel_name = rel_name self.rel_name = rel_name
self.interfaces = [rel_name]
def __call__(self): def __call__(self):
ctxt = {} ctxt = {}
cmp_os_release = CompareOpenStackReleases(os_release('neutron-common')) cmp_os_release = CompareOpenStackReleases(os_release('neutron-common'))
if cmp_os_release < 'rocky': if cmp_os_release < 'rocky':
vdata_providers = [] # if release is Rocky or later, we don't set vendor metadata here
vdata = config('vendor-data') ctxt.update(super(NovaMetadataContext, self).__call__())
vdata_url = config('vendor-data-url')
if vdata:
ctxt['vendor_data'] = True
vdata_providers.append('StaticJSON')
if vdata_url:
if cmp_os_release > 'mitaka':
ctxt['vendor_data_url'] = vdata_url
vdata_providers.append('DynamicJSON')
else:
log('Dynamic vendor data unsupported'
' for {}.'.format(cmp_os_release), level=ERROR)
ctxt['vendordata_providers'] = ','.join(vdata_providers)
for rid in relation_ids(self.rel_name): for rid in relation_ids(self.rel_name):
for unit in related_units(rid): for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit) rdata = relation_get(rid=rid, unit=unit)
@ -207,6 +194,20 @@ class NovaMetadataContext(OSContextGenerator):
return ctxt return ctxt
class NovaMetadataJSONContext(NovaVendorMetadataJSONContext):
def __call__(self):
vdata_values = super(NovaMetadataJSONContext, self).__call__()
cmp_os_release = CompareOpenStackReleases(os_release('neutron-common'))
if cmp_os_release < 'rocky':
return vdata_values
else:
# if release is Rocky or later, we don't set vendor metadata here
return {'vendor_data_json': '{}'}
SHARED_SECRET = "/etc/{}/secret.txt" SHARED_SECRET = "/etc/{}/secret.txt"

View File

@ -67,7 +67,6 @@ from neutron_utils import (
assess_status, assess_status,
install_systemd_override, install_systemd_override,
configure_apparmor, configure_apparmor,
write_vendordata,
pause_unit_helper, pause_unit_helper,
resume_unit_helper, resume_unit_helper,
remove_legacy_nova_metadata, remove_legacy_nova_metadata,
@ -131,9 +130,6 @@ def config_changed():
create_sysctl(sysctl_settings, create_sysctl(sysctl_settings,
'/etc/sysctl.d/50-quantum-gateway.conf') '/etc/sysctl.d/50-quantum-gateway.conf')
if config('vendor-data'):
write_vendordata(config('vendor-data'))
# Re-run joined hooks as config might have changed # Re-run joined hooks as config might have changed
for r_id in relation_ids('amqp'): for r_id in relation_ids('amqp'):
amqp_joined(relation_id=r_id) amqp_joined(relation_id=r_id)

View File

@ -1,5 +1,4 @@
import os import os
import json
import shutil import shutil
import subprocess import subprocess
from shutil import copy2 from shutil import copy2
@ -76,6 +75,7 @@ from neutron_contexts import (
NeutronGatewayContext, NeutronGatewayContext,
L3AgentContext, L3AgentContext,
NovaMetadataContext, NovaMetadataContext,
NovaMetadataJSONContext,
) )
from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.neutron import (
parse_bridge_mappings, parse_bridge_mappings,
@ -338,6 +338,7 @@ NEUTRON_FWAAS_CONF = "/etc/neutron/fwaas_driver.ini"
NOVA_CONF_DIR = '/etc/nova' NOVA_CONF_DIR = '/etc/nova'
NOVA_CONF = "/etc/nova/nova.conf" NOVA_CONF = "/etc/nova/nova.conf"
VENDORDATA_FILE = '%s/vendor_data.json' % NOVA_CONF_DIR
NOVA_CONFIG_FILES = { NOVA_CONFIG_FILES = {
NOVA_CONF: { NOVA_CONF: {
@ -356,6 +357,10 @@ NOVA_CONFIG_FILES = {
context.AppArmorContext(NOVA_API_METADATA_AA_PROFILE) context.AppArmorContext(NOVA_API_METADATA_AA_PROFILE)
], ],
}, },
VENDORDATA_FILE: {
'services': [],
'hook_contexts': [NovaMetadataJSONContext('neutron-common')],
},
} }
NEUTRON_SHARED_CONFIG_FILES = { NEUTRON_SHARED_CONFIG_FILES = {
@ -1068,20 +1073,6 @@ def configure_apparmor():
context.AppArmorContext(profile).setup_aa_profile() context.AppArmorContext(profile).setup_aa_profile()
VENDORDATA_FILE = '/etc/nova/vendor_data.json'
def write_vendordata(vdata):
try:
json_vdata = json.loads(vdata)
except (TypeError, json.decoder.JSONDecodeError) as e:
log('Error decoding vendor-data. {}'.format(e), level=ERROR)
return False
with open(VENDORDATA_FILE, 'w') as vdata_file:
vdata_file.write(json.dumps(json_vdata, sort_keys=True, indent=2))
return True
def get_availability_zone(): def get_availability_zone():
use_juju_az = config('customize-failure-domain') use_juju_az = config('customize-failure-domain')
juju_az = os.environ.get('JUJU_AVAILABILITY_ZONE') juju_az = os.environ.get('JUJU_AVAILABILITY_ZONE')

View File

@ -453,88 +453,83 @@ class TestNovaMetadataContext(CharmTestCase):
TO_PATCH) TO_PATCH)
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
@patch.object(neutron_contexts.NovaVendorMetadataContext, '__call__')
@patch.object(neutron_contexts, 'get_local_ip') @patch.object(neutron_contexts, 'get_local_ip')
@patch.object(neutron_contexts, 'get_shared_secret') @patch.object(neutron_contexts, 'get_shared_secret')
@patch.object(neutron_contexts, 'relation_ids') @patch.object(neutron_contexts, 'relation_ids')
def test_vendordata_static(self, _relation_ids, _get_shared_secret, def test_vendordata_queens(self, _relation_ids, _get_shared_secret,
_get_local_ip): _get_local_ip, parent):
_get_shared_secret.return_value = 'asecret' _get_shared_secret.return_value = 'asecret'
_get_local_ip.return_value = '127.0.0.1' _get_local_ip.return_value = '127.0.0.1'
_relation_ids.return_value = [] _relation_ids.return_value = []
_vdata = '{"good": "json"}' _vdata_url = 'http://example.org/vdata'
self.os_release.return_value = 'pike' _vdata_providers = 'StaticJSON,DynamicJSON'
self.os_release.return_value = 'queens'
parent.return_value = {
'vendor_data': True,
'vendor_data_url': _vdata_url,
'vendordata_providers': _vdata_providers,
}
self.test_config.set('vendor-data', _vdata)
ctxt = neutron_contexts.NovaMetadataContext()() ctxt = neutron_contexts.NovaMetadataContext()()
self.assertTrue(ctxt['vendor_data']) self.assertTrue(ctxt['vendor_data'])
self.assertEqual(ctxt['vendordata_providers'], 'StaticJSON') self.assertEqual(ctxt['vendordata_providers'], _vdata_providers)
@patch.object(neutron_contexts, 'get_local_ip')
@patch.object(neutron_contexts, 'get_shared_secret')
@patch.object(neutron_contexts, 'relation_ids')
def test_vendordata_dynamic(self, _relation_ids, _get_shared_secret,
_get_local_ip):
_get_shared_secret.return_value = 'asecret'
_get_local_ip.return_value = '127.0.0.1'
_relation_ids.return_value = []
_vdata_url = 'http://example.org/vdata'
self.os_release.return_value = 'pike'
self.test_config.set('vendor-data-url', _vdata_url)
ctxt = neutron_contexts.NovaMetadataContext()()
self.assertEqual(ctxt['vendor_data_url'], _vdata_url) self.assertEqual(ctxt['vendor_data_url'], _vdata_url)
self.assertEqual(ctxt['vendordata_providers'], 'DynamicJSON')
@patch.object(neutron_contexts.NovaVendorMetadataContext, '__call__')
@patch.object(neutron_contexts, 'get_local_ip') @patch.object(neutron_contexts, 'get_local_ip')
@patch.object(neutron_contexts, 'get_shared_secret') @patch.object(neutron_contexts, 'get_shared_secret')
@patch.object(neutron_contexts, 'relation_ids') @patch.object(neutron_contexts, 'relation_ids')
def test_vendordata_static_and_dynamic(self, _relation_ids, def test_vendordata_rocky(self, _relation_ids, _get_shared_secret,
_get_shared_secret, _get_local_ip): _get_local_ip, parent):
_get_shared_secret.return_value = 'asecret' _get_shared_secret.return_value = 'asecret'
_get_local_ip.return_value = '127.0.0.1' _get_local_ip.return_value = '127.0.0.1'
_relation_ids.return_value = [] _relation_ids.return_value = []
_vdata = '{"good": "json"}' self.os_release.return_value = 'rocky'
_vdata_url = 'http://example.org/vdata' parent.return_value = {
self.os_release.return_value = 'pike' 'vendor_data': True,
'vendor_data_url': 'http://example.org/vdata',
'vendordata_providers': 'StaticJSON,DynamicJSON',
}
self.test_config.set('vendor-data', _vdata)
self.test_config.set('vendor-data-url', _vdata_url)
ctxt = neutron_contexts.NovaMetadataContext()() ctxt = neutron_contexts.NovaMetadataContext()()
self.assertTrue(ctxt['vendor_data']) self.assertNotIn('vendor_data', ctxt)
self.assertEqual(ctxt['vendor_data_url'], _vdata_url) self.assertNotIn('vendor_data_url', ctxt)
self.assertEqual(ctxt['vendordata_providers'], self.assertNotIn('vendordata_providers', ctxt)
'StaticJSON,DynamicJSON')
@patch.object(neutron_contexts, 'get_local_ip') @patch.object(neutron_contexts.NovaVendorMetadataJSONContext, '__call__')
@patch.object(neutron_contexts, 'get_shared_secret') def test_vendordata_json_queens(self, parent):
@patch.object(neutron_contexts, 'relation_ids') self.os_release.return_value = 'queens'
def test_vendordata_mitaka(self, _relation_ids, _get_shared_secret, result = {
_get_local_ip): 'vendor_data_json': '{"good": "json"}',
_get_shared_secret.return_value = 'asecret' }
_get_local_ip.return_value = '127.0.0.1' parent.return_value = result
_relation_ids.return_value = []
_vdata_url = 'http://example.org/vdata'
self.os_release.return_value = 'mitaka'
self.test_config.set('vendor-data-url', _vdata_url) ctxt = neutron_contexts.NovaMetadataJSONContext('neutron-common')()
ctxt = neutron_contexts.NovaMetadataContext()()
self.assertEqual( self.assertEqual(result, ctxt)
ctxt,
{ @patch.object(neutron_contexts.NovaVendorMetadataJSONContext, '__call__')
'nova_metadata_host': '127.0.0.1', def test_vendordata_json_rocky(self, parent):
'nova_metadata_port': '8775', self.os_release.return_value = 'rocky'
'nova_metadata_protocol': 'http', result = {
'shared_secret': 'asecret', 'vendor_data_json': '{}',
'vendordata_providers': ''}) }
parent.return_value = {
'vendor_data_json': '{"good": "json"}',
}
ctxt = neutron_contexts.NovaMetadataJSONContext('neutron-common')()
self.assertEqual(result, ctxt)
@patch.object(neutron_contexts, 'relation_get') @patch.object(neutron_contexts, 'relation_get')
@patch.object(neutron_contexts, 'related_units') @patch.object(neutron_contexts, 'related_units')
@patch.object(neutron_contexts, 'relation_ids') @patch.object(neutron_contexts, 'relation_ids')
def test_NovaMetadataContext(self, _relation_ids, _related_units, def test_NovaMetadataContext_with_relations(self, _relation_ids,
_relation_get): _related_units, _relation_get):
_relation_ids.return_value = ['rid:1'] _relation_ids.return_value = ['rid:1']
_related_units.return_value = ['nova-cloud-contoller/0'] _related_units.return_value = ['nova-cloud-contoller/0']
_relation_get.return_value = { _relation_get.return_value = {
@ -542,7 +537,7 @@ class TestNovaMetadataContext(CharmTestCase):
'nova-metadata-port': '8775', 'nova-metadata-port': '8775',
'nova-metadata-protocol': 'http', 'nova-metadata-protocol': 'http',
'shared-metadata-secret': 'auuid'} 'shared-metadata-secret': 'auuid'}
self.os_release.return_value = 'queens' self.os_release.return_value = 'rocky'
self.assertEqual( self.assertEqual(
neutron_contexts.NovaMetadataContext()(), neutron_contexts.NovaMetadataContext()(),
@ -550,24 +545,23 @@ class TestNovaMetadataContext(CharmTestCase):
'nova_metadata_host': '10.0.0.10', 'nova_metadata_host': '10.0.0.10',
'nova_metadata_port': '8775', 'nova_metadata_port': '8775',
'nova_metadata_protocol': 'http', 'nova_metadata_protocol': 'http',
'shared_secret': 'auuid', 'shared_secret': 'auuid'})
'vendordata_providers': ''})
@patch.object(neutron_contexts, 'get_local_ip') @patch.object(neutron_contexts, 'get_local_ip')
@patch.object(neutron_contexts, 'get_shared_secret') @patch.object(neutron_contexts, 'get_shared_secret')
@patch.object(neutron_contexts, 'relation_ids') @patch.object(neutron_contexts, 'relation_ids')
def test_NovaMetadataContext_legacy(self, _relation_ids, def test_NovaMetadataContext_no_relations(self, _relation_ids,
_get_shared_secret, _get_shared_secret,
_get_local_ip): _get_local_ip):
_relation_ids.return_value = [] _relation_ids.return_value = []
_get_shared_secret.return_value = 'buuid' _get_shared_secret.return_value = 'buuid'
_get_local_ip.return_value = '127.0.0.1' _get_local_ip.return_value = '127.0.0.1'
self.os_release.return_value = 'pike' self.os_release.return_value = 'rocky'
self.assertEqual( self.assertEqual(
neutron_contexts.NovaMetadataContext()(), neutron_contexts.NovaMetadataContext()(),
{ {
'nova_metadata_host': '127.0.0.1', 'nova_metadata_host': '127.0.0.1',
'nova_metadata_port': '8775', 'nova_metadata_port': '8775',
'nova_metadata_protocol': 'http', 'nova_metadata_protocol': 'http',
'shared_secret': 'buuid', 'shared_secret': 'buuid'})
'vendordata_providers': ''})

View File

@ -11,10 +11,6 @@ from test_utils import (
CharmTestCase CharmTestCase
) )
from test_neutron_contexts import (
patch_open
)
TO_PATCH = [ TO_PATCH = [
'config', 'config',
'get_os_codename_install_source', 'get_os_codename_install_source',
@ -855,21 +851,6 @@ class TestNeutronUtils(CharmTestCase):
for config in EXC_CONFIG: for config in EXC_CONFIG:
self.assertTrue(config not in actual_configs) self.assertTrue(config not in actual_configs)
def test_write_valid_json_vendordata(self):
_jdata = '{"good": "json"}'
_tdata = '{\n "good": "json"\n}'
with patch_open() as (_open, _file):
self.assertEqual(neutron_utils.write_vendordata(_jdata), True)
_open.assert_called_with(neutron_utils.VENDORDATA_FILE, 'w')
_file.write.assert_called_with(_tdata)
@patch('json.loads')
def test_write_invalid_json_vendordata(self, _json_loads):
_jdata = '{ bad json }'
_json_loads.side_effect = TypeError
with patch_open() as (_open, _file):
self.assertEqual(neutron_utils.write_vendordata(_jdata), False)
@patch.object(neutron_utils.os.environ, 'get') @patch.object(neutron_utils.os.environ, 'get')
def test_get_az_customize_with_env(self, os_environ_get_mock): def test_get_az_customize_with_env(self, os_environ_get_mock):
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get