rebase on trunk

This commit is contained in:
James Page 2014-02-24 17:43:25 +00:00
commit b49ab82a19
17 changed files with 202 additions and 60 deletions

View File

@ -26,6 +26,12 @@ options:
default: openstack
type: string
decsription: Rabbitmq vhost
use-syslog:
type: boolean
default: False
description: |
By default, all services will log into their corresponding log files.
Setting this to True will force all services to log to the syslog.
database-user:
default: nova
type: string
@ -50,6 +56,11 @@ options:
default: "yes"
type: string
description: Whether to run nova-api and nova-network on the compute nodes.
enable-resize:
default: False
type: boolean
description: Enable instance resizing, which requires that passwordless SSH
access be setup between compute hosts.
enable-live-migration:
default: False
type: boolean

View File

@ -26,9 +26,11 @@ from charmhelpers.core.hookenv import (
)
from charmhelpers.contrib.hahelpers.cluster import (
determine_apache_port,
determine_api_port,
determine_haproxy_port,
https,
is_clustered,
peer_units,
)
from charmhelpers.contrib.hahelpers.apache import (
@ -216,12 +218,7 @@ class AMQPContext(OSContextGenerator):
# Sufficient information found = break out!
break
# Used for active/active rabbitmq >= grizzly
if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \
len(related_units(rid)) > 1:
if relation_get('ha_queues'):
ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
else:
ctxt['rabbitmq_ha_queues'] = False
if 'clustered' not in ctxt and len(related_units(rid)) > 1:
rabbitmq_hosts = []
for unit in related_units(rid):
rabbitmq_hosts.append(relation_get('private-address',
@ -250,13 +247,11 @@ class CephContext(OSContextGenerator):
unit=unit))
auth = relation_get('auth', rid=rid, unit=unit)
key = relation_get('key', rid=rid, unit=unit)
use_syslog = str(config('use-syslog')).lower()
ctxt = {
'mon_hosts': ' '.join(mon_hosts),
'auth': auth,
'key': key,
'use_syslog': use_syslog
}
if not os.path.isdir('/etc/ceph'):
@ -385,9 +380,11 @@ class ApacheSSLContext(OSContextGenerator):
'private_address': unit_get('private-address'),
'endpoints': []
}
for api_port in self.external_ports:
ext_port = determine_apache_port(api_port)
int_port = determine_api_port(api_port)
for ext_port in self.external_ports:
if peer_units() or is_clustered():
int_port = determine_haproxy_port(ext_port)
else:
int_port = determine_api_port(ext_port)
portmap = (int(ext_port), int(int_port))
ctxt['endpoints'].append(portmap)
return ctxt
@ -589,7 +586,6 @@ class SubordinateConfigContext(OSContextGenerator):
class SyslogContext(OSContextGenerator):
def __call__(self):
ctxt = {
'use_syslog': config('use-syslog')

View File

@ -0,0 +1 @@
nova_compute_hooks.py

View File

@ -0,0 +1 @@
nova_compute_hooks.py

View File

@ -48,7 +48,8 @@ from nova_compute_utils import (
register_configs,
NOVA_CONF,
QUANTUM_CONF, NEUTRON_CONF,
ceph_config_file, CEPH_SECRET
ceph_config_file, CEPH_SECRET,
enable_shell, disable_shell
)
from nova_compute_context import CEPH_SECRET_UUID
@ -75,7 +76,14 @@ def config_changed():
# Check-in with nova-c-c and register new ssh key, if it has just been
# generated.
initialize_ssh_keys()
[compute_joined(rid) for rid in relation_ids('cloud-compute')]
if config('enable-resize') is True:
enable_shell(user='nova')
initialize_ssh_keys(user='nova')
else:
disable_shell(user='nova')
[compute_joined(rid) for rid in relation_ids('cloud-compute')]
CONFIGS.write_all()
@ -140,15 +148,19 @@ def image_service_changed():
@hooks.hook('cloud-compute-relation-joined')
def compute_joined(rid=None):
if not migration_enabled():
return
auth_type = config('migration-auth-type')
settings = {
'migration_auth_type': auth_type
}
if auth_type == 'ssh':
settings['ssh_public_key'] = public_ssh_key()
relation_set(relation_id=rid, **settings)
if migration_enabled():
auth_type = config('migration-auth-type')
settings = {
'migration_auth_type': auth_type
}
if auth_type == 'ssh':
settings['ssh_public_key'] = public_ssh_key()
relation_set(relation_id=rid, **settings)
if config('enable-resize'):
settings = {
'nova_ssh_public_key': public_ssh_key(user='nova')
}
relation_set(relation_id=rid, **settings)
@hooks.hook('cloud-compute-relation-changed')
@ -158,6 +170,7 @@ def compute_changed():
# config advertised from controller.
CONFIGS.write_all()
import_authorized_keys()
import_authorized_keys(user='nova', prefix='nova')
import_keystone_ca_cert()
if (network_manager() in ['quantum', 'neutron']
and neutron_plugin() == 'ovs'):
@ -209,6 +222,12 @@ def upgrade_charm():
amqp_joined(relation_id=r_id)
@hooks.hook('nova-ceilometer-relation-changed')
@restart_on_change(restart_map())
def nova_ceilometer_relation_changed():
CONFIGS.write_all()
def main():
try:
hooks.execute(sys.argv)

View File

@ -69,7 +69,13 @@ BASE_RESOURCE_MAP = {
context.OSConfigFlagContext(),
CloudComputeContext(),
NovaComputeLibvirtContext(),
NovaComputeCephContext()],
NovaComputeCephContext(),
context.SyslogContext(),
context.SubordinateConfigContext(
interface='nova-ceilometer',
service='nova',
config_file=NOVA_CONF,
)],
},
}
@ -89,7 +95,9 @@ QUANTUM_CONF = '/etc/quantum/quantum.conf'
QUANTUM_RESOURCES = {
QUANTUM_CONF: {
'services': [],
'contexts': [context.AMQPContext(), NeutronComputeContext()],
'contexts': [context.AMQPContext(),
NeutronComputeContext(),
context.SyslogContext()],
}
}
@ -98,7 +106,9 @@ NEUTRON_CONF = '/etc/neutron/neutron.conf'
NEUTRON_RESOURCES = {
NEUTRON_CONF: {
'services': [],
'contexts': [context.AMQPContext(), NeutronComputeContext()],
'contexts': [context.AMQPContext(),
NeutronComputeContext(),
context.SyslogContext()],
}
}
@ -310,13 +320,18 @@ def initialize_ssh_keys(user='root'):
check_output(['chown', '-R', user, ssh_dir])
def import_authorized_keys(user='root'):
def import_authorized_keys(user='root', prefix=None):
"""Import SSH authorized_keys + known_hosts from a cloud-compute relation
and store in user's $HOME/.ssh.
"""
# XXX: Should this be managed via templates + contexts?
hosts = relation_get('known_hosts')
auth_keys = relation_get('authorized_keys')
if prefix:
hosts = relation_get('{}_known_hosts'.format(prefix))
auth_keys = relation_get('{}_authorized_keys'.format(prefix))
else:
# XXX: Should this be managed via templates + contexts?
hosts = relation_get('known_hosts')
auth_keys = relation_get('authorized_keys')
# XXX: Need to fix charm-helpers to return None for empty settings,
# in all cases.
if not hosts or not auth_keys:
@ -331,25 +346,6 @@ def import_authorized_keys(user='root'):
_hosts.write(b64decode(hosts))
def configure_live_migration(configs=None):
"""
Ensure libvirt live migration is properly configured or disabled,
depending on current config setting.
"""
# dont think we need this
return
configs = configs or register_configs()
configs.write(LIBVIRTD_CONF)
configs.write(LIBVIRT_BIN)
configs.write(NOVA_CONF)
if not migration_enabled():
return
if config('migration-auth-type') == 'ssh':
initialize_ssh_keys()
def do_openstack_upgrade(configs):
new_src = config('openstack-origin')
new_os_rel = get_os_codename_install_source(new_src)
@ -394,3 +390,13 @@ def create_libvirt_secret(secret_file, secret_uuid, key):
cmd = ['virsh', 'secret-set-value', '--secret', secret_uuid,
'--base64', key]
check_call(cmd)
def enable_shell(user):
cmd = ['usermod', '-s', '/bin/bash', user]
check_call(cmd)
def disable_shell(user):
cmd = ['usermod', '-s', '/bin/false', user]
check_call(cmd)

