Added Redis Crud Operations

blueprint redis-support

Change-Id: I741b3a39e1ec24ee81d441788be72f72726eda10
This commit is contained in:
Conrad Weidenkeller 2013-10-16 07:35:07 -05:00
parent 5588e933fd
commit 287ca3ac4f
10 changed files with 765 additions and 38 deletions

View File

@ -1,16 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation
# All Rights Reserved.
#
# 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.

View File

@ -1,16 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation
# All Rights Reserved.
#
# 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.

View File

@ -0,0 +1,124 @@
# Copyright (c) 2013 Rackspace
# All Rights Reserved.
#
# 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.
from trove.common import cfg
from trove.guestagent import dbaas
from trove.guestagent import volume
from trove.guestagent.datastore.redis.service import RedisAppStatus
from trove.guestagent.datastore.redis.service import RedisApp
from trove.guestagent.datastore.redis import system
from trove.openstack.common import log as logging
from trove.openstack.common.gettextutils import _
from trove.openstack.common import periodic_task
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class Manager(periodic_task.PeriodicTasks):
"""
This is the Redis manager class. It is dynamically loaded
based off of the service_type of the trove instance
"""
@periodic_task.periodic_task(ticks_between_runs=3)
def update_status(self, context):
"""
Updates the redis trove instance. It is decorated with
perodic task so it is automatically called every 3 ticks.
"""
RedisAppStatus.get().update()
def change_passwords(self, context, users):
"""
Changes the redis instance password,
it is currently not not implemented.
"""
raise NotImplemented()
def reset_configuration(self, context, configuration):
"""
Resets to the default configuration,
currently this does nothing.
"""
app = RedisApp(RedisAppStatus.get())
app.reset_configuration(configuration)
def _perform_restore(self, backup_info, context, restore_location, app):
"""
Perform a restore on this instance,
currently it is not implemented.
"""
raise NotImplemented()
def prepare(self, context, packages, databases, memory_mb, users,
device_path=None, mount_point=None, backup_info=None,
config_contents=None, root_password=None):
"""
This is called when the trove instance first comes online.
It is the first rpc message passed from the task manager.
prepare handles all the base configuration of the redis instance.
"""
app = RedisApp(RedisAppStatus.get())
RedisAppStatus.get().begin_install()
if device_path:
device = volume.VolumeDevice(device_path)
device.format()
device.mount(system.REDIS_BASE_DIR)
LOG.debug(_('Mounted the volume.'))
app.install_if_needed(packages)
LOG.info(_('Securing redis now.'))
app.write_config(config_contents)
app.complete_install_or_restart()
LOG.info(_('"prepare" redis call has finished.'))
def restart(self, context):
"""
Restart this redis instance.
This method is called when the guest agent
gets a restart message from the taskmanager.
"""
app = RedisApp(RedisAppStatus.get())
app.restart()
def start_db_with_conf_changes(self, context, config_contents):
"""
Start this redis instance with new conf changes.
Right now this does nothing.
"""
raise NotImplemented()
def stop_db(self, context, do_not_start_on_reboot=False):
"""
Stop this redis instance.
This method is called when the guest agent
gets a stop message from the taskmanager.
"""
app = RedisApp(RedisAppStatus.get())
app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot)
def get_filesystem_stats(self, context, fs_path):
"""
Gets file system stats from the provided fs_path.
"""
return dbaas.get_filesystem_volume_stats(fs_path)
def create_backup(self, context, backup_info):
"""
This will eventually create a backup. Right now
it does nothing.
"""
raise NotImplemented()

View File

