Merge "Create templated config files"

This commit is contained in:
Jenkins 2013-07-16 05:11:54 +00:00 committed by Gerrit Code Review
commit db9c21c881
13 changed files with 262 additions and 101 deletions

View File

@ -18,3 +18,4 @@ python-swiftclient
iso8601
oslo.config>=1.1.0
jsonschema>=1.0.0,!=1.4.0,<2
Jinja2

46
trove/common/template.py Normal file
View File

@ -0,0 +1,46 @@
# Copyright 2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
_ENV = None
import jinja2
def get_env():
global _ENV
if not _ENV:
_ENV = create_env()
return _ENV
def create_env():
loader = jinja2.ChoiceLoader([
jinja2.FileSystemLoader("/etc/trove/templates"),
jinja2.PackageLoader("trove", "templates")
])
return jinja2.Environment(loader=loader)
class SingleInstanceConfigTemplate(object):
_location_types = {'mysql': '/etc/mysql/my.cnf',
'percona': '/etc/mysql/my.cnf'}
def __init__(self, location_type, flavor_dict):
self.config_location = self._location_types[location_type]
self.flavor_dict = flavor_dict
self.template = get_env().get_template("mysql.config.template")
def render(self):
self.config_contents = self.template.render(
flavor=self.flavor_dict)

View File