View File

@ -6,7 +6,7 @@ description: |
addition to its "native" API (the OpenStack API), it also supports the Amazon
EC2 API.
categories:
- openstack
- openstack
provides:
cloud-compute:
interface: nova-compute
@ -22,6 +22,9 @@ requires:
nrpe-external-master:
interface: nrpe-external-master
scope: container
nova-ceilometer:
interface: nova-ceilometer
scope: container
peers:
compute-peer:
interface: nova

View File

@ -1 +1 @@
130
131

View File

@ -16,6 +16,7 @@
--connection_type=libvirt
--root_helper=sudo nova-rootwrap
--verbose
--use_syslog={{ use_syslog }}
--ec2_private_dns_show_ip
{% if database_host -%}
--sql_connection=mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}

View File

@ -18,6 +18,7 @@ libvirt_use_virtio_for_bridges=True
connection_type=libvirt
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
verbose=True
use_syslog = {{ use_syslog }}
ec2_private_dns_show_ip=True
api_paste_config=/etc/nova/api-paste.ini
volumes_path=/var/lib/nova/volumes

View File

@ -3,6 +3,9 @@
# [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten.
###############################################################################
[DEFAULT]
use_syslog = {{ use_syslog }}
[OVS]
tunnel_id_ranges = 1:1000
tenant_network_type = gre