@ -0,0 +1,269 @@
# Copyright (c) 2013 Rackspace
# All Rights Reserved.
#
# 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 os
from trove.common import cfg
from trove.common import utils as utils
from trove.common import exception
from trove.common import instance as rd_instance
from trove.guestagent import pkg
from trove.guestagent.datastore import service
from trove.guestagent.datastore.redis import system
from trove.openstack.common import log as logging
from trove.openstack.common.gettextutils import _
LOG = logging.getLogger(__name__)
TMP_REDIS_CONF = '/tmp/redis.conf.tmp'
TIME_OUT = 1200
CONF = cfg.CONF
packager = pkg.Package()
def _load_redis_options():
"""
Reads the redis config file for all redis options.
Right now this does not do any smart parsing and returns only key
value pairs as a str, str.
So: 'foo bar baz' becomes {'foo' : 'bar baz'}
"""
options = {}
with open(system.REDIS_CONFIG, 'r') as fd:
for opt in fd.readlines():
opt = opt.rstrip().split(' ')
options.update({opt[0]: ' '.join(opt[1:])})
return options
class RedisAppStatus(service.BaseDbStatus):
"""
Handles all of the status updating for the redis guest agent.
"""
@classmethod
def get(cls):
"""
Gets an instance of the RedisAppStatus class.
"""
if not cls._instance:
cls._instance = RedisAppStatus()
return cls._instance
def _get_actual_db_status(self):
"""
Gets the actual status of the Redis instance
First it attempts to make a connection to the redis instance
by making a PING request.
If PING does not return PONG we do a ps
to see if the process is blocked or hung.
This implementation stinks but redis-cli only returns 0
at this time.
http://redis.googlecode.com/svn/trunk/redis-cli.c
If we raise another exception.ProcessExecutionError while
running ps.
We attempt to locate the PID file and see if the process
is crashed or shutdown.
Remeber by default execute_with_timeout raises this exception
if a non 0 status code is returned from the cmd called.
"""
options = _load_redis_options()
out = ""
err = ""
try:
if 'requirepass' in options:
LOG.info(_('Password is set running ping with password'))
out, err = utils.execute_with_timeout(
system.REDIS_CLI,
'-a',
options['requirepass'],
'PING',
run_as_root=True,
root_helper='sudo')
else:
LOG.info(_('Password not set running ping without password'))
out, err = utils.execute_with_timeout(
system.REDIS_CLI,
'PING',
run_as_root=True,
root_helper='sudo')
LOG.info(_('Redis is RUNNING.'))
return rd_instance.ServiceStatuses.RUNNING
except exception.ProcessExecutionError:
LOG.error(_('Process execution error on redis-cli'))
if 'PONG' not in out:
try:
out, err = utils.execute_with_timeout('/bin/ps', '-C',
'redis-server', 'h')
pid = out.split()[0]
msg = _('Redis pid: %s') % (pid)
LOG.info(msg)
LOG.info(_('Service Status is BLOCKED.'))
return rd_instance.ServiceStatuses.BLOCKED
except exception.ProcessExecutionError:
pid_file = options.get('pidfile',
'/var/run/redis/redis-server.pid')
if os.path.exists(pid_file):
LOG.info(_('Service Status is CRASHED.'))
return rd_instance.ServiceStatuses.CRASHED
else:
LOG.info(_('Service Status is SHUTDOWN.'))
return rd_instance.ServiceStatuses.SHUTDOWN
class RedisApp(object):
"""
Handles installation and configuration of redis
on a trove instance.
"""
def __init__(self, status, state_change_wait_time=None):
"""
Sets default status and state_change_wait_time
"""
if state_change_wait_time:
self.state_change_wait_time = state_change_wait_time
else:
self.state_change_wait_time = CONF.state_change_wait_time
self.status = status
def install_if_needed(self, packages):
"""
Install redis if needed do nothing if it is already installed.
"""
LOG.info(_('Preparing Guest as Redis Server'))
if not packager.pkg_is_installed(packages):
LOG.info(_('Installing Redis'))
self._install_redis(packages)
LOG.info(_('Dbaas install_if_needed complete'))
def complete_install_or_restart(self):
"""
finalize status updates for install or restart.
"""
self.status.end_install_or_restart()
def _install_redis(self, packages):
"""
Install the redis server.
"""
LOG.debug(_('Installing redis server'))
msg = _("Creating %s") % (system.REDIS_CONF_DIR)
LOG.debug(msg)
utils.execute_with_timeout('mkdir',
'-p',
system.REDIS_CONF_DIR,
run_as_root=True,
root_helper='sudo')
pkg_opts = {}
packager.pkg_install(packages, pkg_opts, TIME_OUT)
self.start_redis()
LOG.debug(_('Finished installing redis server'))
def _enable_redis_on_boot(self):
"""
Enables redis on boot.
"""
LOG.info(_('Enabling redis on boot.'))
if os.path.isfile(system.REDIS_INIT):
LOG.info(_("OS Using Upstart"))
cmd = "sudo sed -i '/^manual$/d' %s" % (system.REDIS_INIT)
utils.execute_with_timeout(cmd,
shell=True)
else:
cmd = 'sudo %s' % (system.REDIS_CMD_ENABLE)
utils.execute_with_timeout(cmd,
shell=True)
def _disable_redis_on_boot(self):
"""
Disables redis on boot.
"""
LOG.info(_('Disabling redis on boot.'))
if os.path.isfile(system.REDIS_INIT):
LOG.info(_("OS Using Upstart"))
utils.execute_with_timeout('echo',
"'manual'",
'>>',
system.REDIS_INIT,
run_as_root=True,
root_helper='sudo')
else:
cmd = 'sudo %s' % (system.REDIS_CMD_DISABLE)
utils.execute_with_timeout(cmd,
shell=True)
def stop_db(self, update_db=False, do_not_start_on_reboot=False):
"""
Stops the redis application on the trove instance.
"""
LOG.info(_('Stopping redis...'))
if do_not_start_on_reboot:
self._disable_redis_on_boot()
cmd = 'sudo %s' % (system.REDIS_CMD_STOP)
utils.execute_with_timeout(cmd,
shell=True)
if not self.status.wait_for_real_status_to_change_to(
rd_instance.ServiceStatuses.SHUTDOWN,
self.state_change_wait_time, update_db):
LOG.error(_('Could not stop Redis!'))
self.status.end_install_or_restart()
def restart(self):
"""
Restarts the redis daemon.
"""
try:
self.status.begin_restart()
self.stop_db()
self.start_redis()
finally:
self.status.end_install_or_restart()
def write_config(self, config_contents):
"""
Write the redis config.
"""
with open(TMP_REDIS_CONF, 'w') as fd:
fd.write(config_contents)
utils.execute_with_timeout('mv',
TMP_REDIS_CONF,
system.REDIS_CONFIG,
run_as_root=True,
root_helper='sudo')
def start_redis(self, update_db=False):
"""
Start the redis daemon.
"""
LOG.info(_("Starting redis..."))
self._enable_redis_on_boot()
try:
cmd = 'sudo %s' % (system.REDIS_CMD_START)
utils.execute_with_timeout(cmd,
shell=True)
except exception.ProcessExecutionError:
pass
if not self.status.wait_for_real_status_to_change_to(
rd_instance.ServiceStatuses.RUNNING,
self.state_change_wait_time, update_db):
LOG.error(_("Start up of redis failed!"))
try:
utils.execute_with_timeout('pkill', '-9',
'redis-server',
run_as_root=True,
root_helper='sudo')
except exception.ProcessExecutionError as p:
LOG.error('Error killing stalled redis start command.')
LOG.error(p)
self.status.end_install_or_restart()

