[gnuoy,r=james-page] Conditional migration of neutron db in neutron-api for >=kilo

This commit is contained in:
James Page 2015-04-23 11:36:55 +01:00
commit c0f642c0cd
4 changed files with 181 additions and 5 deletions

View File

@ -11,6 +11,7 @@ from charmhelpers.core.hookenv import (
UnregisteredHookError, UnregisteredHookError,
config, config,
is_relation_made, is_relation_made,
local_unit,
log, log,
ERROR, ERROR,
relation_get, relation_get,
@ -23,6 +24,7 @@ from charmhelpers.core.hookenv import (
from charmhelpers.core.host import ( from charmhelpers.core.host import (
restart_on_change, restart_on_change,
service_reload, service_reload,
service_restart,
) )
from charmhelpers.fetch import ( from charmhelpers.fetch import (
@ -37,10 +39,12 @@ from charmhelpers.contrib.openstack.utils import (
git_install_requested, git_install_requested,
openstack_upgrade_available, openstack_upgrade_available,
os_requires_version, os_requires_version,
os_release,
sync_db_with_multi_ipv6_addresses sync_db_with_multi_ipv6_addresses
) )
from neutron_api_utils import ( from neutron_api_utils import (
CLUSTER_RES,
NEUTRON_CONF, NEUTRON_CONF,
api_port, api_port,
determine_packages, determine_packages,
@ -49,6 +53,7 @@ from neutron_api_utils import (
git_install, git_install,
dvr_router_present, dvr_router_present,
l3ha_router_present, l3ha_router_present,
migrate_neutron_database,
neutron_ready, neutron_ready,
register_configs, register_configs,
restart_map, restart_map,
@ -66,6 +71,7 @@ from neutron_api_context import (
from charmhelpers.contrib.hahelpers.cluster import ( from charmhelpers.contrib.hahelpers.cluster import (
get_hacluster_config, get_hacluster_config,
is_elected_leader,
) )
from charmhelpers.payload.execd import execd_preinstall from charmhelpers.payload.execd import execd_preinstall
@ -91,6 +97,25 @@ hooks = Hooks()
CONFIGS = register_configs() CONFIGS = register_configs()
def conditional_neutron_migration():
if os_release('neutron-server') < 'kilo':
log('Not running neutron database migration as migrations are handled '
'by the neutron-server process or nova-cloud-controller charm.')
return
if is_elected_leader(CLUSTER_RES):
allowed_units = relation_get('allowed_units')
if allowed_units and local_unit() in allowed_units.split():
migrate_neutron_database()
service_restart('neutron-server')
else:
log('Not running neutron database migration, either no'
' allowed_units or this unit is not present')
return
else:
log('Not running neutron database migration, not leader')
def configure_https(): def configure_https():
''' '''
Enables SSL API Apache config if appropriate and kicks identity-service Enables SSL API Apache config if appropriate and kicks identity-service
@ -231,12 +256,14 @@ def db_changed():
log('shared-db relation incomplete. Peer not ready?') log('shared-db relation incomplete. Peer not ready?')
return return
CONFIGS.write_all() CONFIGS.write_all()
conditional_neutron_migration()
@hooks.hook('pgsql-db-relation-changed') @hooks.hook('pgsql-db-relation-changed')
@restart_on_change(restart_map()) @restart_on_change(restart_map())
def postgresql_neutron_db_changed(): def postgresql_neutron_db_changed():
CONFIGS.write(NEUTRON_CONF) CONFIGS.write(NEUTRON_CONF)
conditional_neutron_migration()
@hooks.hook('amqp-relation-broken', @hooks.hook('amqp-relation-broken',

View File

@ -3,6 +3,7 @@ from copy import deepcopy
from functools import partial from functools import partial
import os import os
import shutil import shutil
import subprocess
from base64 import b64encode from base64 import b64encode
from charmhelpers.contrib.openstack import context, templating from charmhelpers.contrib.openstack import context, templating
from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.neutron import (
@ -258,6 +259,7 @@ def do_openstack_upgrade(configs):
:param configs: The charms main OSConfigRenderer object. :param configs: The charms main OSConfigRenderer object.
""" """
cur_os_rel = os_release('neutron-server')
new_src = config('openstack-origin') new_src = config('openstack-origin')
new_os_rel = get_os_codename_install_source(new_src) new_os_rel = get_os_codename_install_source(new_src)
@ -279,6 +281,38 @@ def do_openstack_upgrade(configs):
# set CONFIGS to load templates from new release # set CONFIGS to load templates from new release
configs.set_release(openstack_release=new_os_rel) configs.set_release(openstack_release=new_os_rel)
# Before kilo it's nova-cloud-controllers job
if new_os_rel >= 'kilo':
stamp_neutron_database(cur_os_rel)
migrate_neutron_database()
def stamp_neutron_database(release):
'''Stamp the database with the current release before upgrade.'''
log('Stamping the neutron database with release %s.' % release)
plugin = config('neutron-plugin')
cmd = ['neutron-db-manage',
'--config-file', NEUTRON_CONF,
'--config-file', neutron_plugin_attribute(plugin,
'config',
'neutron'),
'stamp',
release]
subprocess.check_output(cmd)
def migrate_neutron_database():
'''Initializes a new database or upgrades an existing database.'''
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)
def get_topics(): def get_topics():

View File

@ -34,6 +34,7 @@ TO_PATCH = [
'determine_ports', 'determine_ports',
'do_openstack_upgrade', 'do_openstack_upgrade',
'dvr_router_present', 'dvr_router_present',
'local_unit',
'l3ha_router_present', 'l3ha_router_present',
'execd_preinstall', 'execd_preinstall',
'filter_installed_packages', 'filter_installed_packages',
@ -42,15 +43,19 @@ TO_PATCH = [
'get_l2population', 'get_l2population',
'get_overlay_network_type', 'get_overlay_network_type',
'git_install', 'git_install',
'is_elected_leader',
'is_relation_made', 'is_relation_made',
'log', 'log',
'migrate_neutron_database',
'neutron_ready', 'neutron_ready',
'open_port', 'open_port',
'openstack_upgrade_available', 'openstack_upgrade_available',
'os_release',
'os_requires_version', 'os_requires_version',
'relation_get', 'relation_get',
'relation_ids', 'relation_ids',
'relation_set', 'relation_set',
'service_restart',
'unit_get', 'unit_get',
'get_iface_for_address', 'get_iface_for_address',
'get_netmask_for_address', 'get_netmask_for_address',
@ -292,19 +297,23 @@ class NeutronAPIHooksTests(CharmTestCase):
'Attempting to associate a postgresql database when' 'Attempting to associate a postgresql database when'
' there is already associated a mysql one') ' 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.CONFIGS.complete_contexts.return_value = ['shared-db']
self._call_hook('shared-db-relation-changed') self._call_hook('shared-db-relation-changed')
self.assertTrue(self.CONFIGS.write_all.called) self.assertTrue(self.CONFIGS.write_all.called)
cond_neutron_mig.assert_called_with()
def test_shared_db_changed_partial_ctxt(self): def test_shared_db_changed_partial_ctxt(self):
self.CONFIGS.complete_contexts.return_value = [] self.CONFIGS.complete_contexts.return_value = []
self._call_hook('shared-db-relation-changed') self._call_hook('shared-db-relation-changed')
self.assertFalse(self.CONFIGS.write_all.called) 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._call_hook('pgsql-db-relation-changed')
self.assertTrue(self.CONFIGS.write.called) self.assertTrue(self.CONFIGS.write.called)
cond_neutron_mig.assert_called_with()
def test_amqp_broken(self): def test_amqp_broken(self):
self._call_hook('amqp-relation-broken') self._call_hook('amqp-relation-broken')
@ -668,3 +677,46 @@ class NeutronAPIHooksTests(CharmTestCase):
call('service', 'apache2', 'reload')] call('service', 'apache2', 'reload')]
self.check_call.assert_called_has_calls(calls) self.check_call.assert_called_has_calls(calls)
self.assertTrue(_id_rel_joined.called) self.assertTrue(_id_rel_joined.called)
def test_conditional_neutron_migration_icehouse(self):
self.os_release.return_value = 'icehouse'
hooks.conditional_neutron_migration()
self.log.assert_called_with(
'Not running neutron database migration as migrations are handled '
'by the neutron-server process or nova-cloud-controller charm.'
)
def test_conditional_neutron_migration_ncc_rel_leader_juno(self):
self.test_relation.set({
'allowed_units': 'neutron-api/0 neutron-api/1 neutron-api/4',
})
self.local_unit.return_value = 'neutron-api/1'
self.is_elected_leader.return_value = True
self.os_release.return_value = 'juno'
hooks.conditional_neutron_migration()
self.log.assert_called_with(
'Not running neutron database migration as migrations are handled'
' by the neutron-server process or nova-cloud-controller charm.'
)
def test_conditional_neutron_migration_ncc_rel_leader_kilo(self):
self.test_relation.set({
'allowed_units': 'neutron-api/0 neutron-api/1 neutron-api/4',
})
self.local_unit.return_value = 'neutron-api/1'
self.is_elected_leader.return_value = True
self.os_release.return_value = 'kilo'
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.is_elected_leader.return_value = False
self.os_release.return_value = 'juno'
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 as migrations are handled '
'by the neutron-server process or nova-cloud-controller charm.'
)

View File

@ -3,6 +3,7 @@ from mock import MagicMock, patch, call
from collections import OrderedDict from collections import OrderedDict
from copy import deepcopy from copy import deepcopy
import charmhelpers.contrib.openstack.templating as templating import charmhelpers.contrib.openstack.templating as templating
import charmhelpers.contrib.openstack.utils
import neutron_api_context as ncontext import neutron_api_context as ncontext
templating.OSConfigRenderer = MagicMock() templating.OSConfigRenderer = MagicMock()
@ -30,6 +31,7 @@ TO_PATCH = [
'log', 'log',
'neutron_plugin_attribute', 'neutron_plugin_attribute',
'os_release', 'os_release',
'subprocess',
] ]
openstack_origin_git = \ openstack_origin_git = \
@ -73,7 +75,6 @@ class TestNeutronAPIUtils(CharmTestCase):
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
self.test_config.set('region', 'region101') self.test_config.set('region', 'region101')
self.neutron_plugin_attribute.side_effect = _mock_npa self.neutron_plugin_attribute.side_effect = _mock_npa
self.os_release.side_effect = 'trusty'
def tearDown(self): def tearDown(self):
# Reset cached cache # Reset cached cache
@ -182,15 +183,19 @@ class TestNeutronAPIUtils(CharmTestCase):
nutils.keystone_ca_cert_b64() nutils.keystone_ca_cert_b64()
self.assertTrue(self.b64encode.called) self.assertTrue(self.b64encode.called)
@patch.object(nutils, 'migrate_neutron_database')
@patch.object(nutils, 'stamp_neutron_database')
@patch.object(nutils, 'git_install_requested') @patch.object(nutils, 'git_install_requested')
def test_do_openstack_upgrade(self, git_requested): def test_do_openstack_upgrade_juno(self, git_requested,
stamp_neutron_db, migrate_neutron_db):
git_requested.return_value = False git_requested.return_value = False
self.config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get
self.test_config.set('openstack-origin', 'cloud:trusty-juno') self.test_config.set('openstack-origin', 'cloud:trusty-juno')
self.os_release.side_effect = 'icehouse' self.os_release.return_value = 'icehouse'
self.get_os_codename_install_source.return_value = 'juno' self.get_os_codename_install_source.return_value = 'juno'
configs = MagicMock() configs = MagicMock()
nutils.do_openstack_upgrade(configs) nutils.do_openstack_upgrade(configs)
self.os_release.assert_called_with('neutron-server')
self.log.assert_called() self.log.assert_called()
self.configure_installation_source.assert_called_with( self.configure_installation_source.assert_called_with(
'cloud:trusty-juno' 'cloud:trusty-juno'
@ -209,6 +214,46 @@ class TestNeutronAPIUtils(CharmTestCase):
options=dpkg_opts, options=dpkg_opts,
fatal=True) fatal=True)
configs.set_release.assert_called_with(openstack_release='juno') configs.set_release.assert_called_with(openstack_release='juno')
self.assertItemsEqual(stamp_neutron_db.call_args_list, [])
self.assertItemsEqual(migrate_neutron_db.call_args_list, [])
@patch.object(charmhelpers.contrib.openstack.utils,
'get_os_codename_install_source')
@patch.object(nutils, 'migrate_neutron_database')
@patch.object(nutils, 'stamp_neutron_database')
@patch.object(nutils, 'git_install_requested')
def test_do_openstack_upgrade_kilo(self, git_requested,
stamp_neutron_db, migrate_neutron_db,
gsrc):
git_requested.return_value = False
self.os_release.return_value = 'juno'
self.config.side_effect = self.test_config.get
self.test_config.set('openstack-origin', 'cloud:trusty-kilo')
gsrc.return_value = 'kilo'
self.get_os_codename_install_source.return_value = 'kilo'
configs = MagicMock()
nutils.do_openstack_upgrade(configs)
self.os_release.assert_called_with('neutron-server')
self.log.assert_called()
self.configure_installation_source.assert_called_with(
'cloud:trusty-kilo'
)
self.apt_update.assert_called_with(fatal=True)
dpkg_opts = [
'--option', 'Dpkg::Options::=--force-confnew',
'--option', 'Dpkg::Options::=--force-confdef',
]
self.apt_upgrade.assert_called_with(options=dpkg_opts,
fatal=True,
dist=True)
pkgs = nutils.determine_packages()
pkgs.sort()
self.apt_install.assert_called_with(packages=pkgs,
options=dpkg_opts,
fatal=True)
configs.set_release.assert_called_with(openstack_release='kilo')
stamp_neutron_db.assert_called_with('juno')
migrate_neutron_db.assert_called_with()
@patch.object(ncontext, 'IdentityServiceContext') @patch.object(ncontext, 'IdentityServiceContext')
@patch('neutronclient.v2_0.client.Client') @patch('neutronclient.v2_0.client.Client')
@ -402,3 +447,21 @@ class TestNeutronAPIUtils(CharmTestCase):
call('neutron-server'), call('neutron-server'),
] ]
self.assertEquals(service_restart.call_args_list, expected) self.assertEquals(service_restart.call_args_list, expected)
def test_stamp_neutron_database(self):
nutils.stamp_neutron_database('icehouse')
cmd = ['neutron-db-manage',
'--config-file', '/etc/neutron/neutron.conf',
'--config-file', '/etc/neutron/plugins/ml2/ml2_conf.ini',
'stamp',
'icehouse']
self.subprocess.check_output.assert_called_with(cmd)
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)