Merged trunk next in

This commit is contained in:
Liam Young 2014-09-30 07:40:58 +01:00
commit 1b0c519619
7 changed files with 140 additions and 29 deletions

View File

@ -67,12 +67,13 @@ class NeutronCCContext(context.NeutronContext):
determine_api_port(api_port('neutron-server'))
for rid in relation_ids('neutron-api'):
for unit in related_units(rid):
ctxt['nova_url'] = relation_get(attribute='nova_url',
rid=rid,
unit=unit)
cell_type = relation_get(attribute='cell_type',
rid=rid,
unit=unit)
rdata = relation_get(rid=rid, unit=unit)
cell_type = rdata.get('cell_type')
ctxt['nova_url'] = rdata.get('nova_url')
ctxt['restart_trigger'] = rdata.get('restart_trigger')
# If there are multiple nova-cloud-controllers joined to this
# service in a cell deployment then ignore the non-api cell
# ones
if cell_type and not cell_type == "api":
continue
if ctxt['nova_url']:

View File

@ -19,7 +19,8 @@ from charmhelpers.core.hookenv import (
)
from charmhelpers.core.host import (
restart_on_change
restart_on_change,
service_restart,
)
from charmhelpers.fetch import (
@ -35,14 +36,15 @@ from charmhelpers.contrib.openstack.neutron import (
)
from neutron_api_utils import (
determine_packages,
determine_ports,
register_configs,
restart_map,
CLUSTER_RES,
NEUTRON_CONF,
api_port,
CLUSTER_RES,
determine_packages,
determine_ports,
do_openstack_upgrade,
migrate_neutron_database,
register_configs,
restart_map,
)
from charmhelpers.contrib.hahelpers.cluster import (
@ -129,6 +131,30 @@ def amqp_changed():
CONFIGS.write(NEUTRON_CONF)
def conditional_neutron_migration():
# This is an attempt to stop a race over the db migration between nova-cc
# and neutron-api by having the migration master decided by the presence
# of the neutron-api relation. In the long term this should only be done
# the neutron-api charm and nova-cc should play no hand in it
# * neutron-api refuses to run migrations until neutron-api relation is
# present
# * nova-cc refuses to run migration if neutron-api relations is present
clustered = relation_get('clustered')
if not relation_ids('neutron-api'):
log('Not running neutron database migration, no nova-cloud-controller'
'is present.')
else:
if clustered:
if is_leader(CLUSTER_RES):
migrate_neutron_database()
service_restart('neutron-server')
else:
log('Not running neutron database migration, not leader')
else:
migrate_neutron_database()
service_restart('neutron-server')
@hooks.hook('shared-db-relation-joined')
def db_joined():
if is_relation_made('pgsql-db'):
@ -162,6 +188,7 @@ def db_changed():
log('shared-db relation incomplete. Peer not ready?')
return
CONFIGS.write_all()
conditional_neutron_migration()
@hooks.hook('pgsql-db-relation-changed')
@ -170,6 +197,7 @@ def postgresql_neutron_db_changed():
plugin = config('neutron-plugin')
# DB config might have been moved to main neutron.conf in H?
CONFIGS.write(neutron_plugin_attribute(plugin, 'config'))
conditional_neutron_migration()
@hooks.hook('amqp-relation-broken',

View File

@ -19,6 +19,7 @@ from charmhelpers.core.hookenv import (
)
from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
import neutron_api_context
import subprocess
TEMPLATES = 'templates/'
@ -196,3 +197,18 @@ def do_openstack_upgrade(configs):
# set CONFIGS to load templates from new release
configs.set_release(openstack_release=new_os_rel)
migrate_neutron_database()
def migrate_neutron_database():
'''Runs neutron-db-manage to init a new database or migrate existing'''
log('Migrating the neutron database.')
plugin = config('neutron-plugin')
cmd = ['neutron-db-manage',
'--config-file', NEUTRON_CONF,
'--config-file', neutron_plugin_attribute(plugin,
'config',
'neutron'),
'upgrade',
'head']
subprocess.check_output(cmd)

View File

@ -1,6 +1,7 @@
###############################################################################
# [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten.
## Restart trigger {{ restart_trigger }}
###############################################################################
[DEFAULT]
verbose = {{ verbose }}

View File

@ -1,6 +1,5 @@
from test_utils import CharmTestCase
from test_utils import patch_open
from mock import patch, MagicMock
from mock import patch
import neutron_api_context as context
import charmhelpers
TO_PATCH = [
@ -56,6 +55,7 @@ class HAProxyContextTest(CharmTestCase):
super(HAProxyContextTest, self).setUp(context, TO_PATCH)
self.determine_api_port.return_value = 9686
self.determine_apache_port.return_value = 9686
self.api_port = 9696
def tearDown(self):
super(HAProxyContextTest, self).tearDown()
@ -65,7 +65,8 @@ class HAProxyContextTest(CharmTestCase):
def test_context_No_peers(self, _log, _rids):
_rids.return_value = []
hap_ctxt = context.HAProxyContext()
self.assertTrue('units' not in hap_ctxt())
with patch('__builtin__.__import__'):
self.assertTrue('units' not in hap_ctxt())
@patch.object(charmhelpers.contrib.openstack.context, 'config')
@patch.object(charmhelpers.contrib.openstack.context, 'local_unit')
@ -74,8 +75,10 @@ class HAProxyContextTest(CharmTestCase):
@patch.object(charmhelpers.contrib.openstack.context, 'related_units')
@patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
@patch.object(charmhelpers.contrib.openstack.context, 'log')
def test_context_peers(self, _log, _rids, _runits, _rget, _uget,
_lunit, _config):
@patch('__builtin__.__import__')
@patch('__builtin__.open')
def test_context_peers(self, _open, _import, _log, _rids, _runits, _rget,
_uget, _lunit, _config):
unit_addresses = {
'neutron-api-0': '10.10.10.10',
'neutron-api-1': '10.10.10.11',
@ -93,11 +96,10 @@ class HAProxyContextTest(CharmTestCase):
'service_ports': service_ports,
'neutron_bind_port': 9686,
}
with patch_open() as (_open, _file):
_file.write = MagicMock()
hap_ctxt = context.HAProxyContext()
self.assertEquals(hap_ctxt(), ctxt_data)
_file.write.assert_called_with('ENABLED=1\n')
_import().api_port.return_value = 9696
hap_ctxt = context.HAProxyContext()
self.assertEquals(hap_ctxt(), ctxt_data)
_open.assert_called_with('/etc/default/haproxy', 'w')
class NeutronAPIContextsTest(CharmTestCase):
@ -119,28 +121,32 @@ class NeutronAPIContextsTest(CharmTestCase):
@patch.object(context.NeutronCCContext, 'network_manager')
@patch.object(context.NeutronCCContext, 'plugin')
def test_neutroncc_context_no_setting(self, plugin, nm):
@patch('__builtin__.__import__')
def test_neutroncc_context_no_setting(self, _import, plugin, nm):
plugin.return_value = None
napi_ctxt = context.NeutronCCContext()
ctxt_data = {
'debug': True,
'external_network': 'bob',
'neutron_bind_port': self.api_port,
'verbose': True,
}
napi_ctxt = context.NeutronCCContext()
with patch.object(napi_ctxt, '_ensure_packages'):
self.assertEquals(ctxt_data, napi_ctxt())
@patch.object(context.NeutronCCContext, 'network_manager')
@patch.object(context.NeutronCCContext, 'plugin')
def test_neutroncc_context_api_rel(self, plugin, nm):
@patch('__builtin__.__import__')
def test_neutroncc_context_api_rel(self, _import, plugin, nm):
nova_url = 'http://127.0.0.10'
plugin.return_value = None
self.related_units.return_value = ['unit1']
self.relation_ids.return_value = ['rid2']
self.test_relation.set({'nova_url': nova_url})
self.test_relation.set({'nova_url': nova_url,
'restart_trigger': 'bob'})
napi_ctxt = context.NeutronCCContext()
self.assertEquals(nova_url, napi_ctxt()['nova_url'])
self.assertEquals('bob', napi_ctxt()['restart_trigger'])
self.assertEquals(self.api_port, napi_ctxt()['neutron_bind_port'])
def test_neutroncc_context_manager(self):

View File

@ -42,6 +42,8 @@ TO_PATCH = [
'unit_get',
'get_iface_for_address',
'get_netmask_for_address',
'migrate_neutron_database',
'service_restart',
]
NEUTRON_CONF_DIR = "/etc/neutron"
@ -154,19 +156,23 @@ class NeutronAPIHooksTests(CharmTestCase):
'Attempting to associate a postgresql database when'
' there is already associated a mysql one')
def test_shared_db_changed(self):
@patch.object(hooks, 'conditional_neutron_migration')
def test_shared_db_changed(self, cond_neutron_mig):
self.CONFIGS.complete_contexts.return_value = ['shared-db']
self._call_hook('shared-db-relation-changed')
self.assertTrue(self.CONFIGS.write_all.called)
cond_neutron_mig.assert_called_with()
def test_shared_db_changed_partial_ctxt(self):
self.CONFIGS.complete_contexts.return_value = []
self._call_hook('shared-db-relation-changed')
self.assertFalse(self.CONFIGS.write_all.called)
def test_pgsql_db_changed(self):
@patch.object(hooks, 'conditional_neutron_migration')
def test_pgsql_db_changed(self, cond_neutron_mig):
self._call_hook('pgsql-db-relation-changed')
self.assertTrue(self.CONFIGS.write.called)
cond_neutron_mig.assert_called_with()
def test_amqp_broken(self):
self._call_hook('amqp-relation-broken')
@ -356,3 +362,44 @@ class NeutronAPIHooksTests(CharmTestCase):
self.check_call.assert_called_with(['a2dissite',
'openstack_https_frontend'])
self.assertTrue(_id_rel_joined.called)
def test_conditional_neutron_migration_no_ncc_rel(self):
self.test_relation.set({
'clustered': 'false',
})
self.relation_ids.return_value = []
hooks.conditional_neutron_migration()
self.log.assert_called_with(
'Not running neutron database migration, no nova-cloud-controller'
'is present.'
)
def test_conditional_neutron_migration_ncc_rel_leader(self):
self.test_relation.set({
'clustered': 'true',
})
self.is_leader.return_value = True
hooks.conditional_neutron_migration()
self.migrate_neutron_database.assert_called_with()
self.service_restart.assert_called_with('neutron-server')
def test_conditional_neutron_migration_ncc_rel_notleader(self):
self.test_relation.set({
'clustered': 'true',
})
self.is_leader.return_value = False
hooks.conditional_neutron_migration()
self.assertFalse(self.migrate_neutron_database.called)
self.assertFalse(self.service_restart.called)
self.log.assert_called_with(
'Not running neutron database migration, not leader'
)
def test_conditional_neutron_migration_not_clustered(self):
self.test_relation.set({
'clustered': 'false',
})
self.relation_ids.return_value = ['nova-cc/o']
hooks.conditional_neutron_migration()
self.migrate_neutron_database.assert_called_with()
self.service_restart.assert_called_with('neutron-server')

View File

@ -5,7 +5,9 @@ import charmhelpers.contrib.openstack.templating as templating
templating.OSConfigRenderer = MagicMock()
import neutron_api_utils as nutils
with patch('charmhelpers.core.hookenv.config') as config:
config.return_value = 'neutron'
import neutron_api_utils as nutils
from test_utils import (
CharmTestCase,
@ -26,6 +28,7 @@ TO_PATCH = [
'log',
'neutron_plugin_attribute',
'os_release',
'subprocess',
]
@ -166,3 +169,12 @@ class TestNeutronAPIUtils(CharmTestCase):
self.configure_installation_source.assert_called_with(
'cloud:precise-havana'
)
def test_migrate_neutron_database(self):
nutils.migrate_neutron_database()
cmd = ['neutron-db-manage',
'--config-file', '/etc/neutron/neutron.conf',
'--config-file', '/etc/neutron/plugins/ml2/ml2_conf.ini',
'upgrade',
'head']
self.subprocess.check_output.assert_called_with(cmd)