Merge "Create templated config files"
This commit is contained in:
commit
db9c21c881
@ -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
46
trove/common/template.py
Normal 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)
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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?
|
||||
|
@ -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):
|
||||
|
58
trove/templates/mysql.config.template
Normal file
58
trove/templates/mysql.config.template
Normal 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/
|
@ -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)\
|
||||
|
@ -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')
|
||||
|
||||
|
57
trove/tests/unittests/common/test_template.py
Normal file
57
trove/tests/unittests/common/test_template.py
Normal 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)
|
@ -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)
|
||||
|
||||
|
@ -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'))
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user