Merge "Added Redis Crud Operations"
This commit is contained in:
commit
570c283754
0
trove/guestagent/datastore/redis/__init__.py
Normal file
0
trove/guestagent/datastore/redis/__init__.py
Normal file
124
trove/guestagent/datastore/redis/manager.py
Normal file
124
trove/guestagent/datastore/redis/manager.py
Normal 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()
|
269
trove/guestagent/datastore/redis/service.py
Normal file
269
trove/guestagent/datastore/redis/service.py
Normal 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()
|
39
trove/guestagent/datastore/redis/system.py
Normal file
39
trove/guestagent/datastore/redis/system.py
Normal 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'
|
@ -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
|
||||
|
||||
|
||||
|
41
trove/templates/redis/config.template
Normal file
41
trove/templates/redis/config.template
Normal 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
|
@ -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()
|
||||
|
@ -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
|
||||
@ -223,3 +226,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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user