View File

@ -0,0 +1,39 @@
# Copyright (c) 2013 Rackspace
# All Rights Reserved.
#
# 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.
"""
Determines operating system version and os depended commands.
"""
from trove.guestagent.common.operating_system import get_os
OS = get_os()
REDIS_CONFIG = '/etc/redis/redis.conf'
REDIS_CONF_DIR = '/etc/redis'
REDIS_INIT = '/etc/init/redis-server.conf'
REDIS_CLI = '/usr/bin/redis-cli'
REDIS_BIN = '/usr/bin/redis-server'
REDIS_CMD_ENABLE = 'update-rc.d redis-server enable'
REDIS_CMD_DISABLE = 'update-rc.d redis-server disable'
REDIS_CMD_START = 'service redis-server start || /bin/true'
REDIS_CMD_STOP = 'service redis-server stop || /bin/true'
REDIS_PACKAGE = 'redis-server'
REDIS_BASE_DIR = '/var/lib/redis'
if OS is 'redhat':
REDIS_BIN = '/usr/libexec/redis-server'
REDIS_CMD_ENABLE = 'chkconfig redis-server on'
REDIS_CMD_DISABLE = 'chkconfig redis-server off'
REDIS_CMD_START = 'service redis-server start'
REDIS_CMD_STOP = 'service redis-server stop'

