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 = {
|
defaults = {
|
||||||
'mysql': 'trove.guestagent.datastore.mysql.manager.Manager',
|
'mysql': 'trove.guestagent.datastore.mysql.manager.Manager',
|
||||||
'percona': 'trove.guestagent.datastore.mysql.manager.Manager',
|
'percona': 'trove.guestagent.datastore.mysql.manager.Manager',
|
||||||
|
'redis': 'trove.guestagent.datastore.redis.manager.Manager',
|
||||||
}
|
}
|
||||||
|
|
||||||
CONF = cfg.CONF
|
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 Is
|
||||||
from testtools.matchers import Equals
|
from testtools.matchers import Equals
|
||||||
from testtools.matchers import Not
|
from testtools.matchers import Not
|
||||||
|
|
||||||
from trove.extensions.mysql.models import RootHistory
|
from trove.extensions.mysql.models import RootHistory
|
||||||
import trove
|
import trove
|
||||||
from trove.common.context import TroveContext
|
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 to_gb
|
||||||
from trove.guestagent.dbaas import get_filesystem_volume_stats
|
from trove.guestagent.dbaas import get_filesystem_volume_stats
|
||||||
from trove.guestagent.datastore.service import BaseDbStatus
|
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 MySqlAdmin
|
||||||
from trove.guestagent.datastore.mysql.service import MySqlRootAccess
|
from trove.guestagent.datastore.mysql.service import MySqlRootAccess
|
||||||
from trove.guestagent.datastore.mysql.service import MySqlApp
|
from trove.guestagent.datastore.mysql.service import MySqlApp
|
||||||
@ -931,7 +933,7 @@ class ServiceRegistryTest(testtools.TestCase):
|
|||||||
dbaas_sr.get_custom_managers = Mock(return_value=
|
dbaas_sr.get_custom_managers = Mock(return_value=
|
||||||
datastore_registry_ext_test)
|
datastore_registry_ext_test)
|
||||||
test_dict = dbaas_sr.datastore_registry()
|
test_dict = dbaas_sr.datastore_registry()
|
||||||
self.assertEqual(3, len(test_dict))
|
self.assertEqual(4, len(test_dict))
|
||||||
self.assertEqual(test_dict.get('test'),
|
self.assertEqual(test_dict.get('test'),
|
||||||
datastore_registry_ext_test.get('test', None))
|
datastore_registry_ext_test.get('test', None))
|
||||||
self.assertEqual(test_dict.get('mysql'),
|
self.assertEqual(test_dict.get('mysql'),
|
||||||
@ -940,6 +942,9 @@ class ServiceRegistryTest(testtools.TestCase):
|
|||||||
self.assertEqual(test_dict.get('percona'),
|
self.assertEqual(test_dict.get('percona'),
|
||||||
'trove.guestagent.datastore.mysql.'
|
'trove.guestagent.datastore.mysql.'
|
||||||
'manager.Manager')
|
'manager.Manager')
|
||||||
|
self.assertEqual(test_dict.get('redis'),
|
||||||
|
'trove.guestagent.datastore.redis.'
|
||||||
|
'manager.Manager')
|
||||||
|
|
||||||
def test_datastore_registry_with_existing_manager(self):
|
def test_datastore_registry_with_existing_manager(self):
|
||||||
datastore_registry_ext_test = {
|
datastore_registry_ext_test = {
|
||||||
@ -949,26 +954,30 @@ class ServiceRegistryTest(testtools.TestCase):
|
|||||||
dbaas_sr.get_custom_managers = Mock(return_value=
|
dbaas_sr.get_custom_managers = Mock(return_value=
|
||||||
datastore_registry_ext_test)
|
datastore_registry_ext_test)
|
||||||
test_dict = dbaas_sr.datastore_registry()
|
test_dict = dbaas_sr.datastore_registry()
|
||||||
self.assertEqual(2, len(test_dict))
|
self.assertEqual(3, len(test_dict))
|
||||||
self.assertEqual(test_dict.get('mysql'),
|
self.assertEqual(test_dict.get('mysql'),
|
||||||
'trove.guestagent.datastore.mysql.'
|
'trove.guestagent.datastore.mysql.'
|
||||||
'manager.Manager123')
|
'manager.Manager123')
|
||||||
self.assertEqual(test_dict.get('percona'),
|
self.assertEqual(test_dict.get('percona'),
|
||||||
'trove.guestagent.datastore.mysql.'
|
'trove.guestagent.datastore.mysql.'
|
||||||
'manager.Manager')
|
'manager.Manager')
|
||||||
|
self.assertEqual(test_dict.get('redis'),
|
||||||
|
'trove.guestagent.datastore.redis.manager.Manager')
|
||||||
|
|
||||||
def test_datastore_registry_with_blank_dict(self):
|
def test_datastore_registry_with_blank_dict(self):
|
||||||
datastore_registry_ext_test = dict()
|
datastore_registry_ext_test = dict()
|
||||||
dbaas_sr.get_custom_managers = Mock(return_value=
|
dbaas_sr.get_custom_managers = Mock(return_value=
|
||||||
datastore_registry_ext_test)
|
datastore_registry_ext_test)
|
||||||
test_dict = dbaas_sr.datastore_registry()
|
test_dict = dbaas_sr.datastore_registry()
|
||||||
self.assertEqual(2, len(test_dict))
|
self.assertEqual(3, len(test_dict))
|
||||||
self.assertEqual(test_dict.get('mysql'),
|
self.assertEqual(test_dict.get('mysql'),
|
||||||
'trove.guestagent.datastore.mysql.'
|
'trove.guestagent.datastore.mysql.'
|
||||||
'manager.Manager')
|
'manager.Manager')
|
||||||
self.assertEqual(test_dict.get('percona'),
|
self.assertEqual(test_dict.get('percona'),
|
||||||
'trove.guestagent.datastore.mysql.'
|
'trove.guestagent.datastore.mysql.'
|
||||||
'manager.Manager')
|
'manager.Manager')
|
||||||
|
self.assertEqual(test_dict.get('redis'),
|
||||||
|
'trove.guestagent.datastore.redis.manager.Manager')
|
||||||
|
|
||||||
|
|
||||||
class KeepAliveConnectionTest(testtools.TestCase):
|
class KeepAliveConnectionTest(testtools.TestCase):
|
||||||
@ -1194,3 +1203,186 @@ class MySqlAppStatusTest(testtools.TestCase):
|
|||||||
status = self.mySqlAppStatus._get_actual_db_status()
|
status = self.mySqlAppStatus._get_actual_db_status()
|
||||||
|
|
||||||
self.assertEqual(rd_instance.ServiceStatuses.BLOCKED, 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 mockito import verify, when, unstub, any, mock, never
|
||||||
from testtools.matchers import Is, Equals, Not
|
from testtools.matchers import Is, Equals, Not
|
||||||
|
|
||||||
from trove.guestagent import volume
|
|
||||||
from trove.common.context import TroveContext
|
from trove.common.context import TroveContext
|
||||||
|
from trove.guestagent import volume
|
||||||
from trove.guestagent.datastore.mysql.manager import Manager
|
from trove.guestagent.datastore.mysql.manager import Manager
|
||||||
import trove.guestagent.datastore.mysql.service as dbaas
|
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 import backup
|
||||||
from trove.guestagent.volume import VolumeDevice
|
from trove.guestagent.volume import VolumeDevice
|
||||||
from trove.guestagent import pkg
|
from trove.guestagent import pkg
|
||||||
@ -223,3 +226,94 @@ class GuestAgentManagerTest(testtools.TestCase):
|
|||||||
verify(dbaas.MySqlApp).secure_root(secure_remote_root=any())
|
verify(dbaas.MySqlApp).secure_root(secure_remote_root=any())
|
||||||
verify(dbaas.MySqlAdmin, times=times_report).report_root_enabled(
|
verify(dbaas.MySqlAdmin, times=times_report).report_root_enabled(
|
||||||
self.context)
|
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