@ -205,7 +205,7 @@ class API(proxy.RpcProxy):
def prepare(self, memory_mb, databases, users,
device_path='/dev/vdb', mount_point='/mnt/volume',
backup_id=None):
backup_id=None, config_location=None, config_contents=None):
"""Make an asynchronous call to prepare the guest
as a database container optionally includes a backup id for restores
"""
@ -213,19 +213,21 @@ class API(proxy.RpcProxy):
self._cast_with_consumer(
"prepare", databases=databases, memory_mb=memory_mb,
users=users, device_path=device_path, mount_point=mount_point,
backup_id=backup_id)
backup_id=backup_id, config_location=config_location,
config_contents=config_contents)
def restart(self):
"""Restart the MySQL server."""
LOG.debug(_("Sending the call to restart MySQL on the Guest."))
self._call("restart", AGENT_HIGH_TIMEOUT)
def start_db_with_conf_changes(self, updated_memory_size):
def start_db_with_conf_changes(self, config_location, config_contents):
"""Start the MySQL server."""
LOG.debug(_("Sending the call to start MySQL on the Guest with "
"a timeout of %s.") % AGENT_HIGH_TIMEOUT)
self._call("start_db_with_conf_changes", AGENT_HIGH_TIMEOUT,
updated_memory_size=updated_memory_size)
config_location=config_location,
config_contents=config_contents)
def reset_configuration(self, configuration):
"""Ignore running state of MySQL, and just change the config file

View File

@ -18,7 +18,7 @@ CONF = cfg.CONF
class Manager(periodic_task.PeriodicTasks):
@periodic_task.periodic_task(ticks_between_runs=10)
@periodic_task.periodic_task(ticks_between_runs=3)
def update_status(self, context):
"""Update the status of the MySQL service"""
MySqlAppStatus.get().update()
@ -82,7 +82,8 @@ class Manager(periodic_task.PeriodicTasks):
LOG.info(_("Restored database successfully"))
def prepare(self, context, databases, memory_mb, users, device_path=None,
mount_point=None, backup_id=None):
mount_point=None, backup_id=None, config_location=None,
config_contents=None):
"""Makes ready DBAAS on a Guest container."""
MySqlAppStatus.get().begin_mysql_install()
# status end_mysql_install set with secure()
@ -109,7 +110,7 @@ class Manager(periodic_task.PeriodicTasks):
if backup_id:
self._perform_restore(backup_id, context, CONF.mount_point, app)
LOG.info(_("Securing mysql now."))
app.secure(memory_mb)
app.secure(config_location, config_contents)
if backup_id and MySqlAdmin().is_root_enabled():
MySqlAdmin().report_root_enabled(context)
else:
@ -128,9 +129,10 @@ class Manager(periodic_task.PeriodicTasks):
app = MySqlApp(MySqlAppStatus.get())
app.restart()
def start_db_with_conf_changes(self, context, updated_memory_size):
def start_db_with_conf_changes(self, context, config_location,
config_contents):
app = MySqlApp(MySqlAppStatus.get())
app.start_db_with_conf_changes(updated_memory_size)
app.start_db_with_conf_changes(config_location, config_contents)
def stop_db(self, context, do_not_start_on_reboot=False):
app = MySqlApp(MySqlAppStatus.get())

View File

@ -29,10 +29,7 @@ MYSQLD_ARGS = None
PREPARING = False
UUID = False
ORIG_MYCNF = "/etc/mysql/my.cnf"
FINAL_MYCNF = "/var/lib/mysql/my.cnf"
TMP_MYCNF = "/tmp/my.cnf.tmp"
DBAAS_MYCNF = "/etc/dbaas/my.cnf/my.cnf.%dM"
MYSQL_BASE_DIR = "/var/lib/mysql"
CONF = cfg.CONF
@ -614,7 +611,7 @@ class MySqlApp(object):
def complete_install_or_restart(self):
self.status.end_install_or_restart()
def secure(self, memory_mb):
def secure(self, config_location, config_contents):
LOG.info(_("Generating admin password..."))
admin_password = generate_random_password()
@ -625,7 +622,7 @@ class MySqlApp(object):
self._create_admin_user(client, admin_password)
self.stop_db()
self._write_mycnf(memory_mb, admin_password)
self._write_mycnf(admin_password, config_location, config_contents)
self.start_mysql()
LOG.info(_("Dbaas secure complete."))
@ -757,40 +754,27 @@ class MySqlApp(object):
if "No such file or directory" not in str(pe):
raise
def _write_mycnf(self, update_memory_mb, admin_password):
def _write_mycnf(self, admin_password, config_location, config_contents):
"""
Install the set of mysql my.cnf templates from dbaas-mycnf package.
The package generates a template suited for the current
container flavor. Update the os_admin user and password
to the my.cnf file for direct login from localhost
Install the set of mysql my.cnf templates.
Update the os_admin user and password to the my.cnf
file for direct login from localhost
"""
LOG.info(_("Writing my.cnf templates."))
if admin_password is None:
admin_password = get_auth_password()
# As of right here, the admin_password contains the password to be
# applied to the my.cnf file, whether it was there before (and we
# passed it in) or we generated a new one just now (because we didn't
# find it).
with open(TMP_MYCNF, 'w') as t:
t.write(config_contents)
utils.execute_with_timeout("sudo", "mv", TMP_MYCNF,
config_location)
LOG.debug(_("Installing my.cnf templates"))
pkg.pkg_install("dbaas-mycnf", self.TIME_OUT)
LOG.info(_("Replacing my.cnf with template."))
template_path = DBAAS_MYCNF % update_memory_mb
# replace my.cnf with template.
self._replace_mycnf_with_template(template_path, ORIG_MYCNF)
LOG.info(_("Writing new temp my.cnf."))
self._write_temp_mycnf_with_admin_account(ORIG_MYCNF, TMP_MYCNF,
self._write_temp_mycnf_with_admin_account(config_location,
TMP_MYCNF,
admin_password)
# permissions work-around
LOG.info(_("Moving tmp into final."))
utils.execute_with_timeout("sudo", "mv", TMP_MYCNF, FINAL_MYCNF)
LOG.info(_("Removing original my.cnf."))
utils.execute_with_timeout("sudo", "rm", ORIG_MYCNF)
LOG.info(_("Symlinking final my.cnf."))
utils.execute_with_timeout("sudo", "ln", "-s", FINAL_MYCNF, ORIG_MYCNF)
utils.execute_with_timeout("sudo", "mv", TMP_MYCNF,
config_location)
self.wipe_ib_logfiles()
def start_mysql(self, update_db=False):
@ -825,9 +809,8 @@ class MySqlApp(object):
self.status.end_install_or_restart()
raise RuntimeError("Could not start MySQL!")
def start_db_with_conf_changes(self, updated_memory_mb):
LOG.info(_("Starting mysql with conf changes to memory(%s)...")
% updated_memory_mb)
def start_db_with_conf_changes(self, config_location, config_contents):
LOG.info(_("Starting mysql with conf changes..."))
LOG.info(_("inside the guest - self.status.is_mysql_running(%s)...")
% self.status.is_mysql_running)
if self.status.is_mysql_running:
@ -835,14 +818,14 @@ class MySqlApp(object):
"MySQL state == %s!") % self.status)
raise RuntimeError("MySQL not stopped.")
LOG.info(_("Initiating config."))
self._write_mycnf(updated_memory_mb, None)
self._write_mycnf(None, config_location, config_contents)
self.start_mysql(True)
def reset_configuration(self, configuration):
updated_memory_mb = configuration['memory_mb']
LOG.info(_("Changing configuration to memory(%s)...")
% updated_memory_mb)
self._write_mycnf(updated_memory_mb, None)
config_location = configuration['config_location']
config_contents = configuration['config_contents']
LOG.info(_("Resetting configuration"))
self._write_mycnf(None, config_location, config_contents)
def is_installed(self):
#(cp16net) could raise an exception, does it need to be handled here?

View File

@ -17,6 +17,7 @@ import traceback
from eventlet import greenthread
from novaclient import exceptions as nova_exceptions
from trove.common import cfg
from trove.common import template
from trove.common import utils
from trove.common.exception import GuestError
from trove.common.exception import GuestTimeout
@ -110,7 +111,20 @@ class NotifyMixin(object):
payload)
class FreshInstanceTasks(FreshInstance, NotifyMixin):
class ConfigurationMixin(object):
"""Configuration Mixin
Configuration related tasks for instances and resizes.
"""
def _render_config(self, service_type, flavor):
config = template.SingleInstanceConfigTemplate(
service_type, flavor)
config.render()
return config
class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
def create_instance(self, flavor, image_id, databases, users,
service_type, volume_size, security_groups,
@ -136,9 +150,13 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
err = inst_models.InstanceTasks.BUILDING_ERROR_DNS
self._log_and_raise(e, msg, err)
config = self._render_config(service_type, flavor)
if server:
self._guest_prepare(server, flavor['ram'], volume_info,
databases, users, backup_id)
databases, users, backup_id,
config.config_location,
config.config_contents)
if not self.db_info.task_status.is_error:
self.update_db(task_status=inst_models.InstanceTasks.NONE)
@ -331,13 +349,16 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
return server
def _guest_prepare(self, server, flavor_ram, volume_info,
databases, users, backup_id=None):
databases, users, backup_id=None,
config_location=None, config_contents=None):
LOG.info("Entering guest_prepare.")
# Now wait for the response from the create to do additional work
self.guest.prepare(flavor_ram, databases, users,
device_path=volume_info['device_path'],
mount_point=volume_info['mount_point'],
backup_id=backup_id)
backup_id=backup_id,
config_location=config_location,
config_contents=config_contents)
def _create_dns_entry(self):
LOG.debug("%s: Creating dns entry for instance: %s" %
@ -377,7 +398,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin):
(greenthread.getcurrent(), self.id))
class BuiltInstanceTasks(BuiltInstance, NotifyMixin):
class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
"""
Performs the various asynchronous instance related tasks.
"""
@ -563,7 +584,7 @@ class BackupTasks(object):
backup.delete()
class ResizeActionBase(object):
class ResizeActionBase(ConfigurationMixin):
"""Base class for executing a resize action."""
def __init__(self, instance):
@ -711,7 +732,10 @@ class ResizeAction(ResizeActionBase):
% self.instance.id)
LOG.debug("Repairing config.")
try:
config = {'memory_mb': self.old_flavor['ram']}
config = self._render_config(self.instance.service_type,
self.old_flavor)
config = {'config_location': config.config_location,
'config_contents': config.config_contents}
self.instance.guest.reset_configuration(config)
except GuestTimeout as gt:
LOG.exception("Error sending reset_configuration call.")
@ -730,7 +754,10 @@ class ResizeAction(ResizeActionBase):
modify_at=timeutils.isotime())
def _start_mysql(self):
self.instance.guest.start_db_with_conf_changes(self.new_flavor['ram'])
config = self._render_config(self.instance.service_type,
self.new_flavor)
self.instance.guest.start_db_with_conf_changes(config.config_location,
config.config_contents)
class MigrateAction(ResizeActionBase):

View File

@ -0,0 +1,58 @@
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
user = mysql
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
####tmpdir = /tmp
tmpdir = /var/tmp
pid_file = /var/run/mysqld/mysqld.pid
skip-external-locking
key_buffer_size = {{ 50 * flavor['ram']//512 }}M
max_allowed_packet = {{ 1 * flavor['ram']//512 }}M
thread_stack = 192K
thread_cache_size = {{ 4 * flavor['ram']//512 }}
myisam-recover = BACKUP
query_cache_type = 1
query_cache_limit = 1M
query_cache_size = {{ 8 * flavor['ram']//512 }}M
log_error = /var/log/mysql/mysqld.log
innodb_data_file_path = ibdata1:10M:autoextend
innodb_buffer_pool_size = {{ 150 * flavor['ram']//512 }}M
innodb_file_per_table = 1
innodb_log_files_in_group = 2
innodb_log_file_size=50M
innodb_log_buffer_size=25M
connect_timeout = 15
wait_timeout = 120
join_buffer_size = 1M
read_buffer_size = 512K
read_rnd_buffer_size = 512K
sort_buffer_size = 1M
tmp_table_size = {{ 16 * flavor['ram']//512 }}M
max_heap_table_size = {{ 16 * flavor['ram']//512 }}M
table_open_cache = {{ 256 * flavor['ram']//512 }}
table_definition_cache = {{ 256 * flavor['ram']//512 }}
open_files_limit = {{ 512 * flavor['ram']//512 }}
max_user_connections = {{ 100 * flavor['ram']//512 }}
max_connections = {{ 100 * flavor['ram']//512 }}
default_storage_engine = innodb
local-infile = 0
[mysqldump]
quick
quote-names
max_allowed_packet = 16M
[isamchk]
key_buffer = 16M
!includedir /etc/mysql/conf.d/

View File

@ -21,6 +21,7 @@ from novaclient.exceptions import BadRequest
from novaclient.v1_1.servers import Server
from trove.common.exception import PollTimeOut
from trove.common import template
from trove.common import utils
from trove.common.context import TroveContext
from trove.guestagent import api as guest
@ -114,7 +115,11 @@ class ResizeTests(ResizeTestBase):
self._teardown()
def _start_mysql(self):
self.instance.guest.start_db_with_conf_changes(NEW_FLAVOR.ram)
config = template.SingleInstanceConfigTemplate(
"mysql", NEW_FLAVOR.__dict__)
config.render()
self.instance.guest.start_db_with_conf_changes(config.config_location,
config.config_contents)
def test_guest_wont_stop_mysql(self):
self.guest.stop_db(do_not_start_on_reboot=True)\

View File

@ -181,7 +181,8 @@ class FakeGuest(object):
return self.users.get((username, hostname), None)
def prepare(self, memory_mb, databases, users, device_path=None,
mount_point=None, backup_id=None):
mount_point=None, backup_id=None, config_location=None,
config_contents=None):
from trove.instance.models import DBInstance
from trove.instance.models import InstanceServiceStatus
from trove.instance.models import ServiceStatuses
@ -225,7 +226,7 @@ class FakeGuest(object):
# There's nothing to do here, since there is no config to update.
pass
def start_db_with_conf_changes(self, updated_memory_size):
def start_db_with_conf_changes(self, config_location, config_contents):
time.sleep(2)
self._set_status('RUNNING')

View File

@ -0,0 +1,57 @@
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
import testtools
import re
from trove.common import template
from trove.tests.unittests.util import util
class TemplateTest(testtools.TestCase):
def setUp(self):
super(TemplateTest, self).setUp()
util.init_db()
self.env = template.get_env()
self.template = self.env.get_template("mysql.config.template")
self.flavor_dict = {'ram': 1024}
def tearDown(self):
super(TemplateTest, self).tearDown()
def validate_template(self, contents, teststr, test_flavor):
# expected query_cache_size = {{ 8 * flavor_multiplier }}M
flavor_multiplier = test_flavor['ram'] / 512
found_group = None
for line in contents.split('\n'):
m = re.search('^%s.*' % teststr, line)
if m:
found_group = m.group(0)
if not found_group:
raise "Could not find text in template"
# Check that the last group has been rendered
memsize = found_group.split(" ")[2]
self.assertEqual(memsize, "%sM" % (8 * flavor_multiplier))
def test_rendering(self):
rendered = self.template.render(flavor=self.flavor_dict)
self.validate_template(rendered, "query_cache_size", self.flavor_dict)
def test_single_instance_config_rendering(self):
location = "/etc/mysql/my.cnf"
config = template.SingleInstanceConfigTemplate('mysql',
self.flavor_dict)
config.render()
self.assertEqual(location, config.config_location)
self.validate_template(config.config_contents, "query_cache_size",
self.flavor_dict)

View File

@ -177,9 +177,9 @@ class ApiTest(testtools.TestCase):
def test_start_db_with_conf_changes(self):
exp_msg = RpcMsgMatcher('start_db_with_conf_changes',
'updated_memory_size')
'config_location', 'config_contents')
self._mock_rpc_call(exp_msg)
self.api.start_db_with_conf_changes('512')
self.api.start_db_with_conf_changes(None, None)
self._verify_rpc_call(exp_msg)
def test_stop_db(self):
@ -218,12 +218,13 @@ class ApiTest(testtools.TestCase):
when(rpc).create_connection(new=True).thenReturn(mock_conn)
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
'device_path', 'mount_point', 'backup_id')
'device_path', 'mount_point', 'backup_id',
'config_location', 'config_contents')
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
'bkup-1232')
'bkup-1232', 'loc', 'cont')
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)
@ -232,11 +233,12 @@ class ApiTest(testtools.TestCase):
when(rpc).create_connection(new=True).thenReturn(mock_conn)
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
'device_path', 'mount_point', 'backup_id')
'device_path', 'mount_point', 'backup_id',
'config_location', 'config_contents')
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
'backup_id_123')
'backup_id_123', 'loc', 'cont')
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)

View File

@ -61,6 +61,7 @@ FAKE_DB_2 = {"_name": "testDB2", "_character_set": "latin2",
"_collate": "latin2_general_ci"}
FAKE_USER = [{"_name": "random", "_password": "guesswhat",
"_databases": [FAKE_DB]}]
MYCNF = '/etc/mysql/my.cnf'
class FakeAppStatus(MySqlAppStatus):
@ -595,7 +596,7 @@ class MySqlAppTest(testtools.TestCase):
self.mysql_starts_successfully()
self.appStatus.status = ServiceStatuses.SHUTDOWN
self.mySqlApp.start_db_with_conf_changes(Mock())
self.mySqlApp.start_db_with_conf_changes(Mock(), Mock())
self.assertTrue(self.mySqlApp._write_mycnf.called)
self.assertTrue(self.mySqlApp.start_mysql.called)
@ -609,7 +610,8 @@ class MySqlAppTest(testtools.TestCase):
self.appStatus.status = ServiceStatuses.RUNNING
self.assertRaises(RuntimeError,
self.mySqlApp.start_db_with_conf_changes, Mock())
self.mySqlApp.start_db_with_conf_changes,
Mock(), Mock())
class MySqlAppInstallTest(MySqlAppTest):
@ -641,7 +643,7 @@ class MySqlAppInstallTest(MySqlAppTest):
self.mysql_starts_successfully()
sqlalchemy.create_engine = Mock()
self.mySqlApp.secure(100)
self.mySqlApp.secure(MYCNF, 'contents')
self.assertTrue(self.mySqlApp.stop_db.called)
self.assertTrue(self.mySqlApp._write_mycnf.called)
@ -668,13 +670,13 @@ class MySqlAppInstallTest(MySqlAppTest):
self.mySqlApp.start_mysql = Mock()
self.mySqlApp.stop_db = Mock()
self.mySqlApp._write_mycnf = \
Mock(side_effect=pkg.PkgPackageStateError("Install error"))
Mock(side_effect=IOError("Could not write file"))
self.mysql_stops_successfully()
self.mysql_starts_successfully()
sqlalchemy.create_engine = Mock()
self.assertRaises(pkg.PkgPackageStateError,
self.mySqlApp.secure, 100)
self.assertRaises(IOError,
self.mySqlApp.secure, "/etc/mycnf/my.cnf", "foo")
self.assertTrue(self.mySqlApp.stop_db.called)
self.assertTrue(self.mySqlApp._write_mycnf.called)
@ -731,26 +733,6 @@ def mock_admin_sql_connection():
class MySqlAppMockTest(testtools.TestCase):
@classmethod
def stub_file(cls, filename):
return MySqlAppMockTest.StubFile(filename)
class StubFile(object):
def __init__(self, filename):
when(__builtin__).open(filename, any()).thenReturn(self)
def next(self):
raise StopIteration
def __iter__(self):
return self
def write(self, data):
pass
def close(self):
pass
def tearDown(self):
super(MySqlAppMockTest, self).tearDown()
unstub()
@ -760,8 +742,6 @@ class MySqlAppMockTest(testtools.TestCase):
when(mock_conn).execute(any()).thenReturn(None)
when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn(
None)
when(pkg).pkg_install("dbaas-mycnf", any()).thenRaise(
pkg.PkgPackageStateError("Install error"))
# skip writing the file for now
when(os.path).isfile(any()).thenReturn(False)
mock_status = mock(MySqlAppStatus)
@ -769,7 +749,7 @@ class MySqlAppMockTest(testtools.TestCase):
any(), any(), any()).thenReturn(True)
app = MySqlApp(mock_status)
self.assertRaises(pkg.PkgPackageStateError, app.secure, 2048)
self.assertRaises(TypeError, app.secure, MYCNF, None)
verify(mock_conn, atleast=2).execute(any())
inorder.verify(mock_status).wait_for_real_status_to_change_to(
@ -782,20 +762,16 @@ class MySqlAppMockTest(testtools.TestCase):
when(mock_conn).execute(any()).thenReturn(None)
when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn(
None)
when(pkg).pkg_install("dbaas-mycnf", any()).thenReturn(None)
# skip writing the file for now
when(os.path).isfile(any()).thenReturn(False)
when(utils).execute_with_timeout(
"sudo", "chmod", any(), any()).thenReturn(None)
MySqlAppMockTest.stub_file("/etc/mysql/my.cnf")
MySqlAppMockTest.stub_file("/etc/dbaas/my.cnf/my.cnf.2048M")
MySqlAppMockTest.stub_file("/tmp/my.cnf.tmp")
mock_status = mock(MySqlAppStatus)
when(mock_status).wait_for_real_status_to_change_to(
any(), any(), any()).thenReturn(True)
app = MySqlApp(mock_status)
app.secure(2048)
when(app)._write_mycnf(any(), any()).thenReturn(True)
app.secure(MYCNF, 'foo')
verify(mock_conn, never).execute(TextClauseMatcher('root'))

View File

@ -181,7 +181,8 @@ class GuestAgentManagerTest(testtools.TestCase):
if backup_id:
verify(backup).restore(self.context, backup_id, '/var/lib/mysql')
verify(dbaas.MySqlApp).install_if_needed()
verify(dbaas.MySqlApp).secure('2048')
# We dont need to make sure the exact contents are there
verify(dbaas.MySqlApp).secure(any(), any())
verify(dbaas.MySqlAdmin, never).create_database()
verify(dbaas.MySqlAdmin, never).create_user()
times_report = 1 if is_root_enabled else 0