View File

@ -36,8 +36,8 @@ LOG = log.getLogger(__name__)
defaults = {
'mysql': 'trove.guestagent.datastore.mysql.manager.Manager',
'percona': 'trove.guestagent.datastore.mysql.manager.Manager',
'redis': 'trove.guestagent.datastore.redis.manager.Manager',
}
CONF = cfg.CONF

View File

@ -0,0 +1,41 @@
daemonize yes
pidfile /var/run/redis/redis-server.pid
port 6379
timeout 0
loglevel notice
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
slave-serve-stale-data yes
slave-read-only yes
slave-priority 100
rename-command CONFIG ""
maxclients 10000
maxmemory 1073741824
maxmemory-policy volatile-lru
maxmemory-samples 3
appendonly yes
appendfilename appendonly.aof
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

View File

@ -31,7 +31,6 @@ import testtools
from testtools.matchers import Is
from testtools.matchers import Equals
from testtools.matchers import Not
from trove.extensions.mysql.models import RootHistory
import trove
from trove.common.context import TroveContext
@ -44,6 +43,9 @@ from trove.guestagent import pkg
from trove.guestagent.dbaas import to_gb
from trove.guestagent.dbaas import get_filesystem_volume_stats
from trove.guestagent.datastore.service import BaseDbStatus
from trove.guestagent.datastore.redis import service as rservice
from trove.guestagent.datastore.redis.service import RedisApp
from trove.guestagent.datastore.redis import system as RedisSystem
from trove.guestagent.datastore.mysql.service import MySqlAdmin
from trove.guestagent.datastore.mysql.service import MySqlRootAccess
from trove.guestagent.datastore.mysql.service import MySqlApp
@ -931,7 +933,7 @@ class ServiceRegistryTest(testtools.TestCase):
dbaas_sr.get_custom_managers = Mock(return_value=
datastore_registry_ext_test)
test_dict = dbaas_sr.datastore_registry()
self.assertEqual(3, len(test_dict))
self.assertEqual(4, len(test_dict))
self.assertEqual(test_dict.get('test'),
datastore_registry_ext_test.get('test', None))
self.assertEqual(test_dict.get('mysql'),
@ -940,6 +942,9 @@ class ServiceRegistryTest(testtools.TestCase):
self.assertEqual(test_dict.get('percona'),
'trove.guestagent.datastore.mysql.'
'manager.Manager')
self.assertEqual(test_dict.get('redis'),
'trove.guestagent.datastore.redis.'
'manager.Manager')
def test_datastore_registry_with_existing_manager(self):
datastore_registry_ext_test = {
@ -949,26 +954,30 @@ class ServiceRegistryTest(testtools.TestCase):
dbaas_sr.get_custom_managers = Mock(return_value=
datastore_registry_ext_test)
test_dict = dbaas_sr.datastore_registry()
self.assertEqual(2, len(test_dict))
self.assertEqual(3, len(test_dict))
self.assertEqual(test_dict.get('mysql'),
'trove.guestagent.datastore.mysql.'
'manager.Manager123')
self.assertEqual(test_dict.get('percona'),
'trove.guestagent.datastore.mysql.'
'manager.Manager')
self.assertEqual(test_dict.get('redis'),
'trove.guestagent.datastore.redis.manager.Manager')
def test_datastore_registry_with_blank_dict(self):
datastore_registry_ext_test = dict()
dbaas_sr.get_custom_managers = Mock(return_value=
datastore_registry_ext_test)
test_dict = dbaas_sr.datastore_registry()
self.assertEqual(2, len(test_dict))
self.assertEqual(3, len(test_dict))
self.assertEqual(test_dict.get('mysql'),
'trove.guestagent.datastore.mysql.'
'manager.Manager')
self.assertEqual(test_dict.get('percona'),
'trove.guestagent.datastore.mysql.'
'manager.Manager')
self.assertEqual(test_dict.get('redis'),
'trove.guestagent.datastore.redis.manager.Manager')
class KeepAliveConnectionTest(testtools.TestCase):
@ -1194,3 +1203,186 @@ class MySqlAppStatusTest(testtools.TestCase):
status = self.mySqlAppStatus._get_actual_db_status()
self.assertEqual(rd_instance.ServiceStatuses.BLOCKED, status)
class TestRedisApp(testtools.TestCase):
def setUp(self):
super(TestRedisApp, self).setUp()
self.FAKE_ID = 1000
self.appStatus = FakeAppStatus(self.FAKE_ID,
rd_instance.ServiceStatuses.NEW)
self.app = RedisApp(self.appStatus)
self.orig_os_path_isfile = os.path.isfile
self.orig_utils_execute_with_timeout = utils.execute_with_timeout
utils.execute_with_timeout = Mock()
rservice.utils.execute_with_timeout = Mock()
def tearDown(self):
super(TestRedisApp, self).tearDown()
self.app = None
os.path.isfile = self.orig_os_path_isfile
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
rservice.utils.execute_with_timeout = \
self.orig_utils_execute_with_timeout
unstub()
def test_install_if_needed_installed(self):
when(pkg.Package).pkg_is_installed(any()).thenReturn(True)
when(RedisApp)._install_redis('bar').thenReturn(None)
self.app.install_if_needed('bar')
verify(pkg.Package).pkg_is_installed('bar')
verify(RedisApp, times=0)._install_redis('bar')
def test_install_if_needed_not_installed(self):
when(pkg.Package).pkg_is_installed(any()).thenReturn(False)
when(RedisApp)._install_redis('asdf').thenReturn(None)
self.app.install_if_needed('asdf')
verify(pkg.Package).pkg_is_installed('asdf')
verify(RedisApp)._install_redis('asdf')
def test_install_redis(self):
when(utils).execute_with_timeout(any())
when(pkg.Package).pkg_install('redis', {}, 1200).thenReturn(None)
when(RedisApp).start_redis().thenReturn(None)
self.app._install_redis('redis')
verify(utils).execute_with_timeout(any())
verify(pkg.Package).pkg_install('redis', {}, 1200)
verify(RedisApp).start_redis()
def test_enable_redis_on_boot_without_upstart(self):
when(os.path).isfile(RedisSystem.REDIS_INIT).thenReturn(False)
when(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_ENABLE,
shell=True).thenReturn(None)
self.app._enable_redis_on_boot()
verify(os.path).isfile(RedisSystem.REDIS_INIT)
verify(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_ENABLE,
shell=True)
def test_enable_redis_on_boot_with_upstart(self):
when(os.path).isfile(RedisSystem.REDIS_INIT).thenReturn(True)
when(utils).execute_with_timeout("sudo sed -i '/^manual$/d' " +
RedisSystem.REDIS_INIT,
shell=True).thenReturn(None)
self.app._enable_redis_on_boot()
verify(os.path).isfile(RedisSystem.REDIS_INIT)
verify(utils).execute_with_timeout("sudo sed -i '/^manual$/d' " +
RedisSystem.REDIS_INIT,
shell=True)
def test_disable_redis_on_boot_with_upstart(self):
when(os.path).isfile(RedisSystem.REDIS_INIT).thenReturn(True)
when(utils).execute_with_timeout('echo',
"'manual'",
'>>',
RedisSystem.REDIS_INIT,
run_as_root=True,
root_helper='sudo').thenReturn(None)
self.app._disable_redis_on_boot()
verify(os.path).isfile(RedisSystem.REDIS_INIT)
verify(utils).execute_with_timeout('echo',
"'manual'",
'>>',
RedisSystem.REDIS_INIT,
run_as_root=True,
root_helper='sudo')
def test_disable_redis_on_boot_without_upstart(self):
when(os.path).isfile(RedisSystem.REDIS_INIT).thenReturn(False)
when(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_DISABLE,
shell=True).thenReturn(None)
self.app._disable_redis_on_boot()
verify(os.path).isfile(RedisSystem.REDIS_INIT)
verify(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_DISABLE,
shell=True)
def test_stop_db_without_fail(self):
mock_status = mock()
when(mock_status).wait_for_real_status_to_change_to(
any(), any(), any()).thenReturn(True)
app = RedisApp(mock_status, state_change_wait_time=0)
when(RedisApp)._disable_redis_on_boot().thenReturn(None)
when(utils).execute_with_timeout('sudo ' + RedisSystem.REDIS_CMD_STOP,
shell=True).thenReturn(None)
when(mock_status).wait_for_real_status_to_change_to(
any(),
any(),
any()).thenReturn(True)
app.stop_db(do_not_start_on_reboot=True)
verify(RedisApp)._disable_redis_on_boot()
verify(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_STOP,
shell=True)
verify(mock_status).wait_for_real_status_to_change_to(
any(),
any(),
any())
def test_stop_db_with_failure(self):
mock_status = mock()
when(mock_status).wait_for_real_status_to_change_to(
any(), any(), any()).thenReturn(True)
app = RedisApp(mock_status, state_change_wait_time=0)
when(RedisApp)._disable_redis_on_boot().thenReturn(None)
when(utils).execute_with_timeout('sudo ' + RedisSystem.REDIS_CMD_STOP,
shell=True).thenReturn(None)
when(mock_status).wait_for_real_status_to_change_to(
any(),
any(),
any()).thenReturn(False)
app.stop_db(do_not_start_on_reboot=True)
verify(RedisApp)._disable_redis_on_boot()
verify(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_STOP,
shell=True)
verify(mock_status).wait_for_real_status_to_change_to(
any(),
any(),
any())
verify(mock_status).end_install_or_restart()
def test_restart(self):
mock_status = mock()
app = RedisApp(mock_status, state_change_wait_time=0)
when(mock_status).begin_restart().thenReturn(None)
when(RedisApp).stop_db().thenReturn(None)
when(RedisApp).start_redis().thenReturn(None)
when(mock_status).end_install_or_restart().thenReturn(None)
app.restart()
verify(mock_status).begin_restart()
verify(RedisApp).stop_db()
verify(RedisApp).start_redis()
verify(mock_status).end_install_or_restart()
def test_start_redis(self):
mock_status = mock()
app = RedisApp(mock_status, state_change_wait_time=0)
when(RedisApp)._enable_redis_on_boot().thenReturn(None)
when(utils).execute_with_timeout('sudo ' + RedisSystem.REDIS_CMD_START,
shell=True).thenReturn(None)
when(mock_status).wait_for_real_status_to_change_to(any(),
any(),
any()).thenReturn(
None)
when(utils).execute_with_timeout('pkill', '-9',
'redis-server',
run_as_root=True,
root_helper='sudo').thenReturn(None)
when(mock_status).end_install_or_restart().thenReturn(None)
app.start_redis()
verify(RedisApp)._enable_redis_on_boot()
verify(utils).execute_with_timeout('sudo ' +
RedisSystem.REDIS_CMD_START,
shell=True)
verify(mock_status).wait_for_real_status_to_change_to(any(),
any(),
any())
verify(utils).execute_with_timeout('pkill', '-9',
'redis-server',
run_as_root=True,
root_helper='sudo')
verify(mock_status).end_install_or_restart()

View File

@ -18,10 +18,13 @@ import testtools
from mockito import verify, when, unstub, any, mock, never
from testtools.matchers import Is, Equals, Not
from trove.guestagent import volume
from trove.common.context import TroveContext
from trove.guestagent import volume
from trove.guestagent.datastore.mysql.manager import Manager
import trove.guestagent.datastore.mysql.service as dbaas
from trove.guestagent.datastore.redis.manager import Manager as RedisManager
import trove.guestagent.datastore.redis.service as redis_service
import trove.guestagent.datastore.redis.system as redis_system
from trove.guestagent import backup
from trove.guestagent.volume import VolumeDevice
from trove.guestagent import pkg
@ -208,3 +211,94 @@ class GuestAgentManagerTest(testtools.TestCase):
verify(dbaas.MySqlApp).secure_root(secure_remote_root=any())
verify(dbaas.MySqlAdmin, times=times_report).report_root_enabled(
self.context)
class RedisGuestAgentManagerTest(testtools.TestCase):
def setUp(self):
super(RedisGuestAgentManagerTest, self).setUp()
self.context = TroveContext()
self.manager = RedisManager()
self.packages = 'redis-server'
self.origin_RedisAppStatus = redis_service.RedisAppStatus
self.origin_os_path_exists = os.path.exists
self.origin_format = volume.VolumeDevice.format
self.origin_migrate_data = volume.VolumeDevice.migrate_data
self.origin_mount = volume.VolumeDevice.mount
self.origin_stop_redis = redis_service.RedisApp.stop_db
self.origin_start_redis = redis_service.RedisApp.start_redis
self.origin_install_redis = redis_service.RedisApp._install_redis
def tearDown(self):
super(RedisGuestAgentManagerTest, self).tearDown()
redis_service.RedisAppStatus = self.origin_RedisAppStatus
os.path.exists = self.origin_os_path_exists
volume.VolumeDevice.format = self.origin_format
volume.VolumeDevice.migrate_data = self.origin_migrate_data
volume.VolumeDevice.mount = self.origin_mount
redis_service.RedisApp.stop_db = self.origin_stop_redis
redis_service.RedisApp.start_redis = self.origin_start_redis
redis_service.RedisApp._install_redis = self.origin_install_redis
unstub()
def test_update_status(self):
mock_status = mock()
when(redis_service.RedisAppStatus).get().thenReturn(mock_status)
self.manager.update_status(self.context)
verify(redis_service.RedisAppStatus).get()
verify(mock_status).update()
def test_prepare_device_path_true(self):
self._prepare_dynamic()
def test_prepare_device_path_false(self):
self._prepare_dynamic(device_path=None)
def test_prepare_redis_not_installed(self):
self._prepare_dynamic(is_redis_installed=False)
def _prepare_dynamic(self, device_path='/dev/vdb', is_redis_installed=True,
backup_info=None, is_root_enabled=False):
# covering all outcomes is starting to cause trouble here
dev_path = 1 if device_path else 0
mock_status = mock()
when(redis_service.RedisAppStatus).get().thenReturn(mock_status)
when(mock_status).begin_install().thenReturn(None)
when(VolumeDevice).format().thenReturn(None)
when(VolumeDevice).mount().thenReturn(None)
when(redis_service.RedisApp).start_redis().thenReturn(None)
when(redis_service.RedisApp).install_if_needed().thenReturn(None)
when(backup).restore(self.context, backup_info).thenReturn(None)
when(redis_service.RedisApp).write_config(any()).thenReturn(None)
when(redis_service.RedisApp).complete_install_or_restart(
any()).thenReturn(None)
self.manager.prepare(self.context, self.packages,
None, '2048',
None, device_path=device_path,
mount_point='/var/lib/redis',
backup_info=backup_info)
verify(redis_service.RedisAppStatus, times=2).get()
verify(mock_status).begin_install()
verify(VolumeDevice, times=dev_path).format()
verify(VolumeDevice, times=dev_path).mount(redis_system.REDIS_BASE_DIR)
verify(redis_service.RedisApp).install_if_needed(self.packages)
verify(redis_service.RedisApp).write_config(None)
verify(redis_service.RedisApp).complete_install_or_restart()
def test_restart(self):
mock_status = mock()
when(redis_service.RedisAppStatus).get().thenReturn(mock_status)
when(redis_service.RedisApp).restart().thenReturn(None)
self.manager.restart(self.context)
verify(redis_service.RedisAppStatus).get()
verify(redis_service.RedisApp).restart()
def test_stop_db(self):
mock_status = mock()
when(redis_service.RedisAppStatus).get().thenReturn(mock_status)
when(redis_service.RedisApp).stop_db(do_not_start_on_reboot=
False).thenReturn(None)
self.manager.stop_db(self.context)
verify(redis_service.RedisAppStatus).get()
verify(redis_service.RedisApp).stop_db(do_not_start_on_reboot=False)