3330 lines
141 KiB
Python
3330 lines
141 KiB
Python
# Copyright 2012 OpenStack Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import ConfigParser
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
from uuid import uuid4
|
|
|
|
from mock import ANY
|
|
from mock import DEFAULT
|
|
from mock import MagicMock
|
|
from mock import Mock
|
|
from mock import patch
|
|
from mock import PropertyMock
|
|
from oslo_utils import netutils
|
|
import sqlalchemy
|
|
import testtools
|
|
from testtools.matchers import Equals
|
|
from testtools.matchers import Is
|
|
from testtools.matchers import Not
|
|
|
|
from trove.common import cfg
|
|
from trove.common.exception import BadRequest
|
|
from trove.common.exception import GuestError
|
|
from trove.common.exception import PollTimeOut
|
|
from trove.common.exception import ProcessExecutionError
|
|
from trove.common import instance as rd_instance
|
|
from trove.common import utils
|
|
from trove.conductor import api as conductor_api
|
|
from trove.guestagent.common import operating_system
|
|
from trove.guestagent.common.operating_system import FileMode
|
|
from trove.guestagent.datastore.experimental.cassandra import (
|
|
service as cass_service)
|
|
from trove.guestagent.datastore.experimental.cassandra import (
|
|
system as cass_system)
|
|
from trove.guestagent.datastore.experimental.couchbase import (
|
|
service as couchservice)
|
|
from trove.guestagent.datastore.experimental.couchdb import (
|
|
service as couchdb_service)
|
|
from trove.guestagent.datastore.experimental.db2 import (
|
|
service as db2service)
|
|
from trove.guestagent.datastore.experimental.mongodb import (
|
|
service as mongo_service)
|
|
from trove.guestagent.datastore.experimental.mongodb import (
|
|
system as mongo_system)
|
|
from trove.guestagent.datastore.experimental.redis import service as rservice
|
|
from trove.guestagent.datastore.experimental.redis.service import RedisApp
|
|
from trove.guestagent.datastore.experimental.redis import system as RedisSystem
|
|
from trove.guestagent.datastore.experimental.vertica import (
|
|
system as vertica_system)
|
|
from trove.guestagent.datastore.experimental.vertica.service import (
|
|
VerticaAppStatus)
|
|
from trove.guestagent.datastore.experimental.vertica.service import VerticaApp
|
|
import trove.guestagent.datastore.mysql.service as dbaas
|
|
from trove.guestagent.datastore.mysql.service import KeepAliveConnection
|
|
from trove.guestagent.datastore.mysql.service import MySqlAdmin
|
|
from trove.guestagent.datastore.mysql.service import MySqlApp
|
|
from trove.guestagent.datastore.mysql.service import MySqlAppStatus
|
|
from trove.guestagent.datastore.mysql.service import MySqlRootAccess
|
|
from trove.guestagent.datastore.service import BaseDbStatus
|
|
from trove.guestagent.db import models
|
|
from trove.guestagent import dbaas as dbaas_sr
|
|
from trove.guestagent.dbaas import get_filesystem_volume_stats
|
|
from trove.guestagent.dbaas import to_gb
|
|
from trove.guestagent import pkg
|
|
from trove.guestagent.volume import VolumeDevice
|
|
from trove.instance.models import InstanceServiceStatus
|
|
from trove.tests.unittests.util import util
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
"""
|
|
Unit tests for the classes and functions in dbaas.py.
|
|
"""
|
|
|
|
FAKE_DB = {"_name": "testDB", "_character_set": "latin2",
|
|
"_collate": "latin2_general_ci"}
|
|
FAKE_DB_2 = {"_name": "testDB2", "_character_set": "latin2",
|
|
"_collate": "latin2_general_ci"}
|
|
FAKE_USER = [{"_name": "random", "_password": "guesswhat",
|
|
"_host": "%", "_databases": [FAKE_DB]}]
|
|
|
|
conductor_api.API.get_client = Mock()
|
|
conductor_api.API.heartbeat = Mock()
|
|
|
|
|
|
class FakeAppStatus(BaseDbStatus):
|
|
|
|
def __init__(self, id, status):
|
|
self.id = id
|
|
self.next_fake_status = status
|
|
|
|
def _get_actual_db_status(self):
|
|
return self.next_fake_status
|
|
|
|
def set_next_status(self, next_status):
|
|
self.next_fake_status = next_status
|
|
|
|
def _is_query_router(self):
|
|
return False
|
|
|
|
|
|
class DbaasTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(DbaasTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout
|
|
self.orig_utils_execute = dbaas.utils.execute
|
|
|
|
def tearDown(self):
|
|
super(DbaasTest, self).tearDown()
|
|
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
dbaas.utils.execute = self.orig_utils_execute
|
|
|
|
@patch.object(operating_system, 'remove')
|
|
def test_clear_expired_password(self, mock_remove):
|
|
secret_content = ("# The random password set for the "
|
|
"root user at Wed May 14 14:06:38 2014 "
|
|
"(local time): somepassword")
|
|
with patch.object(dbaas.utils, 'execute',
|
|
return_value=(secret_content, None)):
|
|
dbaas.clear_expired_password()
|
|
self.assertEqual(2, dbaas.utils.execute.call_count)
|
|
self.assertEqual(1, mock_remove.call_count)
|
|
|
|
@patch.object(operating_system, 'remove')
|
|
def test_no_secret_content_clear_expired_password(self, mock_remove):
|
|
with patch.object(dbaas.utils, 'execute', return_value=('', None)):
|
|
dbaas.clear_expired_password()
|
|
self.assertEqual(1, dbaas.utils.execute.call_count)
|
|
mock_remove.assert_not_called()
|
|
|
|
@patch.object(operating_system, 'remove')
|
|
def test_fail_password_update_content_clear_expired_password(self,
|
|
mock_remove):
|
|
secret_content = ("# The random password set for the "
|
|
"root user at Wed May 14 14:06:38 2014 "
|
|
"(local time): somepassword")
|
|
with patch.object(dbaas.utils, 'execute',
|
|
side_effect=[(secret_content, None),
|
|
ProcessExecutionError]):
|
|
dbaas.clear_expired_password()
|
|
self.assertEqual(2, dbaas.utils.execute.call_count)
|
|
mock_remove.assert_not_called()
|
|
|
|
@patch.object(operating_system, 'remove')
|
|
@patch.object(dbaas.utils, 'execute', side_effect=ProcessExecutionError)
|
|
def test_fail_retrieve_secret_content_clear_expired_password(self,
|
|
mock_execute,
|
|
mock_remove):
|
|
dbaas.clear_expired_password()
|
|
self.assertEqual(1, mock_execute.call_count)
|
|
mock_remove.assert_not_called()
|
|
|
|
def test_get_auth_password(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock(
|
|
return_value=("password ", None))
|
|
|
|
password = dbaas.get_auth_password()
|
|
|
|
self.assertEqual("password", password)
|
|
|
|
def test_get_auth_password_error(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock(
|
|
return_value=("password", "Error"))
|
|
|
|
self.assertRaises(RuntimeError, dbaas.get_auth_password)
|
|
|
|
def test_service_discovery(self):
|
|
with patch.object(os.path, 'isfile', return_value=True):
|
|
mysql_service = dbaas.operating_system.service_discovery(["mysql"])
|
|
self.assertIsNotNone(mysql_service['cmd_start'])
|
|
self.assertIsNotNone(mysql_service['cmd_enable'])
|
|
|
|
def test_load_mysqld_options(self):
|
|
|
|
output = "mysqld would've been started with the these args:\n"\
|
|
"--user=mysql --port=3306 --basedir=/usr "\
|
|
"--tmpdir=/tmp --skip-external-locking"
|
|
|
|
with patch.object(os.path, 'isfile', return_value=True):
|
|
dbaas.utils.execute = Mock(return_value=(output, None))
|
|
options = dbaas.load_mysqld_options()
|
|
|
|
self.assertEqual(5, len(options))
|
|
self.assertEqual(["mysql"], options["user"])
|
|
self.assertEqual(["3306"], options["port"])
|
|
self.assertEqual(["/usr"], options["basedir"])
|
|
self.assertEqual(["/tmp"], options["tmpdir"])
|
|
self.assertTrue("skip-external-locking" in options)
|
|
|
|
def test_load_mysqld_options_contains_plugin_loads_options(self):
|
|
output = ("mysqld would've been started with the these args:\n"
|
|
"--plugin-load=blackhole=ha_blackhole.so "
|
|
"--plugin-load=federated=ha_federated.so")
|
|
|
|
with patch.object(os.path, 'isfile', return_value=True):
|
|
dbaas.utils.execute = Mock(return_value=(output, None))
|
|
options = dbaas.load_mysqld_options()
|
|
|
|
self.assertEqual(1, len(options))
|
|
self.assertEqual(["blackhole=ha_blackhole.so",
|
|
"federated=ha_federated.so"],
|
|
options["plugin-load"])
|
|
|
|
@patch.object(os.path, 'isfile', return_value=True)
|
|
def test_load_mysqld_options_error(self, mock_exists):
|
|
|
|
dbaas.utils.execute = Mock(side_effect=ProcessExecutionError())
|
|
|
|
self.assertFalse(dbaas.load_mysqld_options())
|
|
|
|
def test_get_datadir(self):
|
|
cnf_value = '[mysqld]\ndatadir=/var/lib/mysql/data'
|
|
with patch.object(dbaas, 'read_mycnf', Mock(return_value=cnf_value)):
|
|
self.assertEqual('/var/lib/mysql/data',
|
|
dbaas.get_datadir(reset_cache=True))
|
|
|
|
|
|
class ResultSetStub(object):
|
|
|
|
def __init__(self, rows):
|
|
self._rows = rows
|
|
|
|
def __iter__(self):
|
|
return self._rows.__iter__()
|
|
|
|
@property
|
|
def rowcount(self):
|
|
return len(self._rows)
|
|
|
|
def __repr__(self):
|
|
return self._rows.__repr__()
|
|
|
|
|
|
class MySqlAdminMockTest(testtools.TestCase):
|
|
|
|
def tearDown(self):
|
|
super(MySqlAdminMockTest, self).tearDown()
|
|
|
|
def test_list_databases(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
with patch.object(mock_conn, 'execute',
|
|
return_value=ResultSetStub(
|
|
[('db1', 'utf8', 'utf8_bin'),
|
|
('db2', 'utf8', 'utf8_bin'),
|
|
('db3', 'utf8', 'utf8_bin')])):
|
|
databases, next_marker = MySqlAdmin().list_databases(limit=10)
|
|
|
|
self.assertThat(next_marker, Is(None))
|
|
self.assertThat(len(databases), Is(3))
|
|
|
|
|
|
class MySqlAdminTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
super(MySqlAdminTest, self).setUp()
|
|
|
|
self.orig_get_engine = dbaas.get_engine
|
|
self.orig_LocalSqlClient = dbaas.LocalSqlClient
|
|
self.orig_LocalSqlClient_enter = dbaas.LocalSqlClient.__enter__
|
|
self.orig_LocalSqlClient_exit = dbaas.LocalSqlClient.__exit__
|
|
self.orig_LocalSqlClient_execute = dbaas.LocalSqlClient.execute
|
|
self.orig_MySQLUser_is_valid_user_name = (
|
|
models.MySQLUser._is_valid_user_name)
|
|
dbaas.get_engine = MagicMock(name='get_engine')
|
|
dbaas.LocalSqlClient = Mock
|
|
dbaas.LocalSqlClient.__enter__ = Mock()
|
|
dbaas.LocalSqlClient.__exit__ = Mock()
|
|
dbaas.LocalSqlClient.execute = Mock()
|
|
self.mySqlAdmin = MySqlAdmin()
|
|
|
|
def tearDown(self):
|
|
|
|
super(MySqlAdminTest, self).tearDown()
|
|
dbaas.get_engine = self.orig_get_engine
|
|
dbaas.LocalSqlClient = self.orig_LocalSqlClient
|
|
dbaas.LocalSqlClient.__enter__ = self.orig_LocalSqlClient_enter
|
|
dbaas.LocalSqlClient.__exit__ = self.orig_LocalSqlClient_exit
|
|
dbaas.LocalSqlClient.execute = self.orig_LocalSqlClient_execute
|
|
models.MySQLUser._is_valid_user_name = (
|
|
self.orig_MySQLUser_is_valid_user_name)
|
|
|
|
def test__associate_dbs(self):
|
|
db_result = [{"grantee": "'test_user'@'%'", "table_schema": "db1"},
|
|
{"grantee": "'test_user'@'%'", "table_schema": "db2"},
|
|
{"grantee": "'test_user'@'%'", "table_schema": "db3"},
|
|
{"grantee": "'test_user1'@'%'", "table_schema": "db1"},
|
|
{"grantee": "'test_user1'@'%'", "table_schema": "db3"}]
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user.databases = []
|
|
expected = ("SELECT grantee, table_schema FROM "
|
|
"information_schema.SCHEMA_PRIVILEGES WHERE privilege_type"
|
|
" != 'USAGE' GROUP BY grantee, table_schema;")
|
|
|
|
with patch.object(dbaas.LocalSqlClient, 'execute',
|
|
Mock(return_value=db_result)):
|
|
self.mySqlAdmin._associate_dbs(user)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
|
|
self.assertEqual(3, len(user.databases))
|
|
self.assertEqual(expected, args[0].text,
|
|
"Associate database queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_change_passwords(self):
|
|
user = [{"name": "test_user", "host": "%", "password": "password"}]
|
|
self.mySqlAdmin.change_passwords(user)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("UPDATE mysql.user SET Password="
|
|
"PASSWORD('password') WHERE User = 'test_user' "
|
|
"AND Host = '%';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Change password queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_update_attributes_password(self):
|
|
db_result = [{"grantee": "'test_user'@'%'", "table_schema": "db1"},
|
|
{"grantee": "'test_user'@'%'", "table_schema": "db2"}]
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user_attrs = {"password": "password"}
|
|
with patch.object(dbaas.LocalSqlClient, 'execute',
|
|
Mock(return_value=db_result)):
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
with patch.object(self.mySqlAdmin, 'grant_access'):
|
|
self.mySqlAdmin.update_attributes('test_user', '%',
|
|
user_attrs)
|
|
self.assertEqual(0,
|
|
self.mySqlAdmin.grant_access.call_count)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[1]
|
|
expected = ("UPDATE mysql.user SET Password="
|
|
"PASSWORD('password') WHERE User = 'test_user' "
|
|
"AND Host = '%';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Update attributes queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_update_attributes_name(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user_attrs = {"name": "new_name"}
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
with patch.object(self.mySqlAdmin, 'grant_access'):
|
|
self.mySqlAdmin.update_attributes('test_user', '%', user_attrs)
|
|
self.mySqlAdmin.grant_access.assert_called_with(
|
|
'new_name', '%', set([]))
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[1]
|
|
expected = ("UPDATE mysql.user SET User='new_name' "
|
|
"WHERE User = 'test_user' AND Host = '%';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Update attributes queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_update_attributes_host(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user_attrs = {"host": "new_host"}
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
with patch.object(self.mySqlAdmin, 'grant_access'):
|
|
self.mySqlAdmin.update_attributes('test_user', '%', user_attrs)
|
|
self.mySqlAdmin.grant_access.assert_called_with(
|
|
'test_user', 'new_host', set([]))
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[1]
|
|
expected = ("UPDATE mysql.user SET Host='new_host' "
|
|
"WHERE User = 'test_user' AND Host = '%';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Update attributes queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_create_database(self):
|
|
|
|
databases = []
|
|
databases.append(FAKE_DB)
|
|
|
|
self.mySqlAdmin.create_database(databases)
|
|
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("CREATE DATABASE IF NOT EXISTS "
|
|
"`testDB` CHARACTER SET = 'latin2' "
|
|
"COLLATE = 'latin2_general_ci';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Create database queries are not the same")
|
|
|
|
self.assertEqual(1, dbaas.LocalSqlClient.execute.call_count,
|
|
"The client object was not called exactly once, " +
|
|
"it was called %d times"
|
|
% dbaas.LocalSqlClient.execute.call_count)
|
|
|
|
def test_create_database_more_than_1(self):
|
|
|
|
databases = []
|
|
databases.append(FAKE_DB)
|
|
databases.append(FAKE_DB_2)
|
|
|
|
self.mySqlAdmin.create_database(databases)
|
|
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("CREATE DATABASE IF NOT EXISTS "
|
|
"`testDB` CHARACTER SET = 'latin2' "
|
|
"COLLATE = 'latin2_general_ci';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Create database queries are not the same")
|
|
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[1]
|
|
expected = ("CREATE DATABASE IF NOT EXISTS "
|
|
"`testDB2` CHARACTER SET = 'latin2' "
|
|
"COLLATE = 'latin2_general_ci';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Create database queries are not the same")
|
|
|
|
self.assertEqual(2, dbaas.LocalSqlClient.execute.call_count,
|
|
"The client object was not called exactly twice, " +
|
|
"it was called %d times"
|
|
% dbaas.LocalSqlClient.execute.call_count)
|
|
|
|
def test_create_database_no_db(self):
|
|
|
|
databases = []
|
|
|
|
self.mySqlAdmin.create_database(databases)
|
|
|
|
self.assertFalse(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was called when it wasn't " +
|
|
"supposed to")
|
|
|
|
def test_delete_database(self):
|
|
|
|
database = {"_name": "testDB"}
|
|
|
|
self.mySqlAdmin.delete_database(database)
|
|
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = "DROP DATABASE `testDB`;"
|
|
self.assertEqual(expected, args[0].text,
|
|
"Delete database queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_delete_user(self):
|
|
|
|
user = {"_name": "testUser", "_host": None}
|
|
|
|
self.mySqlAdmin.delete_user(user)
|
|
|
|
# For some reason, call_args is None.
|
|
call_args = dbaas.LocalSqlClient.execute.call_args
|
|
if call_args is not None:
|
|
args, _ = call_args
|
|
expected = "DROP USER `testUser`@`%`;"
|
|
self.assertEqual(expected, args[0].text,
|
|
"Delete user queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_create_user(self):
|
|
self.mySqlAdmin.create_user(FAKE_USER)
|
|
access_grants_expected = ("GRANT ALL PRIVILEGES ON `testDB`.* TO "
|
|
"`random`@`%` IDENTIFIED BY 'guesswhat';")
|
|
create_user_expected = ("GRANT USAGE ON *.* TO `random`@`%` "
|
|
"IDENTIFIED BY 'guesswhat';")
|
|
|
|
create_user, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
self.assertEqual(create_user_expected, create_user[0].text,
|
|
"Create user queries are not the same")
|
|
|
|
access_grants, _ = dbaas.LocalSqlClient.execute.call_args_list[1]
|
|
self.assertEqual(access_grants_expected, access_grants[0].text,
|
|
"Create user queries are not the same")
|
|
self.assertEqual(2, dbaas.LocalSqlClient.execute.call_count)
|
|
|
|
def test_list_databases(self):
|
|
self.mySqlAdmin.list_databases()
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = ["SELECT schema_name as name,",
|
|
"default_character_set_name as charset,",
|
|
"default_collation_name as collation",
|
|
"FROM information_schema.schemata",
|
|
("schema_name NOT IN ('" + "', '".join(CONF.ignore_dbs) +
|
|
"')"),
|
|
"ORDER BY schema_name ASC",
|
|
]
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
|
|
def test_list_databases_with_limit(self):
|
|
limit = 2
|
|
self.mySqlAdmin.list_databases(limit)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = ["SELECT schema_name as name,",
|
|
"default_character_set_name as charset,",
|
|
"default_collation_name as collation",
|
|
"FROM information_schema.schemata",
|
|
("schema_name NOT IN ('" + "', '".join(CONF.ignore_dbs) +
|
|
"')"),
|
|
"ORDER BY schema_name ASC",
|
|
]
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertTrue("LIMIT " + str(limit + 1) in args[0].text)
|
|
|
|
def test_list_databases_with_marker(self):
|
|
marker = "aMarker"
|
|
self.mySqlAdmin.list_databases(marker=marker)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = ["SELECT schema_name as name,",
|
|
"default_character_set_name as charset,",
|
|
"default_collation_name as collation",
|
|
"FROM information_schema.schemata",
|
|
("schema_name NOT IN ('" + "', '".join(CONF.ignore_dbs) +
|
|
"')"),
|
|
"ORDER BY schema_name ASC",
|
|
]
|
|
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
|
|
self.assertTrue("AND schema_name > '" + marker + "'" in args[0].text)
|
|
|
|
def test_list_databases_with_include_marker(self):
|
|
marker = "aMarker"
|
|
self.mySqlAdmin.list_databases(marker=marker, include_marker=True)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = ["SELECT schema_name as name,",
|
|
"default_character_set_name as charset,",
|
|
"default_collation_name as collation",
|
|
"FROM information_schema.schemata",
|
|
("schema_name NOT IN ('" + "', '".join(CONF.ignore_dbs) +
|
|
"')"),
|
|
"ORDER BY schema_name ASC",
|
|
]
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
|
|
self.assertTrue(("AND schema_name >= '%s'" % marker) in args[0].text)
|
|
|
|
def test_list_users(self):
|
|
self.mySqlAdmin.list_users()
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
|
|
expected = ["SELECT User, Host",
|
|
"FROM mysql.user",
|
|
"WHERE Host != 'localhost'",
|
|
"ORDER BY User",
|
|
]
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
self.assertFalse("AND Marker > '" in args[0].text)
|
|
|
|
def test_list_users_with_limit(self):
|
|
limit = 2
|
|
self.mySqlAdmin.list_users(limit)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
|
|
expected = ["SELECT User, Host",
|
|
"FROM mysql.user",
|
|
"WHERE Host != 'localhost'",
|
|
"ORDER BY User",
|
|
("LIMIT " + str(limit + 1)),
|
|
]
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
def test_list_users_with_marker(self):
|
|
marker = "aMarker"
|
|
self.mySqlAdmin.list_users(marker=marker)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
|
|
expected = ["SELECT User, Host, Marker",
|
|
"FROM mysql.user",
|
|
"WHERE Host != 'localhost'",
|
|
"ORDER BY User",
|
|
]
|
|
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
self.assertTrue("AND Marker > '" + marker + "'" in args[0].text)
|
|
|
|
def test_list_users_with_include_marker(self):
|
|
marker = "aMarker"
|
|
self.mySqlAdmin.list_users(marker=marker, include_marker=True)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
|
|
expected = ["SELECT User, Host",
|
|
"FROM mysql.user",
|
|
"WHERE Host != 'localhost'",
|
|
"ORDER BY User",
|
|
]
|
|
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
|
|
self.assertFalse("LIMIT " in args[0].text)
|
|
|
|
self.assertTrue("AND Marker >= '" + marker + "'" in args[0].text)
|
|
|
|
@patch.object(dbaas.MySqlAdmin, '_associate_dbs')
|
|
def test_get_user(self, mock_associate_dbs):
|
|
"""
|
|
Unit tests for mySqlAdmin.get_user.
|
|
This test case checks if the sql query formed by the get_user method
|
|
is correct or not by checking with expected query.
|
|
"""
|
|
username = "user1"
|
|
hostname = "%"
|
|
user = [{"User": "user1", "Host": "%", 'Password': 'some_thing'}]
|
|
dbaas.LocalSqlClient.execute.return_value.fetchall = Mock(
|
|
return_value=user)
|
|
self.mySqlAdmin.get_user(username, hostname)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args
|
|
expected = ["SELECT User, Host",
|
|
"FROM mysql.user",
|
|
"WHERE Host != 'localhost' AND User = 'user1'",
|
|
"ORDER BY User, Host",
|
|
]
|
|
|
|
for text in expected:
|
|
self.assertTrue(text in args[0].text, "%s not in query." % text)
|
|
self.assertEqual(1, mock_associate_dbs.call_count)
|
|
|
|
def test_fail_get_user(self):
|
|
username = "os_admin"
|
|
hostname = "host"
|
|
self.assertRaisesRegexp(BadRequest, "Username os_admin is not valid",
|
|
self.mySqlAdmin.get_user, username, hostname)
|
|
|
|
def test_grant_access(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user.password = 'some_password'
|
|
databases = ['db1']
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
self.mySqlAdmin.grant_access('test_user', '%', databases)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("GRANT ALL PRIVILEGES ON `db1`.* TO `test_user`@`%` "
|
|
"IDENTIFIED BY PASSWORD 'some_password';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Grant access queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_fail_grant_access(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user.password = 'some_password'
|
|
databases = ['mysql']
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
self.mySqlAdmin.grant_access('test_user', '%', databases)
|
|
# since mysql is not a database to be provided access to,
|
|
# testing that executed was not called in grant access.
|
|
dbaas.LocalSqlClient.execute.assert_not_called()
|
|
|
|
def test_is_root_enabled(self):
|
|
self.mySqlAdmin.is_root_enabled()
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("SELECT User FROM mysql.user WHERE "
|
|
"User = 'root' AND Host != 'localhost';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Find root enabled queries are not the same")
|
|
|
|
self.assertTrue(dbaas.LocalSqlClient.execute.called,
|
|
"The client object was not called")
|
|
|
|
def test_revoke_access(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user.password = 'some_password'
|
|
databases = ['db1']
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
self.mySqlAdmin.revoke_access('test_usr', '%', databases)
|
|
args, _ = dbaas.LocalSqlClient.execute.call_args_list[0]
|
|
expected = ("REVOKE ALL ON `['db1']`.* FROM `test_user`@`%`;")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Revoke access queries are not the same")
|
|
|
|
def test_list_access(self):
|
|
user = MagicMock()
|
|
user.name = "test_user"
|
|
user.host = "%"
|
|
user.databases = ['db1', 'db2']
|
|
with patch.object(self.mySqlAdmin, '_get_user', return_value=user):
|
|
databases = self.mySqlAdmin.list_access('test_usr', '%')
|
|
self.assertEqual(2, len(databases),
|
|
"List access queries are not the same")
|
|
|
|
|
|
class MySqlAppTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(MySqlAppTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout
|
|
self.orig_time_sleep = time.sleep
|
|
self.orig_unlink = os.unlink
|
|
self.orig_get_auth_password = dbaas.get_auth_password
|
|
self.orig_service_discovery = operating_system.service_discovery
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.mySqlApp = MySqlApp(self.appStatus)
|
|
mysql_service = {'cmd_start': Mock(),
|
|
'cmd_stop': Mock(),
|
|
'cmd_enable': Mock(),
|
|
'cmd_disable': Mock(),
|
|
'bin': Mock()}
|
|
operating_system.service_discovery = Mock(
|
|
return_value=mysql_service)
|
|
time.sleep = Mock()
|
|
os.unlink = Mock()
|
|
dbaas.get_auth_password = Mock()
|
|
self.mock_client = Mock()
|
|
self.mock_execute = Mock()
|
|
self.mock_client.__enter__ = Mock()
|
|
self.mock_client.__exit__ = Mock()
|
|
self.mock_client.__enter__.return_value.execute = self.mock_execute
|
|
|
|
def tearDown(self):
|
|
super(MySqlAppTest, self).tearDown()
|
|
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
time.sleep = self.orig_time_sleep
|
|
os.unlink = self.orig_unlink
|
|
operating_system.service_discovery = self.orig_service_discovery
|
|
dbaas.get_auth_password = self.orig_get_auth_password
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def mysql_starts_successfully(self):
|
|
def start(update_db=False):
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.mySqlApp.start_mysql.side_effect = start
|
|
|
|
def mysql_starts_unsuccessfully(self):
|
|
def start():
|
|
raise RuntimeError("MySQL failed to start!")
|
|
|
|
self.mySqlApp.start_mysql.side_effect = start
|
|
|
|
def mysql_stops_successfully(self):
|
|
def stop():
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mySqlApp.stop_db.side_effect = stop
|
|
|
|
def mysql_stops_unsuccessfully(self):
|
|
def stop():
|
|
raise RuntimeError("MySQL failed to stop!")
|
|
|
|
self.mySqlApp.stop_db.side_effect = stop
|
|
|
|
def test_stop_mysql(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mySqlApp.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_stop_mysql_with_db_update(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mySqlApp.stop_db(True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.SHUTDOWN.description}))
|
|
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_stop_mysql_do_not_start_on_reboot(self, mock_execute):
|
|
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mySqlApp.stop_db(True, True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.SHUTDOWN.description}))
|
|
self.assertEqual(2, mock_execute.call_count)
|
|
|
|
def test_stop_mysql_error(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.mySqlApp.state_change_wait_time = 1
|
|
self.assertRaises(RuntimeError, self.mySqlApp.stop_db)
|
|
|
|
@patch.object(operating_system, 'service_discovery',
|
|
side_effect=KeyError('error'))
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_stop_mysql_key_error(self, mock_execute, mock_service):
|
|
self.assertRaisesRegexp(RuntimeError, 'Service is not discovered.',
|
|
self.mySqlApp.stop_db)
|
|
self.assertEqual(0, mock_execute.call_count)
|
|
|
|
def test_restart_is_successful(self):
|
|
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.stop_db = Mock()
|
|
self.mysql_stops_successfully()
|
|
self.mysql_starts_successfully()
|
|
|
|
self.mySqlApp.restart()
|
|
|
|
self.assertTrue(self.mySqlApp.stop_db.called)
|
|
self.assertTrue(self.mySqlApp.start_mysql.called)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.RUNNING.description}))
|
|
|
|
def test_restart_mysql_wont_start_up(self):
|
|
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.stop_db = Mock()
|
|
self.mysql_stops_unsuccessfully()
|
|
self.mysql_starts_unsuccessfully()
|
|
|
|
self.assertRaises(RuntimeError, self.mySqlApp.restart)
|
|
|
|
self.assertTrue(self.mySqlApp.stop_db.called)
|
|
self.assertFalse(self.mySqlApp.start_mysql.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_wipe_ib_logfiles_error(self):
|
|
|
|
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
|
dbaas.utils.execute_with_timeout = mocked
|
|
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.mySqlApp.wipe_ib_logfiles)
|
|
|
|
def test_start_mysql(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.mySqlApp._enable_mysql_on_boot = Mock()
|
|
self.mySqlApp.start_mysql()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_mysql_with_db_update(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.mySqlApp._enable_mysql_on_boot = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.mySqlApp.start_mysql(update_db=True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.RUNNING.description}))
|
|
|
|
def test_start_mysql_runs_forever(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock()
|
|
self.mySqlApp._enable_mysql_on_boot = Mock()
|
|
self.mySqlApp.state_change_wait_time = 1
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.assertRaises(RuntimeError, self.mySqlApp.start_mysql)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.SHUTDOWN.description}))
|
|
|
|
def test_start_mysql_error(self):
|
|
|
|
self.mySqlApp._enable_mysql_on_boot = Mock()
|
|
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
|
dbaas.utils.execute_with_timeout = mocked
|
|
|
|
self.assertRaises(RuntimeError, self.mySqlApp.start_mysql)
|
|
|
|
def test_start_db_with_conf_changes(self):
|
|
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp._write_mycnf = Mock()
|
|
self.mysql_starts_successfully()
|
|
|
|
self.appStatus.status = rd_instance.ServiceStatuses.SHUTDOWN
|
|
self.mySqlApp.start_db_with_conf_changes(Mock())
|
|
|
|
self.assertTrue(self.mySqlApp._write_mycnf.called)
|
|
self.assertTrue(self.mySqlApp.start_mysql.called)
|
|
self.assertEqual(rd_instance.ServiceStatuses.RUNNING,
|
|
self.appStatus._get_actual_db_status())
|
|
|
|
def test_start_db_with_conf_changes_mysql_is_running(self):
|
|
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp._write_mycnf = Mock()
|
|
|
|
self.appStatus.status = rd_instance.ServiceStatuses.RUNNING
|
|
self.assertRaises(RuntimeError,
|
|
self.mySqlApp.start_db_with_conf_changes,
|
|
Mock())
|
|
|
|
def test_remove_overrides(self):
|
|
|
|
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
|
dbaas.utils.execute_with_timeout = mocked
|
|
self.assertRaises(ProcessExecutionError, self.mySqlApp.start_mysql)
|
|
|
|
@patch.object(operating_system, 'move')
|
|
@patch.object(operating_system, 'remove')
|
|
@patch.object(dbaas, 'get_auth_password', return_value='some_password')
|
|
@patch.object(dbaas.MySqlApp, '_write_config_overrides')
|
|
def test_reset_configuration(self, mock_write_overrides,
|
|
mock_get_auth_password, mock_remove,
|
|
mock_move):
|
|
configuration = {'config_contents': 'some junk'}
|
|
self.mySqlApp.reset_configuration(configuration=configuration)
|
|
self.assertEqual(1, mock_get_auth_password.call_count)
|
|
self.assertEqual(2, mock_move.call_count)
|
|
self.assertEqual(2, mock_remove.call_count)
|
|
self.assertEqual(0, mock_write_overrides.call_count)
|
|
|
|
@patch.object(operating_system, 'move')
|
|
@patch.object(operating_system, 'remove')
|
|
@patch.object(dbaas.MySqlApp, '_write_config_overrides')
|
|
def test__write_mycnf(self, mock_write_overrides, mock_remove, mock_move):
|
|
self.mySqlApp._write_mycnf('some_password', 'some junk', 'something')
|
|
self.assertEqual(2, mock_move.call_count)
|
|
self.assertEqual(2, mock_remove.call_count)
|
|
self.assertEqual(1, mock_write_overrides.call_count)
|
|
|
|
def test_mysql_error_in_write_config_verify_unlink(self):
|
|
configuration = {'config_contents': 'some junk'}
|
|
dbaas.utils.execute_with_timeout = (
|
|
Mock(side_effect=ProcessExecutionError('something')))
|
|
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.mySqlApp.reset_configuration,
|
|
configuration=configuration)
|
|
self.assertEqual(1, dbaas.utils.execute_with_timeout.call_count)
|
|
self.assertEqual(1, os.unlink.call_count)
|
|
self.assertEqual(1, dbaas.get_auth_password.call_count)
|
|
|
|
def test_mysql_error_in_write_config(self):
|
|
configuration = {'config_contents': 'some junk'}
|
|
dbaas.utils.execute_with_timeout = (
|
|
Mock(side_effect=ProcessExecutionError('something')))
|
|
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.mySqlApp.reset_configuration,
|
|
configuration=configuration)
|
|
self.assertEqual(1, dbaas.utils.execute_with_timeout.call_count)
|
|
self.assertEqual(1, dbaas.get_auth_password.call_count)
|
|
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test__enable_mysql_on_boot(self, mock_execute):
|
|
mysql_service = dbaas.operating_system.service_discovery(["mysql"])
|
|
self.mySqlApp._enable_mysql_on_boot()
|
|
self.assertEqual(1, mock_execute.call_count)
|
|
mock_execute.assert_called_with(mysql_service['cmd_enable'],
|
|
shell=True)
|
|
|
|
@patch.object(operating_system, 'service_discovery',
|
|
side_effect=KeyError('error'))
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_fail__enable_mysql_on_boot(self, mock_execute, mock_service):
|
|
self.assertRaisesRegexp(RuntimeError, 'Service is not discovered.',
|
|
self.mySqlApp._enable_mysql_on_boot)
|
|
self.assertEqual(0, mock_execute.call_count)
|
|
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test__disable_mysql_on_boot(self, mock_execute):
|
|
mysql_service = dbaas.operating_system.service_discovery(["mysql"])
|
|
self.mySqlApp._disable_mysql_on_boot()
|
|
self.assertEqual(1, mock_execute.call_count)
|
|
mock_execute.assert_called_with(mysql_service['cmd_disable'],
|
|
shell=True)
|
|
|
|
@patch.object(operating_system, 'service_discovery',
|
|
side_effect=KeyError('error'))
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_fail__disable_mysql_on_boot(self, mock_execute, mock_service):
|
|
self.assertRaisesRegexp(RuntimeError, 'Service is not discovered.',
|
|
self.mySqlApp._disable_mysql_on_boot)
|
|
self.assertEqual(0, mock_execute.call_count)
|
|
|
|
@patch.object(operating_system, 'move')
|
|
@patch.object(operating_system, 'chmod')
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_update_overrides(self, mock_execute, mock_chmod, mock_move):
|
|
override_value = 'something'
|
|
self.mySqlApp.update_overrides(override_value)
|
|
with open(dbaas.MYCNF_OVERRIDES_TMP, 'r') as test_file:
|
|
test_data = test_file.read()
|
|
self.assertEqual(override_value, test_data)
|
|
mock_chmod.assert_called_with(dbaas.MYCNF_OVERRIDES,
|
|
dbaas.FileMode.SET_GRP_RW_OTH_R,
|
|
as_root=True)
|
|
mock_move.assert_called_with(dbaas.MYCNF_OVERRIDES_TMP,
|
|
dbaas.MYCNF_OVERRIDES, as_root=True)
|
|
|
|
# Remove the residual file
|
|
os.remove(dbaas.MYCNF_OVERRIDES_TMP)
|
|
|
|
@patch.object(os.path, 'exists', return_value=True)
|
|
@patch.object(operating_system, 'remove')
|
|
def test_remove_override(self, mock_remove, mock_exists):
|
|
self.mySqlApp.remove_overrides()
|
|
self.assertEqual(1, mock_remove.call_count)
|
|
self.assertEqual(1, mock_exists.call_count)
|
|
mock_remove.assert_called_once_with(ANY, as_root=True)
|
|
|
|
@patch.object(operating_system, 'move')
|
|
@patch.object(operating_system, 'chmod')
|
|
def test_write_replication_source_overrides(self, mock_chmod, mock_move):
|
|
self.mySqlApp.write_replication_source_overrides('something')
|
|
self.assertEqual(1, mock_move.call_count)
|
|
self.assertEqual(1, mock_chmod.call_count)
|
|
|
|
@patch.object(dbaas.MySqlApp, '_write_replication_overrides')
|
|
def test_write_replication_replica_overrides(self, mock_write_overrides):
|
|
self.mySqlApp.write_replication_replica_overrides('something')
|
|
self.assertEqual(1, mock_write_overrides.call_count)
|
|
|
|
@patch.object(os.path, 'exists', return_value=True)
|
|
@patch.object(operating_system, 'remove')
|
|
def test_remove_replication_source_overrides(self, mock_remove, mock_exists
|
|
):
|
|
self.mySqlApp.remove_replication_source_overrides()
|
|
self.assertEqual(1, mock_remove.call_count)
|
|
self.assertEqual(1, mock_exists.call_count)
|
|
|
|
@patch.object(dbaas.MySqlApp, '_remove_replication_overrides')
|
|
def test_remove_replication_replica_overrides(self, mock_remove_overrides):
|
|
self.mySqlApp.remove_replication_replica_overrides()
|
|
self.assertEqual(1, mock_remove_overrides.call_count)
|
|
|
|
@patch.object(os.path, 'exists', return_value=True)
|
|
def test_exists_replication_source_overrides(self, mock_exists):
|
|
self.assertTrue(self.mySqlApp.exists_replication_source_overrides())
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_grant_replication_privilege(self, *args):
|
|
replication_user = {'name': 'testUSr', 'password': 'somePwd'}
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.grant_replication_privilege(replication_user)
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("GRANT REPLICATION SLAVE ON *.* TO `testUSr`@`%` "
|
|
"IDENTIFIED BY 'somePwd';")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Replication grant statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_get_port(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.get_port()
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SELECT @@port")
|
|
self.assertEqual(expected, args[0],
|
|
"Port queries are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_get_binlog_position(self, *args):
|
|
result = {'File': 'mysql-bin.003', 'Position': '73'}
|
|
self.mock_execute.return_value.first = Mock(return_value=result)
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
found_result = self.mySqlApp.get_binlog_position()
|
|
|
|
self.assertEqual(result['File'], found_result['log_file'])
|
|
self.assertEqual(result['Position'], found_result['position'])
|
|
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SHOW MASTER STATUS")
|
|
self.assertEqual(expected, args[0],
|
|
"Master status queries are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_execute_on_client(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.execute_on_client('show tables')
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("show tables")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
@patch.object(dbaas.MySqlApp, '_wait_for_slave_status')
|
|
def test_start_slave(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.start_slave()
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("START SLAVE")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
@patch.object(dbaas.MySqlApp, '_wait_for_slave_status')
|
|
def test_stop_slave_with_failover(self, *args):
|
|
self.mock_execute.return_value.first = Mock(
|
|
return_value={'Master_User': 'root'})
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
result = self.mySqlApp.stop_slave(True)
|
|
self.assertEqual('root', result['replication_user'])
|
|
|
|
expected = ["SHOW SLAVE STATUS", "STOP SLAVE", "RESET SLAVE ALL"]
|
|
self.assertEqual(len(expected), len(self.mock_execute.call_args_list))
|
|
for i in range(len(self.mock_execute.call_args_list)):
|
|
args, _ = self.mock_execute.call_args_list[i]
|
|
self.assertEqual(expected[i], args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
@patch.object(dbaas.MySqlApp, '_wait_for_slave_status')
|
|
def test_stop_slave_without_failover(self, *args):
|
|
self.mock_execute.return_value.first = Mock(
|
|
return_value={'Master_User': 'root'})
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
result = self.mySqlApp.stop_slave(False)
|
|
self.assertEqual('root', result['replication_user'])
|
|
|
|
expected = ["SHOW SLAVE STATUS", "STOP SLAVE", "RESET SLAVE ALL",
|
|
"DROP USER root"]
|
|
self.assertEqual(len(expected), len(self.mock_execute.call_args_list))
|
|
for i in range(len(self.mock_execute.call_args_list)):
|
|
args, _ = self.mock_execute.call_args_list[i]
|
|
self.assertEqual(expected[i], args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_stop_master(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.stop_master()
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("RESET MASTER")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test__wait_for_slave_status(self, *args):
|
|
mock_client = Mock()
|
|
mock_client.execute = Mock()
|
|
result = ['Slave_running', 'on']
|
|
mock_client.execute.return_value.first = Mock(return_value=result)
|
|
self.mySqlApp._wait_for_slave_status('ON', mock_client, 5)
|
|
args, _ = mock_client.execute.call_args_list[0]
|
|
expected = ("SHOW GLOBAL STATUS like 'slave_running'")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
@patch.object(utils, 'poll_until', side_effect=PollTimeOut)
|
|
def test_fail__wait_for_slave_status(self, *args):
|
|
self.assertRaisesRegexp(RuntimeError,
|
|
"Replication is not on after 5 seconds.",
|
|
self.mySqlApp._wait_for_slave_status, 'ON',
|
|
Mock(), 5)
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test__get_slave_status(self, *args):
|
|
self.mock_execute.return_value.first = Mock(return_value='some_thing')
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
result = self.mySqlApp._get_slave_status()
|
|
self.assertEqual('some_thing', result)
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SHOW SLAVE STATUS")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_get_latest_txn_id(self, *args):
|
|
self.mock_execute.return_value.first = Mock(return_value=['some_thing']
|
|
)
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
result = self.mySqlApp.get_latest_txn_id()
|
|
self.assertEqual('some_thing', result)
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SELECT @@global.gtid_executed")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_wait_for_txn(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.wait_for_txn('abcd')
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS('abcd')")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_get_txn_count(self, *args):
|
|
self.mock_execute.return_value.first = Mock(
|
|
return_value=['b1f3f33a-0789-ee1c-43f3-f8373e12f1ea:1'])
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
result = self.mySqlApp.get_txn_count()
|
|
self.assertEqual(1, result)
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SELECT @@global.gtid_executed")
|
|
self.assertEqual(expected, args[0],
|
|
"Sql statements are not the same")
|
|
|
|
|
|
class MySqlAppInstallTest(MySqlAppTest):
|
|
|
|
def setUp(self):
|
|
super(MySqlAppInstallTest, self).setUp()
|
|
self.orig_create_engine = sqlalchemy.create_engine
|
|
self.orig_pkg_version = dbaas.packager.pkg_version
|
|
self.orig_utils_execute_with_timeout = utils.execute_with_timeout
|
|
self.mock_client = Mock()
|
|
self.mock_execute = Mock()
|
|
self.mock_client.__enter__ = Mock()
|
|
self.mock_client.__exit__ = Mock()
|
|
self.mock_client.__enter__.return_value.execute = self.mock_execute
|
|
|
|
def tearDown(self):
|
|
super(MySqlAppInstallTest, self).tearDown()
|
|
sqlalchemy.create_engine = self.orig_create_engine
|
|
dbaas.packager.pkg_version = self.orig_pkg_version
|
|
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
|
|
def test_install(self):
|
|
|
|
self.mySqlApp._install_mysql = Mock()
|
|
pkg.Package.pkg_is_installed = Mock(return_value=False)
|
|
utils.execute_with_timeout = Mock()
|
|
pkg.Package.pkg_install = Mock()
|
|
self.mySqlApp._clear_mysql_config = Mock()
|
|
self.mySqlApp._create_mysql_confd_dir = Mock()
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.install_if_needed(["package"])
|
|
self.assertTrue(pkg.Package.pkg_install.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_secure(self):
|
|
|
|
dbaas.clear_expired_password = Mock()
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.stop_db = Mock()
|
|
self.mySqlApp._write_mycnf = Mock()
|
|
self.mysql_stops_successfully()
|
|
self.mysql_starts_successfully()
|
|
sqlalchemy.create_engine = Mock()
|
|
|
|
self.mySqlApp.secure('contents', None)
|
|
|
|
self.assertTrue(self.mySqlApp.stop_db.called)
|
|
self.assertTrue(self.mySqlApp._write_mycnf.called)
|
|
self.assertTrue(self.mySqlApp.start_mysql.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
@patch.object(utils, 'generate_random_password',
|
|
return_value='some_password')
|
|
def test_secure_root(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.secure_root()
|
|
update_root_password, _ = self.mock_execute.call_args_list[0]
|
|
update_expected = ("UPDATE mysql.user SET Password="
|
|
"PASSWORD('some_password') "
|
|
"WHERE User = 'root' AND Host = 'localhost';")
|
|
|
|
remove_root, _ = self.mock_execute.call_args_list[1]
|
|
remove_expected = ("DELETE FROM mysql.user WHERE "
|
|
"User = 'root' AND Host != 'localhost';")
|
|
|
|
self.assertEqual(update_expected, update_root_password[0].text,
|
|
"Update root password queries are not the same")
|
|
self.assertEqual(remove_expected, remove_root[0].text,
|
|
"Remove root queries are not the same")
|
|
|
|
@patch.object(operating_system, 'create_directory')
|
|
def test__create_mysql_confd_dir(self, mkdir_mock):
|
|
self.mySqlApp._create_mysql_confd_dir()
|
|
mkdir_mock.assert_called_once_with('/etc/mysql/conf.d', as_root=True)
|
|
|
|
@patch.object(operating_system, 'move')
|
|
def test__clear_mysql_config(self, mock_move):
|
|
self.mySqlApp._clear_mysql_config()
|
|
self.assertEqual(3, mock_move.call_count)
|
|
|
|
@patch.object(operating_system, 'move', side_effect=ProcessExecutionError)
|
|
def test_exception__clear_mysql_config(self, mock_move):
|
|
self.mySqlApp._clear_mysql_config()
|
|
# call-count needs to be same as normal,
|
|
# because exception is eaten to make the flow goto next file-move.
|
|
self.assertEqual(3, mock_move.call_count)
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_apply_overrides(self, *args):
|
|
overrides = {'sort_buffer_size': 1000000}
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.apply_overrides(overrides)
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("SET GLOBAL sort_buffer_size=1000000")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Set global statements are not the same")
|
|
|
|
@patch.object(dbaas, 'get_engine',
|
|
return_value=MagicMock(name='get_engine'))
|
|
def test_make_read_only(self, *args):
|
|
with patch.object(dbaas, 'LocalSqlClient',
|
|
return_value=self.mock_client):
|
|
self.mySqlApp.make_read_only('ON')
|
|
args, _ = self.mock_execute.call_args_list[0]
|
|
expected = ("set global read_only = ON")
|
|
self.assertEqual(expected, args[0].text,
|
|
"Set read_only statements are not the same")
|
|
|
|
def test_install_install_error(self):
|
|
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.stop_db = Mock()
|
|
pkg.Package.pkg_is_installed = Mock(return_value=False)
|
|
self.mySqlApp._clear_mysql_config = Mock()
|
|
self.mySqlApp._create_mysql_confd_dir = Mock()
|
|
pkg.Package.pkg_install = \
|
|
Mock(side_effect=pkg.PkgPackageStateError("Install error"))
|
|
|
|
self.assertRaises(pkg.PkgPackageStateError,
|
|
self.mySqlApp.install_if_needed, ["package"])
|
|
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_secure_write_conf_error(self):
|
|
|
|
dbaas.clear_expired_password = Mock()
|
|
self.mySqlApp.start_mysql = Mock()
|
|
self.mySqlApp.stop_db = Mock()
|
|
self.mySqlApp._write_mycnf = Mock(
|
|
side_effect=IOError("Could not write file"))
|
|
self.mysql_stops_successfully()
|
|
self.mysql_starts_successfully()
|
|
sqlalchemy.create_engine = Mock()
|
|
|
|
self.assertRaises(IOError, self.mySqlApp.secure, "foo", None)
|
|
|
|
self.assertTrue(self.mySqlApp.stop_db.called)
|
|
self.assertTrue(self.mySqlApp._write_mycnf.called)
|
|
self.assertFalse(self.mySqlApp.start_mysql.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
|
|
class TextClauseMatcher(object):
|
|
|
|
def __init__(self, text):
|
|
self.text = text
|
|
|
|
def __repr__(self):
|
|
return "TextClause(%s)" % self.text
|
|
|
|
def __eq__(self, arg):
|
|
print("Matching %s" % arg.text)
|
|
return self.text in arg.text
|
|
|
|
|
|
def mock_sql_connection():
|
|
utils.execute_with_timeout = MagicMock(return_value=['fake_password',
|
|
None])
|
|
mock_engine = MagicMock()
|
|
sqlalchemy.create_engine = MagicMock(return_value=mock_engine)
|
|
mock_conn = MagicMock()
|
|
dbaas.LocalSqlClient.__enter__ = MagicMock(return_value=mock_conn)
|
|
dbaas.LocalSqlClient.__exit__ = MagicMock(return_value=None)
|
|
return mock_conn
|
|
|
|
|
|
class MySqlAppMockTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(MySqlAppMockTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = utils.execute_with_timeout
|
|
|
|
def tearDown(self):
|
|
super(MySqlAppMockTest, self).tearDown()
|
|
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
|
|
def test_secure_keep_root(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
with patch.object(mock_conn, 'execute', return_value=None):
|
|
utils.execute_with_timeout = MagicMock(return_value=None)
|
|
# skip writing the file for now
|
|
with patch.object(os.path, 'isfile', return_value=False):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
dbaas.clear_expired_password = MagicMock(return_value=None)
|
|
app = MySqlApp(mock_status)
|
|
app._write_mycnf = MagicMock(return_value=True)
|
|
app.start_mysql = MagicMock(return_value=None)
|
|
app.stop_db = MagicMock(return_value=None)
|
|
app.secure('foo', None)
|
|
self.assertTrue(mock_conn.execute.called)
|
|
|
|
def test_secure_with_mycnf_error(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
with patch.object(mock_conn, 'execute', return_value=None):
|
|
with patch.object(operating_system, 'service_discovery',
|
|
return_value={'cmd_stop': 'service mysql stop'}):
|
|
utils.execute_with_timeout = MagicMock(return_value=None)
|
|
# skip writing the file for now
|
|
with patch.object(os.path, 'isfile', return_value=False):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
dbaas.clear_expired_password = MagicMock(return_value=None)
|
|
app = MySqlApp(mock_status)
|
|
dbaas.clear_expired_password = MagicMock(return_value=None)
|
|
self.assertRaises(TypeError, app.secure, None, None)
|
|
self.assertTrue(mock_conn.execute.called)
|
|
# At least called twice
|
|
self.assertTrue(mock_conn.execute.call_count >= 2)
|
|
(mock_status.wait_for_real_status_to_change_to.
|
|
assert_called_with(rd_instance.ServiceStatuses.SHUTDOWN,
|
|
app.state_change_wait_time, False))
|
|
|
|
|
|
class MySqlRootStatusTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(MySqlRootStatusTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = utils.execute_with_timeout
|
|
|
|
def tearDown(self):
|
|
super(MySqlRootStatusTest, self).tearDown()
|
|
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
|
|
def test_root_is_enabled(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
mock_rs = MagicMock()
|
|
mock_rs.rowcount = 1
|
|
with patch.object(mock_conn, 'execute', return_value=mock_rs):
|
|
self.assertThat(MySqlRootAccess().is_root_enabled(), Is(True))
|
|
|
|
def test_root_is_not_enabled(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
mock_rs = MagicMock()
|
|
mock_rs.rowcount = 0
|
|
with patch.object(mock_conn, 'execute', return_value=mock_rs):
|
|
self.assertThat(MySqlRootAccess.is_root_enabled(), Equals(False))
|
|
|
|
def test_enable_root(self):
|
|
mock_conn = mock_sql_connection()
|
|
|
|
with patch.object(mock_conn, 'execute', return_value=None):
|
|
# invocation
|
|
user_ser = MySqlRootAccess.enable_root()
|
|
# verification
|
|
self.assertThat(user_ser, Not(Is(None)))
|
|
mock_conn.execute.assert_any_call(TextClauseMatcher('CREATE USER'),
|
|
user='root', host='%')
|
|
mock_conn.execute.assert_any_call(TextClauseMatcher(
|
|
'GRANT ALL PRIVILEGES ON *.*'))
|
|
mock_conn.execute.assert_any_call(TextClauseMatcher(
|
|
'UPDATE mysql.user'))
|
|
|
|
def test_enable_root_failed(self):
|
|
with patch.object(models.MySQLUser, '_is_valid_user_name',
|
|
return_value=False):
|
|
self.assertRaises(ValueError, MySqlAdmin().enable_root)
|
|
|
|
|
|
class MockStats:
|
|
f_blocks = 1024 ** 2
|
|
f_bsize = 4096
|
|
f_bfree = 512 * 1024
|
|
|
|
|
|
class InterrogatorTest(testtools.TestCase):
|
|
|
|
def tearDown(self):
|
|
super(InterrogatorTest, self).tearDown()
|
|
|
|
def test_to_gb(self):
|
|
result = to_gb(123456789)
|
|
self.assertEqual(0.11, result)
|
|
|
|
def test_to_gb_zero(self):
|
|
result = to_gb(0)
|
|
self.assertEqual(0.0, result)
|
|
|
|
def test_get_filesystem_volume_stats(self):
|
|
with patch.object(os, 'statvfs', return_value=MockStats):
|
|
result = get_filesystem_volume_stats('/some/path/')
|
|
|
|
self.assertEqual(4096, result['block_size'])
|
|
self.assertEqual(1048576, result['total_blocks'])
|
|
self.assertEqual(524288, result['free_blocks'])
|
|
self.assertEqual(4.0, result['total'])
|
|
self.assertEqual(2147483648, result['free'])
|
|
self.assertEqual(2.0, result['used'])
|
|
|
|
def test_get_filesystem_volume_stats_error(self):
|
|
with patch.object(os, 'statvfs', side_effect=OSError):
|
|
self.assertRaises(
|
|
RuntimeError,
|
|
get_filesystem_volume_stats, '/nonexistent/path')
|
|
|
|
|
|
class ServiceRegistryTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(ServiceRegistryTest, self).setUp()
|
|
|
|
def tearDown(self):
|
|
super(ServiceRegistryTest, self).tearDown()
|
|
|
|
def test_datastore_registry_with_extra_manager(self):
|
|
datastore_registry_ext_test = {
|
|
'test': 'trove.guestagent.datastore.test.manager.Manager',
|
|
}
|
|
dbaas_sr.get_custom_managers = Mock(
|
|
return_value=datastore_registry_ext_test)
|
|
test_dict = dbaas_sr.datastore_registry()
|
|
self.assertEqual(datastore_registry_ext_test.get('test', None),
|
|
test_dict.get('test'))
|
|
self.assertEqual('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager',
|
|
test_dict.get('mysql'))
|
|
self.assertEqual('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager',
|
|
test_dict.get('percona'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.redis.'
|
|
'manager.Manager',
|
|
test_dict.get('redis'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.cassandra.'
|
|
'manager.Manager',
|
|
test_dict.get('cassandra'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.'
|
|
'couchbase.manager.Manager',
|
|
test_dict.get('couchbase'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.mongodb.'
|
|
'manager.Manager',
|
|
test_dict.get('mongodb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.couchdb.'
|
|
'manager.Manager',
|
|
test_dict.get('couchdb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.db2.'
|
|
'manager.Manager',
|
|
test_dict.get('db2'))
|
|
|
|
def test_datastore_registry_with_existing_manager(self):
|
|
datastore_registry_ext_test = {
|
|
'mysql': 'trove.guestagent.datastore.mysql.'
|
|
'manager.Manager123',
|
|
}
|
|
dbaas_sr.get_custom_managers = Mock(
|
|
return_value=datastore_registry_ext_test)
|
|
test_dict = dbaas_sr.datastore_registry()
|
|
self.assertEqual('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager123',
|
|
test_dict.get('mysql'))
|
|
self.assertEqual('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager',
|
|
test_dict.get('percona'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.redis.'
|
|
'manager.Manager',
|
|
test_dict.get('redis'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.cassandra.'
|
|
'manager.Manager',
|
|
test_dict.get('cassandra'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.couchbase.'
|
|
'manager.Manager',
|
|
test_dict.get('couchbase'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.mongodb.'
|
|
'manager.Manager',
|
|
test_dict.get('mongodb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.couchdb.'
|
|
'manager.Manager',
|
|
test_dict.get('couchdb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.vertica.'
|
|
'manager.Manager',
|
|
test_dict.get('vertica'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.db2.'
|
|
'manager.Manager',
|
|
test_dict.get('db2'))
|
|
|
|
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('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager',
|
|
test_dict.get('mysql'))
|
|
self.assertEqual('trove.guestagent.datastore.mysql.'
|
|
'manager.Manager',
|
|
test_dict.get('percona'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.redis.'
|
|
'manager.Manager',
|
|
test_dict.get('redis'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.cassandra.'
|
|
'manager.Manager',
|
|
test_dict.get('cassandra'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.couchbase.'
|
|
'manager.Manager',
|
|
test_dict.get('couchbase'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.mongodb.'
|
|
'manager.Manager',
|
|
test_dict.get('mongodb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.couchdb.'
|
|
'manager.Manager',
|
|
test_dict.get('couchdb'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.vertica.'
|
|
'manager.Manager',
|
|
test_dict.get('vertica'))
|
|
self.assertEqual('trove.guestagent.datastore.experimental.db2.'
|
|
'manager.Manager',
|
|
test_dict.get('db2'))
|
|
|
|
|
|
class KeepAliveConnectionTest(testtools.TestCase):
|
|
|
|
class OperationalError(Exception):
|
|
|
|
def __init__(self, value):
|
|
self.args = [value]
|
|
|
|
def __str__(self):
|
|
return repr(self.value)
|
|
|
|
def setUp(self):
|
|
super(KeepAliveConnectionTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout
|
|
self.orig_LOG_err = dbaas.LOG
|
|
|
|
def tearDown(self):
|
|
super(KeepAliveConnectionTest, self).tearDown()
|
|
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
dbaas.LOG = self.orig_LOG_err
|
|
|
|
def test_checkout_type_error(self):
|
|
|
|
dbapi_con = Mock()
|
|
dbapi_con.ping = Mock(side_effect=TypeError("Type Error"))
|
|
|
|
self.keepAliveConn = KeepAliveConnection()
|
|
self.assertRaises(TypeError, self.keepAliveConn.checkout,
|
|
dbapi_con, Mock(), Mock())
|
|
|
|
def test_checkout_disconnection_error(self):
|
|
|
|
dbapi_con = Mock()
|
|
dbapi_con.OperationalError = self.OperationalError
|
|
dbapi_con.ping = Mock(side_effect=dbapi_con.OperationalError(2013))
|
|
|
|
self.keepAliveConn = KeepAliveConnection()
|
|
self.assertRaises(sqlalchemy.exc.DisconnectionError,
|
|
self.keepAliveConn.checkout,
|
|
dbapi_con, Mock(), Mock())
|
|
|
|
def test_checkout_operation_error(self):
|
|
|
|
dbapi_con = Mock()
|
|
dbapi_con.OperationalError = self.OperationalError
|
|
dbapi_con.ping = Mock(side_effect=dbapi_con.OperationalError(1234))
|
|
|
|
self.keepAliveConn = KeepAliveConnection()
|
|
self.assertRaises(self.OperationalError, self.keepAliveConn.checkout,
|
|
dbapi_con, Mock(), Mock())
|
|
|
|
|
|
class BaseDbStatusTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(BaseDbStatusTest, self).setUp()
|
|
util.init_db()
|
|
self.orig_dbaas_time_sleep = time.sleep
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
dbaas.CONF.guest_id = self.FAKE_ID
|
|
|
|
def tearDown(self):
|
|
super(BaseDbStatusTest, self).tearDown()
|
|
time.sleep = self.orig_dbaas_time_sleep
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
dbaas.CONF.guest_id = None
|
|
|
|
def test_begin_install(self):
|
|
|
|
self.baseDbStatus = BaseDbStatus()
|
|
|
|
self.baseDbStatus.begin_install()
|
|
|
|
self.assertEqual(rd_instance.ServiceStatuses.BUILDING,
|
|
self.baseDbStatus.status)
|
|
|
|
def test_begin_restart(self):
|
|
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.restart_mode = False
|
|
|
|
self.baseDbStatus.begin_restart()
|
|
|
|
self.assertTrue(self.baseDbStatus.restart_mode)
|
|
|
|
def test_end_install_or_restart(self):
|
|
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus._get_actual_db_status = Mock(
|
|
return_value=rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.baseDbStatus.end_install_or_restart()
|
|
|
|
self.assertEqual(rd_instance.ServiceStatuses.SHUTDOWN,
|
|
self.baseDbStatus.status)
|
|
self.assertFalse(self.baseDbStatus.restart_mode)
|
|
|
|
def test_is_installed(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.RUNNING
|
|
|
|
self.assertTrue(self.baseDbStatus.is_installed)
|
|
|
|
def test_is_installed_none(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = None
|
|
|
|
self.assertTrue(self.baseDbStatus.is_installed)
|
|
|
|
def test_is_installed_building(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.BUILDING
|
|
|
|
self.assertFalse(self.baseDbStatus.is_installed)
|
|
|
|
def test_is_installed_new(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.NEW
|
|
|
|
self.assertFalse(self.baseDbStatus.is_installed)
|
|
|
|
def test_is_installed_failed(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.FAILED
|
|
|
|
self.assertFalse(self.baseDbStatus.is_installed)
|
|
|
|
def test_is_restarting(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.restart_mode = True
|
|
|
|
self.assertTrue(self.baseDbStatus._is_restarting)
|
|
|
|
def test_is_running(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.RUNNING
|
|
|
|
self.assertTrue(self.baseDbStatus.is_running)
|
|
|
|
def test_is_running_not(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus.status = rd_instance.ServiceStatuses.SHUTDOWN
|
|
|
|
self.assertFalse(self.baseDbStatus.is_running)
|
|
|
|
def test_wait_for_real_status_to_change_to(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus._get_actual_db_status = Mock(
|
|
return_value=rd_instance.ServiceStatuses.RUNNING)
|
|
time.sleep = Mock()
|
|
|
|
self.assertTrue(self.baseDbStatus.
|
|
wait_for_real_status_to_change_to
|
|
(rd_instance.ServiceStatuses.RUNNING, 10))
|
|
|
|
def test_wait_for_real_status_to_change_to_timeout(self):
|
|
self.baseDbStatus = BaseDbStatus()
|
|
self.baseDbStatus._get_actual_db_status = Mock(
|
|
return_value=rd_instance.ServiceStatuses.RUNNING)
|
|
time.sleep = Mock()
|
|
|
|
self.assertFalse(self.baseDbStatus.
|
|
wait_for_real_status_to_change_to
|
|
(rd_instance.ServiceStatuses.SHUTDOWN, 10))
|
|
|
|
|
|
class MySqlAppStatusTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(MySqlAppStatusTest, self).setUp()
|
|
util.init_db()
|
|
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout
|
|
self.orig_load_mysqld_options = dbaas.load_mysqld_options
|
|
self.orig_dbaas_os_path_exists = dbaas.os.path.exists
|
|
self.orig_dbaas_time_sleep = time.sleep
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
dbaas.CONF.guest_id = self.FAKE_ID
|
|
|
|
def tearDown(self):
|
|
super(MySqlAppStatusTest, self).tearDown()
|
|
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
|
dbaas.load_mysqld_options = self.orig_load_mysqld_options
|
|
dbaas.os.path.exists = self.orig_dbaas_os_path_exists
|
|
time.sleep = self.orig_dbaas_time_sleep
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
dbaas.CONF.guest_id = None
|
|
|
|
def test_get_actual_db_status(self):
|
|
|
|
dbaas.utils.execute_with_timeout = Mock(return_value=(None, None))
|
|
|
|
self.mySqlAppStatus = MySqlAppStatus()
|
|
status = self.mySqlAppStatus._get_actual_db_status()
|
|
|
|
self.assertEqual(rd_instance.ServiceStatuses.RUNNING, status)
|
|
|
|
@patch.object(utils, 'execute_with_timeout',
|
|
side_effect=ProcessExecutionError())
|
|
@patch.object(os.path, 'exists', return_value=True)
|
|
def test_get_actual_db_status_error_crashed(self, mock_exists,
|
|
mock_execute):
|
|
dbaas.load_mysqld_options = Mock(return_value={})
|
|
self.mySqlAppStatus = MySqlAppStatus()
|
|
status = self.mySqlAppStatus._get_actual_db_status()
|
|
self.assertEqual(rd_instance.ServiceStatuses.CRASHED, status)
|
|
|
|
def test_get_actual_db_status_error_shutdown(self):
|
|
|
|
mocked = Mock(side_effect=ProcessExecutionError())
|
|
dbaas.utils.execute_with_timeout = mocked
|
|
dbaas.load_mysqld_options = Mock(return_value={})
|
|
dbaas.os.path.exists = Mock(return_value=False)
|
|
|
|
self.mySqlAppStatus = MySqlAppStatus()
|
|
status = self.mySqlAppStatus._get_actual_db_status()
|
|
|
|
self.assertEqual(rd_instance.ServiceStatuses.SHUTDOWN, status)
|
|
|
|
def test_get_actual_db_status_error_blocked(self):
|
|
|
|
dbaas.utils.execute_with_timeout = MagicMock(
|
|
side_effect=[ProcessExecutionError(), ("some output", None)])
|
|
dbaas.load_mysqld_options = Mock()
|
|
dbaas.os.path.exists = Mock(return_value=True)
|
|
|
|
self.mySqlAppStatus = MySqlAppStatus()
|
|
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)
|
|
|
|
with patch.multiple(RedisApp, _build_admin_client=DEFAULT,
|
|
_init_overrides_dir=DEFAULT):
|
|
self.app = RedisApp(state_change_wait_time=0)
|
|
|
|
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
|
|
|
|
def test_install_if_needed_installed(self):
|
|
with patch.object(pkg.Package, 'pkg_is_installed', return_value=True):
|
|
with patch.object(RedisApp, '_install_redis', return_value=None):
|
|
self.app.install_if_needed('bar')
|
|
pkg.Package.pkg_is_installed.assert_any_call('bar')
|
|
self.assertEqual(0, RedisApp._install_redis.call_count)
|
|
|
|
def test_install_if_needed_not_installed(self):
|
|
with patch.object(pkg.Package, 'pkg_is_installed', return_value=False):
|
|
with patch.object(RedisApp, '_install_redis', return_value=None):
|
|
self.app.install_if_needed('asdf')
|
|
pkg.Package.pkg_is_installed.assert_any_call('asdf')
|
|
RedisApp._install_redis.assert_any_call('asdf')
|
|
|
|
def test_install_redis(self):
|
|
with patch.object(utils, 'execute_with_timeout'):
|
|
with patch.object(pkg.Package, 'pkg_install', return_value=None):
|
|
with patch.object(RedisApp, 'start_redis', return_value=None):
|
|
self.app._install_redis('redis')
|
|
pkg.Package.pkg_install.assert_any_call('redis', {}, 1200)
|
|
RedisApp.start_redis.assert_any_call()
|
|
self.assertTrue(utils.execute_with_timeout.called)
|
|
|
|
def test_enable_redis_on_boot_without_upstart(self):
|
|
cmd = '123'
|
|
with patch.object(operating_system, 'service_discovery',
|
|
return_value={'cmd_enable': cmd}):
|
|
with patch.object(utils, 'execute_with_timeout',
|
|
return_value=None):
|
|
self.app._enable_redis_on_boot()
|
|
operating_system.service_discovery.assert_any_call(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
utils.execute_with_timeout.assert_any_call(
|
|
cmd, shell=True)
|
|
|
|
def test_enable_redis_on_boot_with_upstart(self):
|
|
cmd = '123'
|
|
with patch.object(operating_system, 'service_discovery',
|
|
return_value={'cmd_enable': cmd}):
|
|
with patch.object(utils, 'execute_with_timeout',
|
|
return_value=None):
|
|
self.app._enable_redis_on_boot()
|
|
operating_system.service_discovery.assert_any_call(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
utils.execute_with_timeout.assert_any_call(
|
|
cmd, shell=True)
|
|
|
|
def test_disable_redis_on_boot_with_upstart(self):
|
|
cmd = '123'
|
|
with patch.object(operating_system, 'service_discovery',
|
|
return_value={'cmd_disable': cmd}):
|
|
with patch.object(utils, 'execute_with_timeout',
|
|
return_value=None):
|
|
self.app._disable_redis_on_boot()
|
|
operating_system.service_discovery.assert_any_call(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
utils.execute_with_timeout.assert_any_call(
|
|
cmd, shell=True)
|
|
|
|
def test_disable_redis_on_boot_without_upstart(self):
|
|
cmd = '123'
|
|
with patch.object(operating_system, 'service_discovery',
|
|
return_value={'cmd_disable': cmd}):
|
|
with patch.object(utils, 'execute_with_timeout',
|
|
return_value=None):
|
|
self.app._disable_redis_on_boot()
|
|
operating_system.service_discovery.assert_any_call(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
utils.execute_with_timeout.assert_any_call(
|
|
cmd, shell=True)
|
|
|
|
def test_stop_db_without_fail(self):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
self.app.status = mock_status
|
|
RedisApp._disable_redis_on_boot = MagicMock(
|
|
return_value=None)
|
|
|
|
with patch.object(operating_system, 'stop_service') as stop_srv_mock:
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
self.app.stop_db(do_not_start_on_reboot=True)
|
|
|
|
stop_srv_mock.assert_called_once_with(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
self.assertTrue(RedisApp._disable_redis_on_boot.called)
|
|
self.assertTrue(
|
|
mock_status.wait_for_real_status_to_change_to.called)
|
|
|
|
def test_stop_db_with_failure(self):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
self.app.status = mock_status
|
|
RedisApp._disable_redis_on_boot = MagicMock(
|
|
return_value=None)
|
|
|
|
with patch.object(operating_system, 'stop_service') as stop_srv_mock:
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=False)
|
|
self.app.stop_db(do_not_start_on_reboot=True)
|
|
|
|
stop_srv_mock.assert_called_once_with(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
self.assertTrue(RedisApp._disable_redis_on_boot.called)
|
|
self.assertTrue(mock_status.end_install_or_restart.called)
|
|
self.assertTrue(
|
|
mock_status.wait_for_real_status_to_change_to.called)
|
|
|
|
def test_restart(self):
|
|
mock_status = MagicMock()
|
|
self.app.status = mock_status
|
|
mock_status.begin_restart = MagicMock(return_value=None)
|
|
with patch.object(RedisApp, 'stop_db', return_value=None):
|
|
with patch.object(RedisApp, 'start_redis', return_value=None):
|
|
mock_status.end_install_or_restart = MagicMock(
|
|
return_value=None)
|
|
self.app.restart()
|
|
mock_status.begin_restart.assert_any_call()
|
|
RedisApp.stop_db.assert_any_call()
|
|
RedisApp.start_redis.assert_any_call()
|
|
mock_status.end_install_or_restart.assert_any_call()
|
|
|
|
def test_start_redis(self):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
|
|
self._assert_start_redis(mock_status)
|
|
|
|
@patch.object(utils, 'execute_with_timeout')
|
|
def test_start_redis_with_failure(self, exec_mock):
|
|
mock_status = MagicMock()
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=False)
|
|
mock_status.end_install_or_restart = MagicMock()
|
|
|
|
self._assert_start_redis(mock_status)
|
|
|
|
exec_mock.assert_called_once_with('pkill', '-9', 'redis-server',
|
|
run_as_root=True, root_helper='sudo')
|
|
|
|
mock_status.end_install_or_restart.assert_called_once_with()
|
|
|
|
@patch.multiple(operating_system, start_service=DEFAULT,
|
|
enable_service_on_boot=DEFAULT)
|
|
def _assert_start_redis(self, mock_status, start_service,
|
|
enable_service_on_boot):
|
|
self.app.status = mock_status
|
|
|
|
self.app.start_redis()
|
|
|
|
mock_status.wait_for_real_status_to_change_to.assert_called_once_with(
|
|
rd_instance.ServiceStatuses.RUNNING, ANY, False)
|
|
enable_service_on_boot.assert_called_once_with(
|
|
RedisSystem.SERVICE_CANDIDATES)
|
|
start_service.assert_called_once_with(RedisSystem.SERVICE_CANDIDATES)
|
|
|
|
|
|
class CassandraDBAppTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(CassandraDBAppTest, self).setUp()
|
|
self.utils_execute_with_timeout = (
|
|
cass_service.utils.execute_with_timeout)
|
|
self.sleep = time.sleep
|
|
self.pkg_version = cass_service.packager.pkg_version
|
|
self.pkg = cass_service.packager
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.cassandra = cass_service.CassandraApp(self.appStatus)
|
|
self.orig_unlink = os.unlink
|
|
|
|
def tearDown(self):
|
|
|
|
super(CassandraDBAppTest, self).tearDown()
|
|
cass_service.utils.execute_with_timeout = (self.
|
|
utils_execute_with_timeout)
|
|
time.sleep = self.sleep
|
|
cass_service.packager.pkg_version = self.pkg_version
|
|
cass_service.packager = self.pkg
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def test_stop_db(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.cassandra.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_stop_db_with_db_update(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.cassandra.stop_db(True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.SHUTDOWN.description}))
|
|
|
|
def test_stop_db_error(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.cassandra.state_change_wait_time = 1
|
|
self.assertRaises(RuntimeError, self.cassandra.stop_db)
|
|
|
|
def test_restart(self):
|
|
|
|
self.cassandra.stop_db = Mock()
|
|
self.cassandra.start_db = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.cassandra.restart()
|
|
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.RUNNING.description}))
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_cassandra(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.cassandra.start_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_cassandra_runs_forever(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
(self.cassandra.status.
|
|
wait_for_real_status_to_change_to) = Mock(return_value=False)
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.assertRaises(RuntimeError, self.cassandra.stop_db)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.SHUTDOWN.description}))
|
|
|
|
def test_start_db_with_db_update(self):
|
|
|
|
cass_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.cassandra.start_db(True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID,
|
|
{'service_status':
|
|
rd_instance.ServiceStatuses.RUNNING.description}))
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_cassandra_error(self):
|
|
self.cassandra._enable_db_on_boot = Mock()
|
|
self.cassandra.state_change_wait_time = 1
|
|
cass_service.utils.execute_with_timeout = Mock(
|
|
side_effect=ProcessExecutionError('Error'))
|
|
|
|
self.assertRaises(RuntimeError, self.cassandra.start_db)
|
|
|
|
def test_install(self):
|
|
|
|
self.cassandra._install_db = Mock()
|
|
self.pkg.pkg_is_installed = Mock(return_value=False)
|
|
self.cassandra.install_if_needed(['cassandra'])
|
|
self.assertTrue(self.cassandra._install_db.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_install_install_error(self):
|
|
|
|
self.cassandra.start_db = Mock()
|
|
self.cassandra.stop_db = Mock()
|
|
self.pkg.pkg_is_installed = Mock(return_value=False)
|
|
self.cassandra._install_db = Mock(
|
|
side_effect=pkg.PkgPackageStateError("Install error"))
|
|
|
|
self.assertRaises(pkg.PkgPackageStateError,
|
|
self.cassandra.install_if_needed,
|
|
['cassandra=1.2.10'])
|
|
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_cassandra_error_in_write_config_verify_unlink(self):
|
|
# this test verifies not only that the write_config
|
|
# method properly invoked execute, but also that it properly
|
|
# attempted to unlink the file (as a result of the exception)
|
|
|
|
mock_unlink = Mock(return_value=0)
|
|
|
|
# We call tempfile.mkstemp() here and Mock() the mkstemp()
|
|
# parameter to write_config for testability.
|
|
(temp_handle, temp_config_name) = tempfile.mkstemp()
|
|
mock_mkstemp = MagicMock(return_value=(temp_handle, temp_config_name))
|
|
|
|
configuration = 'this is my configuration'
|
|
|
|
with patch('trove.guestagent.common.operating_system.move',
|
|
side_effect=ProcessExecutionError('some exception')):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.cassandra.write_config,
|
|
config_contents=configuration,
|
|
execute_function=Mock(),
|
|
mkstemp_function=mock_mkstemp,
|
|
unlink_function=mock_unlink)
|
|
|
|
self.assertEqual(1, mock_unlink.call_count)
|
|
|
|
# really delete the temporary_config_file
|
|
os.unlink(temp_config_name)
|
|
|
|
@patch.multiple('trove.guestagent.common.operating_system',
|
|
chown=DEFAULT, chmod=DEFAULT, move=DEFAULT)
|
|
def test_cassandra_write_config(self, chown, chmod, move):
|
|
# ensure that write_config creates a temporary file, and then
|
|
# moves the file to the final place. Also validate the
|
|
# contents of the file written.
|
|
|
|
# We call tempfile.mkstemp() here and Mock() the mkstemp()
|
|
# parameter to write_config for testability.
|
|
(temp_handle, temp_config_name) = tempfile.mkstemp()
|
|
mock_mkstemp = MagicMock(return_value=(temp_handle, temp_config_name))
|
|
|
|
configuration = 'some arbitrary configuration text'
|
|
|
|
mock_execute = MagicMock(return_value=('', ''))
|
|
|
|
self.cassandra.write_config(configuration,
|
|
execute_function=mock_execute,
|
|
mkstemp_function=mock_mkstemp)
|
|
|
|
move.assert_called_with(temp_config_name, cass_system.CASSANDRA_CONF,
|
|
as_root=True)
|
|
chown.assert_called_with(cass_system.CASSANDRA_CONF,
|
|
"cassandra", "cassandra", recursive=False,
|
|
as_root=True)
|
|
chmod.assert_called_with(
|
|
cass_system.CASSANDRA_CONF, FileMode.ADD_READ_ALL, as_root=True)
|
|
|
|
self.assertEqual(1, mock_mkstemp.call_count)
|
|
|
|
with open(temp_config_name, 'r') as config_file:
|
|
configuration_data = config_file.read()
|
|
|
|
self.assertEqual(configuration, configuration_data)
|
|
|
|
# really delete the temporary_config_file
|
|
os.unlink(temp_config_name)
|
|
|
|
|
|
class CouchbaseAppTest(testtools.TestCase):
|
|
|
|
def fake_couchbase_service_discovery(self, candidates):
|
|
return {
|
|
'cmd_start': 'start',
|
|
'cmd_stop': 'stop',
|
|
'cmd_enable': 'enable',
|
|
'cmd_disable': 'disable'
|
|
}
|
|
|
|
def setUp(self):
|
|
super(CouchbaseAppTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = (
|
|
couchservice.utils.execute_with_timeout)
|
|
self.orig_time_sleep = time.sleep
|
|
time.sleep = Mock()
|
|
self.orig_service_discovery = operating_system.service_discovery
|
|
self.orig_get_ip = netutils.get_my_ipv4
|
|
operating_system.service_discovery = (
|
|
self.fake_couchbase_service_discovery)
|
|
netutils.get_my_ipv4 = Mock()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.couchbaseApp = couchservice.CouchbaseApp(self.appStatus)
|
|
dbaas.CONF.guest_id = self.FAKE_ID
|
|
|
|
def tearDown(self):
|
|
super(CouchbaseAppTest, self).tearDown()
|
|
couchservice.utils.execute_with_timeout = (
|
|
self.orig_utils_execute_with_timeout)
|
|
netutils.get_my_ipv4 = self.orig_get_ip
|
|
operating_system.service_discovery = self.orig_service_discovery
|
|
time.sleep = self.orig_time_sleep
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
dbaas.CONF.guest_id = None
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def test_stop_db(self):
|
|
couchservice.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.couchbaseApp.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_stop_db_error(self):
|
|
couchservice.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchbaseApp.state_change_wait_time = 1
|
|
|
|
self.assertRaises(RuntimeError, self.couchbaseApp.stop_db)
|
|
|
|
def test_restart(self):
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchbaseApp.stop_db = Mock()
|
|
self.couchbaseApp.start_db = Mock()
|
|
|
|
self.couchbaseApp.restart()
|
|
|
|
self.assertTrue(self.couchbaseApp.stop_db.called)
|
|
self.assertTrue(self.couchbaseApp.start_db.called)
|
|
self.assertTrue(conductor_api.API.heartbeat.called)
|
|
|
|
def test_start_db(self):
|
|
couchservice.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchbaseApp._enable_db_on_boot = Mock()
|
|
|
|
self.couchbaseApp.start_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_db_error(self):
|
|
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
|
couchservice.utils.execute_with_timeout = mocked
|
|
self.couchbaseApp._enable_db_on_boot = Mock()
|
|
|
|
self.assertRaises(RuntimeError, self.couchbaseApp.start_db)
|
|
|
|
def test_start_db_runs_forever(self):
|
|
couchservice.utils.execute_with_timeout = Mock()
|
|
self.couchbaseApp._enable_db_on_boot = Mock()
|
|
self.couchbaseApp.state_change_wait_time = 1
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.assertRaises(RuntimeError, self.couchbaseApp.start_db)
|
|
self.assertTrue(conductor_api.API.heartbeat.called)
|
|
|
|
def test_install_when_couchbase_installed(self):
|
|
couchservice.packager.pkg_is_installed = Mock(return_value=True)
|
|
couchservice.utils.execute_with_timeout = Mock()
|
|
|
|
self.couchbaseApp.install_if_needed(["package"])
|
|
self.assertTrue(couchservice.packager.pkg_is_installed.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
|
|
class CouchDBAppTest(testtools.TestCase):
|
|
|
|
def fake_couchdb_service_discovery(self, candidates):
|
|
return {
|
|
'cmd_start': 'start',
|
|
'cmd_stop': 'stop',
|
|
'cmd_enable': 'enable',
|
|
'cmd_disable': 'disable'
|
|
}
|
|
|
|
def setUp(self):
|
|
super(CouchDBAppTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = (
|
|
couchdb_service.utils.execute_with_timeout)
|
|
self.orig_time_sleep = time.sleep
|
|
time.sleep = Mock()
|
|
self.orig_service_discovery = operating_system.service_discovery
|
|
self.orig_get_ip = netutils.get_my_ipv4
|
|
operating_system.service_discovery = (
|
|
self.fake_couchdb_service_discovery)
|
|
netutils.get_my_ipv4 = Mock()
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.couchdbApp = couchdb_service.CouchDBApp(self.appStatus)
|
|
dbaas.CONF.guest_id = self.FAKE_ID
|
|
|
|
def tearDown(self):
|
|
super(CouchDBAppTest, self).tearDown()
|
|
couchdb_service.utils.execute_with_timeout = (
|
|
self.orig_utils_execute_with_timeout)
|
|
netutils.get_my_ipv4 = self.orig_get_ip
|
|
operating_system.service_discovery = self.orig_service_discovery
|
|
time.sleep = self.orig_time_sleep
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
dbaas.CONF.guest_id = None
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def test_stop_db(self):
|
|
couchdb_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.couchdbApp.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_stop_db_error(self):
|
|
couchdb_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchdbApp.state_change_wait_time = 1
|
|
|
|
self.assertRaises(RuntimeError, self.couchdbApp.stop_db)
|
|
|
|
def test_restart(self):
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchdbApp.stop_db = Mock()
|
|
self.couchdbApp.start_db = Mock()
|
|
|
|
self.couchdbApp.restart()
|
|
|
|
self.assertTrue(self.couchdbApp.stop_db.called)
|
|
self.assertTrue(self.couchdbApp.start_db.called)
|
|
self.assertTrue(conductor_api.API.heartbeat.called)
|
|
|
|
def test_start_db(self):
|
|
couchdb_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.couchdbApp._enable_db_on_boot = Mock()
|
|
|
|
self.couchdbApp.start_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_db_error(self):
|
|
couchdb_service.utils.execute_with_timeout = Mock(
|
|
side_effect=ProcessExecutionError('Error'))
|
|
self.couchdbApp._enable_db_on_boot = Mock()
|
|
|
|
self.assertRaises(RuntimeError, self.couchdbApp.start_db)
|
|
|
|
def test_install_when_couchdb_installed(self):
|
|
couchdb_service.packager.pkg_is_installed = Mock(return_value=True)
|
|
couchdb_service.utils.execute_with_timeout = Mock()
|
|
|
|
self.couchdbApp.install_if_needed(["package"])
|
|
self.assertTrue(couchdb_service.packager.pkg_is_installed.called)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
|
|
class MongoDBAppTest(testtools.TestCase):
|
|
|
|
def fake_mongodb_service_discovery(self, candidates):
|
|
return {
|
|
'cmd_start': 'start',
|
|
'cmd_stop': 'stop',
|
|
'cmd_enable': 'enable',
|
|
'cmd_disable': 'disable'
|
|
}
|
|
|
|
def setUp(self):
|
|
super(MongoDBAppTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = (mongo_service.
|
|
utils.execute_with_timeout)
|
|
self.orig_time_sleep = time.sleep
|
|
self.orig_packager = mongo_system.PACKAGER
|
|
self.orig_service_discovery = operating_system.service_discovery
|
|
self.orig_os_unlink = os.unlink
|
|
|
|
operating_system.service_discovery = (
|
|
self.fake_mongodb_service_discovery)
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.mongoDbApp = mongo_service.MongoDBApp(self.appStatus)
|
|
time.sleep = Mock()
|
|
os.unlink = Mock()
|
|
|
|
def tearDown(self):
|
|
super(MongoDBAppTest, self).tearDown()
|
|
mongo_service.utils.execute_with_timeout = (
|
|
self.orig_utils_execute_with_timeout)
|
|
time.sleep = self.orig_time_sleep
|
|
mongo_system.PACKAGER = self.orig_packager
|
|
operating_system.service_discovery = self.orig_service_discovery
|
|
os.unlink = self.orig_os_unlink
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def test_stopdb(self):
|
|
mongo_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mongoDbApp.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_stop_db_with_db_update(self):
|
|
|
|
mongo_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(
|
|
rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.mongoDbApp.stop_db(True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID, {'service_status': 'shutdown'}))
|
|
|
|
def test_stop_db_error(self):
|
|
|
|
mongo_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.mongoDbApp.state_change_wait_time = 1
|
|
self.assertRaises(RuntimeError, self.mongoDbApp.stop_db)
|
|
|
|
def test_restart(self):
|
|
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
self.mongoDbApp.stop_db = Mock()
|
|
self.mongoDbApp.start_db = Mock()
|
|
|
|
self.mongoDbApp.restart()
|
|
|
|
self.assertTrue(self.mongoDbApp.stop_db.called)
|
|
self.assertTrue(self.mongoDbApp.start_db.called)
|
|
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID, {'service_status': 'shutdown'}))
|
|
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID, {'service_status': 'running'}))
|
|
|
|
def test_start_db(self):
|
|
|
|
mongo_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.mongoDbApp.start_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_start_db_with_update(self):
|
|
|
|
mongo_service.utils.execute_with_timeout = Mock()
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
|
|
self.mongoDbApp.start_db(True)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID, {'service_status': 'running'}))
|
|
|
|
def test_start_db_runs_forever(self):
|
|
|
|
mongo_service.utils.execute_with_timeout = Mock(
|
|
return_value=["ubuntu 17036 0.0 0.1 618960 "
|
|
"29232 pts/8 Sl+ Jan29 0:07 mongod", ""])
|
|
self.mongoDbApp.state_change_wait_time = 1
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
|
|
self.assertRaises(RuntimeError, self.mongoDbApp.start_db)
|
|
self.assertTrue(conductor_api.API.heartbeat.called_once_with(
|
|
self.FAKE_ID, {'service_status': 'shutdown'}))
|
|
|
|
def test_start_db_error(self):
|
|
|
|
self.mongoDbApp._enable_db_on_boot = Mock()
|
|
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
|
mongo_service.utils.execute_with_timeout = mocked
|
|
|
|
self.assertRaises(RuntimeError, self.mongoDbApp.start_db)
|
|
|
|
def test_mongodb_error_in_write_config_verify_unlink(self):
|
|
configuration = {'config_contents': 'some junk'}
|
|
|
|
with patch.object(os.path, 'isfile', return_value=True):
|
|
with patch.object(operating_system, 'move',
|
|
side_effect=ProcessExecutionError):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.mongoDbApp.reset_configuration,
|
|
configuration=configuration)
|
|
self.assertEqual(1, operating_system.move.call_count)
|
|
self.assertEqual(1, os.unlink.call_count)
|
|
|
|
def test_start_db_with_conf_changes_db_is_running(self):
|
|
|
|
self.mongoDbApp.start_db = Mock()
|
|
|
|
self.appStatus.status = rd_instance.ServiceStatuses.RUNNING
|
|
self.assertRaises(RuntimeError,
|
|
self.mongoDbApp.start_db_with_conf_changes,
|
|
Mock())
|
|
|
|
def test_install_when_db_installed(self):
|
|
packager_mock = MagicMock()
|
|
packager_mock.pkg_is_installed = MagicMock(return_value=True)
|
|
mongo_system.PACKAGER = packager_mock
|
|
self.mongoDbApp.install_if_needed(['package'])
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_install_when_db_not_installed(self):
|
|
packager_mock = MagicMock()
|
|
packager_mock.pkg_is_installed = MagicMock(return_value=False)
|
|
mongo_system.PACKAGER = packager_mock
|
|
self.mongoDbApp.install_if_needed(['package'])
|
|
packager_mock.pkg_install.assert_any_call(ANY, {}, ANY)
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
|
|
class VerticaAppStatusTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(VerticaAppStatusTest, self).setUp()
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
|
|
def tearDown(self):
|
|
|
|
super(VerticaAppStatusTest, self).tearDown()
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
|
|
def test_get_actual_db_status(self):
|
|
self.verticaAppStatus = VerticaAppStatus()
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(return_value=['db_srvr', None])):
|
|
status = self.verticaAppStatus._get_actual_db_status()
|
|
self.assertEqual(rd_instance.ServiceStatuses.RUNNING, status)
|
|
|
|
def test_get_actual_db_status_shutdown(self):
|
|
self.verticaAppStatus = VerticaAppStatus()
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[['', None],
|
|
['db_srvr', None]])):
|
|
status = self.verticaAppStatus._get_actual_db_status()
|
|
self.assertEqual(rd_instance.ServiceStatuses.SHUTDOWN, status)
|
|
|
|
def test_get_actual_db_status_error_crashed(self):
|
|
self.verticaAppStatus = VerticaAppStatus()
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=ProcessExecutionError('problem'
|
|
))):
|
|
status = self.verticaAppStatus._get_actual_db_status()
|
|
self.assertEqual(rd_instance.ServiceStatuses.CRASHED, status)
|
|
|
|
|
|
class VerticaAppTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(VerticaAppTest, self).setUp()
|
|
self.FAKE_ID = 1000
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.app = VerticaApp(self.appStatus)
|
|
self.setread = VolumeDevice.set_readahead_size
|
|
self.Popen = subprocess.Popen
|
|
vertica_system.shell_execute = MagicMock(return_value=('', ''))
|
|
|
|
VolumeDevice.set_readahead_size = Mock()
|
|
subprocess.Popen = Mock()
|
|
self.test_config = ConfigParser.ConfigParser()
|
|
self.test_config.add_section('credentials')
|
|
self.test_config.set('credentials',
|
|
'dbadmin_password', 'some_password')
|
|
|
|
def tearDown(self):
|
|
super(VerticaAppTest, self).tearDown()
|
|
self.app = None
|
|
VolumeDevice.set_readahead_size = self.setread
|
|
subprocess.Popen = self.Popen
|
|
|
|
def test_install_if_needed_installed(self):
|
|
with patch.object(pkg.Package, 'pkg_is_installed', return_value=True):
|
|
with patch.object(pkg.Package, 'pkg_install', return_value=None):
|
|
self.app.install_if_needed('vertica')
|
|
pkg.Package.pkg_is_installed.assert_any_call('vertica')
|
|
self.assertEqual(0, pkg.Package.pkg_install.call_count)
|
|
|
|
def test_install_if_needed_not_installed(self):
|
|
with patch.object(pkg.Package, 'pkg_is_installed', return_value=False):
|
|
with patch.object(pkg.Package, 'pkg_install', return_value=None):
|
|
self.app.install_if_needed('vertica')
|
|
pkg.Package.pkg_is_installed.assert_any_call('vertica')
|
|
self.assertEqual(1, pkg.Package.pkg_install.call_count)
|
|
|
|
def test_prepare_for_install_vertica(self):
|
|
self.app.prepare_for_install_vertica()
|
|
arguments = vertica_system.shell_execute.call_args_list[0]
|
|
self.assertEqual(1, VolumeDevice.set_readahead_size.call_count)
|
|
expected_command = (
|
|
"VERT_DBA_USR=dbadmin VERT_DBA_HOME=/home/dbadmin "
|
|
"VERT_DBA_GRP=verticadba /opt/vertica/oss/python/bin/python"
|
|
" -m vertica.local_coerce")
|
|
arguments.assert_called_with(expected_command)
|
|
|
|
def test_failure_prepare_for_install_vertica(self):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
side_effect=ProcessExecutionError('Error')):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.app.prepare_for_install_vertica)
|
|
|
|
def test_install_vertica(self):
|
|
with patch.object(self.app, 'write_config',
|
|
return_value=None):
|
|
self.app.install_vertica(members='10.0.0.2')
|
|
arguments = vertica_system.shell_execute.call_args_list[0]
|
|
expected_command = (
|
|
vertica_system.INSTALL_VERTICA % ('10.0.0.2', '/var/lib/vertica'))
|
|
arguments.assert_called_with(expected_command)
|
|
|
|
def test_failure_install_vertica(self):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
side_effect=ProcessExecutionError('some exception')):
|
|
self.assertRaisesRegexp(RuntimeError, 'install_vertica failed.',
|
|
self.app.install_vertica,
|
|
members='10.0.0.2')
|
|
|
|
def test_create_db(self):
|
|
with patch.object(self.app, 'read_config',
|
|
return_value=self.test_config):
|
|
self.app.create_db(members='10.0.0.2')
|
|
arguments = vertica_system.shell_execute.call_args_list[0]
|
|
expected_command = (vertica_system.CREATE_DB % ('10.0.0.2', 'db_srvr',
|
|
'/var/lib/vertica',
|
|
'/var/lib/vertica',
|
|
'some_password'))
|
|
arguments.assert_called_with(expected_command, 'dbadmin')
|
|
|
|
def test_failure_create_db(self):
|
|
with patch.object(self.app, 'read_config',
|
|
side_effect=RuntimeError('Error')):
|
|
self.assertRaisesRegexp(RuntimeError,
|
|
'Vertica database create failed.',
|
|
self.app.create_db)
|
|
# Because of an exception in read_config there was no shell execution.
|
|
self.assertEqual(0, vertica_system.shell_execute.call_count)
|
|
|
|
def test_vertica_write_config(self):
|
|
temp_file_handle = tempfile.NamedTemporaryFile(delete=False)
|
|
mock_mkstemp = MagicMock(return_value=(temp_file_handle))
|
|
mock_unlink = Mock(return_value=0)
|
|
self.app.write_config(config=self.test_config,
|
|
temp_function=mock_mkstemp,
|
|
unlink_function=mock_unlink)
|
|
|
|
arguments = vertica_system.shell_execute.call_args_list[0]
|
|
expected_command = (
|
|
("install -o root -g root -m 644 %(source)s %(target)s"
|
|
) % {'source': temp_file_handle.name,
|
|
'target': vertica_system.VERTICA_CONF})
|
|
arguments.assert_called_with(expected_command)
|
|
self.assertEqual(1, mock_mkstemp.call_count)
|
|
|
|
configuration_data = ConfigParser.ConfigParser()
|
|
configuration_data.read(temp_file_handle.name)
|
|
self.assertEqual(
|
|
self.test_config.get('credentials', 'dbadmin_password'),
|
|
configuration_data.get('credentials', 'dbadmin_password'))
|
|
self.assertEqual(1, mock_unlink.call_count)
|
|
# delete the temporary_config_file
|
|
os.unlink(temp_file_handle.name)
|
|
|
|
def test_vertica_error_in_write_config_verify_unlink(self):
|
|
mock_unlink = Mock(return_value=0)
|
|
temp_file_handle = tempfile.NamedTemporaryFile(delete=False)
|
|
mock_mkstemp = MagicMock(return_value=temp_file_handle)
|
|
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
side_effect=ProcessExecutionError('some exception')):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.app.write_config,
|
|
config=self.test_config,
|
|
temp_function=mock_mkstemp,
|
|
unlink_function=mock_unlink)
|
|
|
|
self.assertEqual(1, mock_unlink.call_count)
|
|
|
|
# delete the temporary_config_file
|
|
os.unlink(temp_file_handle.name)
|
|
|
|
def test_restart(self):
|
|
mock_status = MagicMock()
|
|
app = VerticaApp(mock_status)
|
|
mock_status.begin_restart = MagicMock(return_value=None)
|
|
with patch.object(VerticaApp, 'stop_db', return_value=None):
|
|
with patch.object(VerticaApp, 'start_db', return_value=None):
|
|
mock_status.end_install_or_restart = MagicMock(
|
|
return_value=None)
|
|
app.restart()
|
|
mock_status.begin_restart.assert_any_call()
|
|
VerticaApp.stop_db.assert_any_call()
|
|
VerticaApp.start_db.assert_any_call()
|
|
|
|
def test_start_db(self):
|
|
mock_status = MagicMock()
|
|
type(mock_status)._is_restarting = PropertyMock(return_value=False)
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_enable_db_on_boot', return_value=None):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
mock_status.end_install_or_restart = MagicMock(
|
|
return_value=None)
|
|
app.start_db()
|
|
agent_start, db_start = subprocess.Popen.call_args_list
|
|
agent_expected_command = [
|
|
'sudo', 'su', '-', 'root', '-c',
|
|
(vertica_system.VERTICA_AGENT_SERVICE_COMMAND % 'start')]
|
|
db_expected_cmd = [
|
|
'sudo', 'su', '-', 'dbadmin', '-c',
|
|
(vertica_system.START_DB % ('db_srvr', 'some_password'))]
|
|
self.assertTrue(mock_status.end_install_or_restart.called)
|
|
agent_start.assert_called_with(agent_expected_command)
|
|
db_start.assert_called_with(db_expected_cmd)
|
|
|
|
def test_start_db_failure(self):
|
|
mock_status = MagicMock()
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_enable_db_on_boot',
|
|
side_effect=RuntimeError()):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
self.assertRaises(RuntimeError, app.start_db)
|
|
|
|
def test_stop_db(self):
|
|
mock_status = MagicMock()
|
|
type(mock_status)._is_restarting = PropertyMock(return_value=False)
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_disable_db_on_boot', return_value=None):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[['', ''],
|
|
['db_srvr', None],
|
|
['', '']])):
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=True)
|
|
mock_status.end_install_or_restart = MagicMock(
|
|
return_value=None)
|
|
app.stop_db()
|
|
|
|
self.assertEqual(
|
|
3, vertica_system.shell_execute.call_count)
|
|
# There are 3 shell-executions:
|
|
# a) stop vertica-agent service
|
|
# b) check daatabase status
|
|
# c) stop_db
|
|
# We are matcing that 3rd command called was stop_db
|
|
arguments = vertica_system.shell_execute.call_args_list[2]
|
|
expected_cmd = (vertica_system.STOP_DB % ('db_srvr',
|
|
'some_password'))
|
|
self.assertTrue(
|
|
mock_status.wait_for_real_status_to_change_to.called)
|
|
arguments.assert_called_with(expected_cmd, 'dbadmin')
|
|
|
|
def test_stop_db_do_not_start_on_reboot(self):
|
|
mock_status = MagicMock()
|
|
type(mock_status)._is_restarting = PropertyMock(return_value=True)
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_disable_db_on_boot', return_value=None):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[['', ''],
|
|
['db_srvr', None],
|
|
['', '']])):
|
|
app.stop_db(do_not_start_on_reboot=True)
|
|
|
|
self.assertEqual(
|
|
3, vertica_system.shell_execute.call_count)
|
|
app._disable_db_on_boot.assert_any_call()
|
|
|
|
def test_stop_db_database_not_running(self):
|
|
mock_status = MagicMock()
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_disable_db_on_boot', return_value=None):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
app.stop_db()
|
|
# Since database stop command does not gets executed,
|
|
# so only 2 shell calls were there.
|
|
self.assertEqual(
|
|
2, vertica_system.shell_execute.call_count)
|
|
|
|
def test_stop_db_failure(self):
|
|
mock_status = MagicMock()
|
|
type(mock_status)._is_restarting = PropertyMock(return_value=False)
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, '_disable_db_on_boot', return_value=None):
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[['', ''],
|
|
['db_srvr', None],
|
|
['', '']])):
|
|
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
|
return_value=None)
|
|
mock_status.end_install_or_restart = MagicMock(
|
|
return_value=None)
|
|
self.assertRaises(RuntimeError, app.stop_db)
|
|
|
|
def test_export_conf_to_members(self):
|
|
self.app._export_conf_to_members(members=['member1', 'member2'])
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
|
|
def test_fail__export_conf_to_members(self):
|
|
app = VerticaApp(MagicMock())
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
side_effect=ProcessExecutionError('Error')):
|
|
self.assertRaises(ProcessExecutionError,
|
|
app._export_conf_to_members,
|
|
['member1', 'member2'])
|
|
|
|
def test_authorize_public_keys(self):
|
|
user = 'test_user'
|
|
keys = ['test_key@machine1', 'test_key@machine2']
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
self.app.authorize_public_keys(user=user, public_keys=keys)
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
vertica_system.shell_execute.assert_any_call(
|
|
'cat ' + '/home/' + user + '/.ssh/authorized_keys')
|
|
|
|
def test_authorize_public_keys_authorized_file_not_exists(self):
|
|
user = 'test_user'
|
|
keys = ['test_key@machine1', 'test_key@machine2']
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
with patch.object(
|
|
vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[ProcessExecutionError('Some Error'),
|
|
['', '']])):
|
|
self.app.authorize_public_keys(user=user, public_keys=keys)
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
vertica_system.shell_execute.assert_any_call(
|
|
'cat ' + '/home/' + user + '/.ssh/authorized_keys')
|
|
|
|
def test_fail_authorize_public_keys(self):
|
|
user = 'test_user'
|
|
keys = ['test_key@machine1', 'test_key@machine2']
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
with patch.object(
|
|
vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[ProcessExecutionError('Some Error'),
|
|
ProcessExecutionError('Some Error')
|
|
])):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.app.authorize_public_keys, user, keys)
|
|
|
|
def test_get_public_keys(self):
|
|
user = 'test_user'
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
self.app.get_public_keys(user=user)
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
vertica_system.shell_execute.assert_any_call(
|
|
(vertica_system.SSH_KEY_GEN % ('/home/' + user)), user)
|
|
vertica_system.shell_execute.assert_any_call(
|
|
'cat ' + '/home/' + user + '/.ssh/id_rsa.pub')
|
|
|
|
def test_get_public_keys_if_key_exists(self):
|
|
user = 'test_user'
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
with patch.object(
|
|
vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[ProcessExecutionError('Some Error'),
|
|
['some_key', None]])):
|
|
key = self.app.get_public_keys(user=user)
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
self.assertEqual('some_key', key)
|
|
|
|
def test_fail_get_public_keys(self):
|
|
user = 'test_user'
|
|
with patch.object(os.path, 'expanduser',
|
|
return_value=('/home/' + user)):
|
|
with patch.object(
|
|
vertica_system, 'shell_execute',
|
|
MagicMock(side_effect=[ProcessExecutionError('Some Error'),
|
|
ProcessExecutionError('Some Error')
|
|
])):
|
|
self.assertRaises(ProcessExecutionError,
|
|
self.app.get_public_keys, user)
|
|
|
|
def test_install_cluster(self):
|
|
with patch.object(self.app, 'read_config',
|
|
return_value=self.test_config):
|
|
self.app.install_cluster(members=['member1', 'member2'])
|
|
# Verifying the number of shell calls,
|
|
# as command has already been tested in preceding tests
|
|
self.assertEqual(5, vertica_system.shell_execute.call_count)
|
|
|
|
def test__enable_db_on_boot(self):
|
|
app = VerticaApp(MagicMock())
|
|
app._enable_db_on_boot()
|
|
|
|
restart_policy, agent_enable = subprocess.Popen.call_args_list
|
|
expected_restart_policy = [
|
|
'sudo', 'su', '-', 'dbadmin', '-c',
|
|
(vertica_system.SET_RESTART_POLICY % ('db_srvr', 'always'))]
|
|
expected_agent_enable = [
|
|
'sudo', 'su', '-', 'root', '-c',
|
|
(vertica_system.VERTICA_AGENT_SERVICE_COMMAND % 'enable')]
|
|
|
|
self.assertEqual(2, subprocess.Popen.call_count)
|
|
restart_policy.assert_called_with(expected_restart_policy)
|
|
agent_enable.assert_called_with(expected_agent_enable)
|
|
|
|
def test_failure__enable_db_on_boot(self):
|
|
with patch.object(subprocess, 'Popen', side_effect=OSError):
|
|
self.assertRaisesRegexp(RuntimeError,
|
|
'Could not enable db on boot.',
|
|
self.app._enable_db_on_boot)
|
|
|
|
def test__disable_db_on_boot(self):
|
|
app = VerticaApp(MagicMock())
|
|
app._disable_db_on_boot()
|
|
|
|
restart_policy, agent_disable = (
|
|
vertica_system.shell_execute.call_args_list)
|
|
expected_restart_policy = (
|
|
vertica_system.SET_RESTART_POLICY % ('db_srvr', 'never'))
|
|
expected_agent_disable = (
|
|
vertica_system.VERTICA_AGENT_SERVICE_COMMAND % 'disable')
|
|
|
|
self.assertEqual(2, vertica_system.shell_execute.call_count)
|
|
restart_policy.assert_called_with(expected_restart_policy, 'dbadmin')
|
|
agent_disable.assert_called_with(expected_agent_disable, 'root')
|
|
|
|
def test_failure__disable_db_on_boot(self):
|
|
with patch.object(vertica_system, 'shell_execute',
|
|
side_effect=ProcessExecutionError('Error')):
|
|
self.assertRaisesRegexp(RuntimeError,
|
|
'Could not disable db on boot.',
|
|
self.app._disable_db_on_boot)
|
|
|
|
def test_read_config(self):
|
|
app = VerticaApp(MagicMock())
|
|
with patch.object(ConfigParser, 'ConfigParser',
|
|
return_value=self.test_config):
|
|
test_config = app.read_config()
|
|
self.assertEqual('some_password',
|
|
test_config.get('credentials', 'dbadmin_password')
|
|
)
|
|
|
|
def test_fail_read_config(self):
|
|
with patch.object(ConfigParser.ConfigParser, 'read',
|
|
side_effect=ConfigParser.Error()):
|
|
self.assertRaises(RuntimeError, self.app.read_config)
|
|
|
|
def test_complete_install_or_restart(self):
|
|
app = VerticaApp(MagicMock())
|
|
app.complete_install_or_restart()
|
|
app.status.end_install_or_restart.assert_any_call()
|
|
|
|
def test_start_db_with_conf_changes(self):
|
|
mock_status = MagicMock()
|
|
type(mock_status)._is_restarting = PropertyMock(return_value=False)
|
|
app = VerticaApp(mock_status)
|
|
with patch.object(app, 'read_config',
|
|
return_value=self.test_config):
|
|
app.start_db_with_conf_changes('test_config_contents')
|
|
app.status.end_install_or_restart.assert_any_call()
|
|
|
|
|
|
class DB2AppTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(DB2AppTest, self).setUp()
|
|
self.orig_utils_execute_with_timeout = (
|
|
db2service.utils.execute_with_timeout)
|
|
util.init_db()
|
|
self.FAKE_ID = str(uuid4())
|
|
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
|
status=rd_instance.ServiceStatuses.NEW)
|
|
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
|
rd_instance.ServiceStatuses.NEW)
|
|
self.db2App = db2service.DB2App(self.appStatus)
|
|
dbaas.CONF.guest_id = self.FAKE_ID
|
|
|
|
def tearDown(self):
|
|
super(DB2AppTest, self).tearDown()
|
|
db2service.utils.execute_with_timeout = (
|
|
self.orig_utils_execute_with_timeout)
|
|
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
|
dbaas.CONF.guest_id = None
|
|
self.db2App = None
|
|
|
|
def assert_reported_status(self, expected_status):
|
|
service_status = InstanceServiceStatus.find_by(
|
|
instance_id=self.FAKE_ID)
|
|
self.assertEqual(expected_status, service_status.status)
|
|
|
|
def test_stop_db(self):
|
|
db2service.utils.execute_with_timeout = MagicMock(return_value=None)
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
|
self.db2App.stop_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
def test_restart_server(self):
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
mock_status = MagicMock(return_value=None)
|
|
app = db2service.DB2App(mock_status)
|
|
mock_status.begin_restart = MagicMock(return_value=None)
|
|
app.stop_db = MagicMock(return_value=None)
|
|
app.start_db = MagicMock(return_value=None)
|
|
app.restart()
|
|
|
|
self.assertTrue(mock_status.begin_restart.called)
|
|
self.assertTrue(app.stop_db.called)
|
|
self.assertTrue(app.start_db.called)
|
|
|
|
def test_start_db(self):
|
|
db2service.utils.execute_with_timeout = MagicMock(return_value=None)
|
|
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
|
with patch.object(self.db2App, '_enable_db_on_boot',
|
|
return_value=None):
|
|
self.db2App.start_db()
|
|
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
|
|
|
|
|
class DB2AdminTest(testtools.TestCase):
|
|
|
|
def setUp(self):
|
|
super(DB2AdminTest, self).setUp()
|
|
self.db2Admin = db2service.DB2Admin()
|
|
self.orig_utils_execute_with_timeout = (
|
|
db2service.utils.execute_with_timeout)
|
|
|
|
def tearDown(self):
|
|
super(DB2AdminTest, self).tearDown()
|
|
db2service.utils.execute_with_timeout = (
|
|
self.orig_utils_execute_with_timeout)
|
|
|
|
def test_delete_database(self):
|
|
with patch.object(
|
|
db2service, 'run_command',
|
|
MagicMock(
|
|
return_value=None,
|
|
side_effect=ProcessExecutionError('Error'))):
|
|
self.assertRaises(GuestError,
|
|
self.db2Admin.delete_database,
|
|
FAKE_DB)
|
|
self.assertTrue(db2service.run_command.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 drop database testDB"
|
|
self.assertEqual(expected, args[0],
|
|
"Delete database queries are not the same")
|
|
|
|
def test_list_databases(self):
|
|
with patch.object(db2service, 'run_command', MagicMock(
|
|
side_effect=ProcessExecutionError('Error'))):
|
|
self.db2Admin.list_databases()
|
|
self.assertTrue(db2service.run_command.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 list database directory " \
|
|
"| grep -B6 -i indirect | grep 'Database name' | " \
|
|
"sed 's/.*= //'"
|
|
self.assertEqual(expected, args[0],
|
|
"Delete database queries are not the same")
|
|
|
|
def test_create_users(self):
|
|
with patch.object(db2service, 'run_command', MagicMock(
|
|
return_value=None)):
|
|
db2service.utils.execute_with_timeout = MagicMock(
|
|
return_value=None)
|
|
self.db2Admin.create_user(FAKE_USER)
|
|
self.assertTrue(db2service.utils.execute_with_timeout.called)
|
|
self.assertTrue(db2service.run_command.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 connect to testDB; " \
|
|
"db2 GRANT DBADM,CREATETAB,BINDADD,CONNECT,DATAACCESS " \
|
|
"ON DATABASE TO USER random; db2 connect reset"
|
|
self.assertEqual(
|
|
expected, args[0],
|
|
"Granting database access queries are not the same")
|
|
self.assertEqual(1, db2service.run_command.call_count)
|
|
|
|
def test_delete_users_with_db(self):
|
|
with patch.object(db2service, 'run_command',
|
|
MagicMock(return_value=None)):
|
|
with patch.object(db2service.DB2Admin, 'list_access',
|
|
MagicMock(return_value=None)):
|
|
utils.execute_with_timeout = MagicMock(return_value=None)
|
|
self.db2Admin.delete_user(FAKE_USER[0])
|
|
self.assertTrue(db2service.run_command.called)
|
|
self.assertTrue(db2service.utils.execute_with_timeout.called)
|
|
self.assertFalse(db2service.DB2Admin.list_access.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 connect to testDB; " \
|
|
"db2 REVOKE DBADM,CREATETAB,BINDADD,CONNECT,DATAACCESS " \
|
|
"ON DATABASE FROM USER random; db2 connect reset"
|
|
self.assertEqual(
|
|
expected, args[0],
|
|
"Revoke database access queries are not the same")
|
|
self.assertEqual(1, db2service.run_command.call_count)
|
|
|
|
def test_delete_users_without_db(self):
|
|
FAKE_USER.append(
|
|
{"_name": "random2", "_password": "guesswhat", "_databases": []})
|
|
with patch.object(db2service, 'run_command',
|
|
MagicMock(return_value=None)):
|
|
with patch.object(db2service.DB2Admin, 'list_access',
|
|
MagicMock(return_value=[FAKE_DB])):
|
|
utils.execute_with_timeout = MagicMock(return_value=None)
|
|
self.db2Admin.delete_user(FAKE_USER[1])
|
|
self.assertTrue(db2service.run_command.called)
|
|
self.assertTrue(db2service.DB2Admin.list_access.called)
|
|
self.assertTrue(
|
|
db2service.utils.execute_with_timeout.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 connect to testDB; " \
|
|
"db2 REVOKE DBADM,CREATETAB,BINDADD,CONNECT," \
|
|
"DATAACCESS ON DATABASE FROM USER random2; " \
|
|
"db2 connect reset"
|
|
self.assertEqual(
|
|
expected, args[0],
|
|
"Revoke database access queries are not the same")
|
|
self.assertEqual(1, db2service.run_command.call_count)
|
|
|
|
def test_list_users(self):
|
|
databases = []
|
|
databases.append(FAKE_DB)
|
|
with patch.object(db2service, 'run_command', MagicMock(
|
|
side_effect=ProcessExecutionError('Error'))):
|
|
with patch.object(self.db2Admin, "list_databases",
|
|
MagicMock(return_value=(databases, None))):
|
|
self.db2Admin.list_users()
|
|
self.assertTrue(db2service.run_command.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 +o connect to testDB; " \
|
|
"db2 -x select grantee, dataaccessauth " \
|
|
"from sysibm.sysdbauth; db2 connect reset"
|
|
self.assertEqual(expected, args[0],
|
|
"List database queries are not the same")
|
|
|
|
def test_get_user(self):
|
|
databases = []
|
|
databases.append(FAKE_DB)
|
|
with patch.object(db2service, 'run_command', MagicMock(
|
|
side_effect=ProcessExecutionError('Error'))):
|
|
with patch.object(self.db2Admin, "list_databases",
|
|
MagicMock(return_value=(databases, None))):
|
|
self.db2Admin._get_user('random', None)
|
|
self.assertTrue(db2service.run_command.called)
|
|
args, _ = db2service.run_command.call_args_list[0]
|
|
expected = "db2 +o connect to testDB; " \
|
|
"db2 -x select grantee, dataaccessauth " \
|
|
"from sysibm.sysdbauth; db2 connect reset"
|
|
self.assertEqual(args[0], expected,
|
|
"Delete database queries are not the same")
|