View File

@ -11,6 +11,7 @@ bind_port = 9696
{% if core_plugin -%}
core_plugin = {{ core_plugin }}
{% endif -%}
use_syslog = {{ use_syslog }}
api_paste_config = /etc/quantum/api-paste.ini
auth_strategy = keystone
notification_driver = quantum.openstack.common.notifier.rpc_notifier

View File

@ -18,6 +18,7 @@ libvirt_use_virtio_for_bridges=True
connection_type=libvirt
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
verbose=True
use_syslog = {{ use_syslog }}
ec2_private_dns_show_ip=True
api_paste_config=/etc/nova/api-paste.ini
volumes_path=/var/lib/nova/volumes

View File

@ -16,6 +16,7 @@ core_plugin = {{ core_plugin }}
api_paste_config = /etc/neutron/api-paste.ini
auth_strategy = keystone
notification_driver = neutron.openstack.common.notifier.rpc_notifier
use_syslog = {{ use_syslog }}
default_notification_level = INFO
notification_topics = notifications

View File

@ -18,6 +18,7 @@ libvirt_use_virtio_for_bridges=True
connection_type=libvirt
root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf
verbose=True
use_syslog = {{ use_syslog }}
ec2_private_dns_show_ip=True
api_paste_config=/etc/nova/api-paste.ini
volumes_path=/var/lib/nova/volumes
@ -84,3 +85,9 @@ volume_api_class = nova.volume.cinder.API
{% if live_migration_uri -%}
live_migration_uri = {{ live_migration_uri }}
{% endif -%}
{% if sections and 'DEFAULT' in sections -%}
{% for key, value in sections['DEFAULT'] -%}
{{ key }} = {{ value }}
{% endfor -%}
{% endif -%}

View File

@ -48,6 +48,8 @@ TO_PATCH = [
'neutron_plugin',
'public_ssh_key',
'register_configs',
'disable_shell',
'enable_shell',
# misc_utils
'ensure_ceph_keyring',
'execd_preinstall'
@ -95,6 +97,38 @@ class NovaComputeRelationsTests(CharmTestCase):
call('cloud-compute:1'),
]
self.assertEquals(ex, compute_joined.call_args_list)
self.assertTrue(self.initialize_ssh_keys.called)
@patch.object(hooks, 'compute_joined')
def test_config_changed_with_resize(self, compute_joined):
self.test_config.set('enable-resize', True)
self.relation_ids.return_value = [
'cloud-compute:0',
'cloud-compute:1'
]
hooks.config_changed()
ex = [
call('cloud-compute:0'),
call('cloud-compute:1'),
]
self.assertEquals(ex, compute_joined.call_args_list)
self.initialize_ssh_keys.assert_called_with(user='nova')
self.enable_shell.assert_called_with(user='nova')
@patch.object(hooks, 'compute_joined')
def test_config_changed_without_resize(self, compute_joined):
self.test_config.set('enable-resize', False)
self.relation_ids.return_value = [
'cloud-compute:0',
'cloud-compute:1'
]
hooks.config_changed()
ex = [
call('cloud-compute:0'),
call('cloud-compute:1'),
]
self.assertEquals(ex, compute_joined.call_args_list)
self.disable_shell.assert_called_with(user='nova')
@patch.object(hooks, 'compute_joined')
def test_config_changed_no_upgrade_no_migration(self, compute_joined):
@ -239,7 +273,7 @@ class NovaComputeRelationsTests(CharmTestCase):
hooks.image_service_changed()
configs.write.assert_called_with('/etc/nova/nova.conf')
def test_compute_joined_no_migration(self):
def test_compute_joined_no_migration_no_resize(self):
self.migration_enabled.return_value = False
hooks.compute_joined()
self.assertFalse(self.relation_set.called)
@ -261,14 +295,27 @@ class NovaComputeRelationsTests(CharmTestCase):
migration_auth_type='ssh'
)
def test_compute_joined_with_resize(self):
self.test_config.set('enable-resize', True)
self.public_ssh_key.return_value = 'bar'
hooks.compute_joined()
self.relation_set.assert_called_with(
relation_id=None,
nova_ssh_public_key='bar'
)
hooks.compute_joined(rid='cloud-compute:2')
self.relation_set.assert_called_with(
relation_id='cloud-compute:2',
nova_ssh_public_key='bar'
)
def test_compute_changed(self):
hooks.compute_changed()
expected_funcs = [
self.import_authorized_keys,
self.import_keystone_ca_cert,
]
for func in expected_funcs:
self.assertTrue(func.called)
self.assertTrue(self.import_keystone_ca_cert.called)
self.import_authorized_keys.assert_has_calls([
call(),
call(user='nova', prefix='nova'),
])
def test_ceph_joined(self):
hooks.ceph_joined()

View File

@ -179,7 +179,7 @@ class NovaComputeUtilsTests(CharmTestCase):
self.assertFalse(_open.called)
@patch('pwd.getpwnam')
def test_import_authorized_keys(self, getpwnam):
def _test_import_authorized_keys_base(self, getpwnam, prefix=None):
getpwnam.return_value = self.fake_user('foo')
self.relation_get.side_effect = [
'Zm9vX2tleQo=', # relation_get('known_hosts')
@ -200,6 +200,38 @@ class NovaComputeUtilsTests(CharmTestCase):
self.assertEquals(ex_open, _open.call_args_list)
self.assertEquals(ex_write, _file.write.call_args_list)
self.relation_get.assert_has_called([
call('known_hosts').
call('authorized_keys')
])
@patch('pwd.getpwnam')
def test_import_authorized_keys_prefix(self, getpwnam):
getpwnam.return_value = self.fake_user('foo')
self.relation_get.side_effect = [
'Zm9vX2tleQo=', # relation_get('known_hosts')
'Zm9vX2hvc3QK', # relation_get('authorized_keys')
]
ex_open = [
call('/home/foo/.ssh/authorized_keys', 'wb'),
call('/home/foo/.ssh/known_hosts', 'wb')
]
ex_write = [
call('foo_host\n'),
call('foo_key\n'),
]
with patch_open() as (_open, _file):
utils.import_authorized_keys(user='foo', prefix='bar')
self.assertEquals(ex_open, _open.call_args_list)
self.assertEquals(ex_write, _file.write.call_args_list)
self.relation_get.assert_has_called([
call('bar_known_hosts').
call('bar_authorized_keys')
])
@patch('subprocess.check_call')
def test_import_keystone_cert_missing_data(self, check_call):
self.relation_get.return_value = None
@ -247,3 +279,14 @@ class NovaComputeUtilsTests(CharmTestCase):
call('/etc/nova/nova.conf', [ctxt1])
]
self.assertEquals(fake_renderer.register.call_args_list, ex_reg)
@patch.object(utils, 'check_call')
def test_enable_shell(self, _check_call):
utils.enable_shell('dummy')
_check_call.assert_called_with(['usermod', '-s', '/bin/bash', 'dummy'])
@patch.object(utils, 'check_call')
def test_disable_shell(self, _check_call):
utils.disable_shell('dummy')
_check_call.assert_called_with(['usermod', '-s', '/bin/false',
'dummy'])