Implement configuration management for DB2

In this feature we implement configuration management for DB2 database
manager through Trove.

Change-Id: I303b8ddeafe7c67ec80dc8ec9e8489af913e23f7
Implements: blueprint db2-config-group
This commit is contained in:
Mariam John 2016-08-29 00:49:34 -07:00
parent 4d1bea7429
commit 0609f85d74
12 changed files with 797 additions and 27 deletions

View File

@ -0,0 +1,4 @@
---
features:
- Add support for configuration group management
for DB2 Express-C.

View File

@ -84,3 +84,14 @@ class VerticaConfParser(object):
def parse(self):
return self.CODEC.deserialize(self.config).items()
class DB2ConfParser(object):
CODEC = stream_codecs.PropertiesCodec(delimiter='=')
def __init__(self, config):
self.config = config
def parse(self):
return self.CODEC.deserialize(self.config).items()

View File

@ -39,6 +39,7 @@ SERVICE_PARSERS = {
'cassandra': configurations.CassandraConfParser,
'redis': configurations.RedisConfParser,
'vertica': configurations.VerticaConfParser,
'db2': configurations.DB2ConfParser,
}

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_log import log as logging
from trove.common.i18n import _
@ -42,6 +44,10 @@ class Manager(manager.Manager):
def status(self):
return self.appStatus
@property
def configuration_manager(self):
return self.app.configuration_manager
def do_prepare(self, context, packages, databases, memory_mb, users,
device_path, mount_point, backup_info,
config_contents, root_password, overrides,
@ -51,13 +57,18 @@ class Manager(manager.Manager):
device = volume.VolumeDevice(device_path)
device.unmount_device(device_path)
device.format()
device.mount(mount_point)
LOG.debug('Mounted the volume.')
if os.path.exists(mount_point):
device.migrate_data(mount_point)
device.mount(mount_point)
LOG.debug("Mounted the volume.")
self.app.update_hostname()
self.app.change_ownership(mount_point)
self.app.start_db()
if backup_info:
self._perform_restore(backup_info, context, mount_point)
if config_contents:
self.app.configuration_manager.save_configuration(
config_contents)
def restart(self, context):
"""
@ -133,3 +144,15 @@ class Manager(manager.Manager):
def create_backup(self, context, backup_info):
LOG.debug("Creating backup.")
backup.backup(context, backup_info)
def update_overrides(self, context, overrides, remove=False):
LOG.debug("Updating overrides.")
if remove:
self.app.remove_overrides()
else:
self.app.update_overrides(context, overrides)
def apply_overrides(self, context, overrides):
if overrides:
LOG.debug("Applying overrides: " + str(overrides))
self.app.apply_overrides(overrides)

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_log import log as logging
from oslo_utils import encodeutils
@ -20,7 +22,11 @@ from trove.common import cfg
from trove.common import exception
from trove.common.i18n import _
from trove.common import instance as rd_instance
from trove.common.stream_codecs import PropertiesCodec
from trove.common import utils as utils
from trove.guestagent.common.configuration import ConfigurationManager
from trove.guestagent.common.configuration import ImportOverrideStrategy
from trove.guestagent.common import guestagent_utils
from trove.guestagent.common import operating_system
from trove.guestagent.datastore.experimental.db2 import system
from trove.guestagent.datastore import service
@ -28,6 +34,9 @@ from trove.guestagent.db import models
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
MOUNT_POINT = CONF.db2.mount_point
FAKE_CFG = os.path.join(MOUNT_POINT, "db2.cfg.fake")
DB2_DEFAULT_CFG = os.path.join(MOUNT_POINT, "db2_default_dbm.cfg")
class DB2App(object):
@ -43,6 +52,87 @@ class DB2App(object):
)
LOG.debug("state_change_wait_time = %s." % self.state_change_wait_time)
self.status = status
self.dbm_default_config = {}
self.init_config()
def init_config(self):
if not operating_system.exists(MOUNT_POINT, True):
operating_system.create_directory(MOUNT_POINT,
system.DB2_INSTANCE_OWNER,
system.DB2_INSTANCE_OWNER,
as_root=True)
"""
The database manager configuration file - db2systm is stored under the
/home/db2inst1/sqllib directory. To update the configuration
parameters, DB2 recommends using the command - UPDATE DBM CONFIGURATION
commands instead of directly updating the config file.
The existing PropertiesCodec implementation has been reused to handle
text-file operations. Configuration overrides are implemented using
the ImportOverrideStrategy of the guestagent configuration manager.
"""
LOG.debug("Initialize DB2 configuration")
revision_dir = (
guestagent_utils.build_file_path(
os.path.join(MOUNT_POINT,
os.path.dirname(system.DB2_INSTANCE_OWNER)),
ConfigurationManager.DEFAULT_STRATEGY_OVERRIDES_SUB_DIR)
)
if not operating_system.exists(FAKE_CFG):
operating_system.write_file(FAKE_CFG, '', as_root=True)
operating_system.chown(FAKE_CFG, system.DB2_INSTANCE_OWNER,
system.DB2_INSTANCE_OWNER, as_root=True)
self.configuration_manager = (
ConfigurationManager(FAKE_CFG, system.DB2_INSTANCE_OWNER,
system.DB2_INSTANCE_OWNER,
PropertiesCodec(delimiter='='),
requires_root=True,
override_strategy=ImportOverrideStrategy(
revision_dir, "cnf"))
)
'''
Below we are getting the database manager default configuration and
saving it to the DB2_DEFAULT_CFG file. This is done to help with
correctly resetting the configurations to the original values when
user wants to detach a user-defined configuration group from an
instance. DB2 provides a command to reset the database manager
configuration parameters (RESET DBM CONFIGURATION) but this command
resets all the configuration parameters to the system defaults. When
we build a DB2 guest image there are certain configurations
parameters like SVCENAME which we set so that the instance can start
correctly. Hence resetting this value to the system default will
render the instance in an unstable state. Instead, the recommended
way for resetting a subset of configuration parameters is to save
the output of GET DBM CONFIGURATION of the original configuration
and then call UPDATE DBM CONFIGURATION to reset the value.
http://www.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/
com.ibm.db2.luw.admin.cmd.doc/doc/r0001970.html
'''
if not operating_system.exists(DB2_DEFAULT_CFG):
run_command(system.GET_DBM_CONFIGURATION % {
"dbm_config": DB2_DEFAULT_CFG})
self.process_default_dbm_config()
def process_default_dbm_config(self):
"""
Once the default database manager configuration is saved to
DB2_DEFAULT_CFG, we try to store the configuration parameters
and values into a dictionary object, dbm_default_config. For
example, a sample content of the database manager configuration
file looks like this:
Buffer pool (DFT_MON_BUFPOOL) = OFF
We need to process this so that we key it on the configuration
parameter DFT_MON_BUFPOOL.
"""
with open(DB2_DEFAULT_CFG) as cfg_file:
for line in cfg_file:
if '=' in line:
item = line.rstrip('\n').split(' = ')
fIndex = item[0].rfind('(')
lIndex = item[0].rfind(')')
if fIndex > -1:
param = item[0][fIndex + 1: lIndex]
self.dbm_default_config.update({param: item[1]})
def update_hostname(self):
"""
@ -92,14 +182,8 @@ class DB2App(object):
"Command to disable DB2 server on boot failed."))
def start_db_with_conf_changes(self, config_contents):
'''
Will not be implementing configuration change API for DB2 in
the Kilo release. Currently all that this method does is to start
the DB2 server without any configuration changes. Looks like
this needs to be implemented to enable volume resize on the guest
agent side.
'''
LOG.info(_("Starting DB2 with configuration changes."))
self.configuration_manager.save_configuration(config_contents)
self.start_db(True)
def start_db(self, update_db=False):
@ -142,6 +226,50 @@ class DB2App(object):
finally:
self.status.end_restart()
def update_overrides(self, context, overrides, remove=False):
if overrides:
self.apply_overrides(overrides)
def remove_overrides(self):
config = self.configuration_manager.get_user_override()
self._reset_config(config)
self.configuration_manager.remove_user_override()
def apply_overrides(self, overrides):
self._apply_config(overrides)
self.configuration_manager.apply_user_override(overrides)
def _update_dbm_config(self, param, value):
out, err = run_command(
system.UPDATE_DBM_CONFIGURATION % {
"parameter": param,
"value": value})
if err:
if err.is_warning():
LOG.warning(err)
else:
LOG.error(err)
raise RuntimeError(_("Failed to update config %s") % param)
def _reset_config(self, config):
try:
for k, v in config.iteritems():
default_cfg_value = self.dbm_default_config[k]
self._update_dbm_config(k, default_cfg_value)
except Exception:
LOG.exception(_("DB2 configuration reset failed."))
raise RuntimeError(_("DB2 configuration reset failed."))
LOG.info(_("DB2 configuration reset completed."))
def _apply_config(self, config):
try:
for k, v in config.items():
self._update_dbm_config(k, v)
except Exception:
LOG.exception(_("DB2 configuration apply failed"))
raise RuntimeError(_("DB2 configuration apply failed"))
LOG.info(_("DB2 config apply completed."))
class DB2AppStatus(service.BaseDbStatus):
"""

View File

@ -58,3 +58,6 @@ GET_DB_SIZE = (
"db2 call get_dbsize_info(?, ?, ?, -1) ")
GET_DB_NAMES = ("find /home/db2inst1/db2inst1/backup/ -type f -name '*.001' |"
" grep -Po \"(?<=backup/)[^.']*(?=\.)\"")
GET_DBM_CONFIGURATION = "db2 get dbm configuration > %(dbm_config)s"
UPDATE_DBM_CONFIGURATION = ("db2 update database manager configuration using "
"%(parameter)s %(value)s")

View File

@ -0,0 +1,454 @@
{
"configuration-parameters": [
{
"description": "Agent stack size",
"type": "integer",
"name": "AGENT_STACK_SZ",
"restart_required": true
},
{
"description": "Alternate encryption algorithm for incoming connections at server",
"type": "integer",
"name": "ALTERNATE_AUTH_ENC",
"restart_required": true
},
{
"description": "Max number of concurrently active databases",
"type": "integer",
"name": "NUMDB",
"restart_required": true
},
{
"description": "Federated Database Support",
"type": "integer",
"name": "FEDERATED",
"restart_required": false
},
{
"description": "Transaction processor monitor name",
"type": "string",
"name": "TP_MON_NAME",
"restart_required": true
},
{
"description": "Default charge-back account",
"type": "string",
"name": "DFT_ACCOUNT_STR",
"restart_required": false
},
{
"description": "Size of rotating db2diag & notify logs (MB)",
"type": "integer",
"name": "DIAGSIZE",
"restart_required": true
},
{
"description": "Default database monitor switch for Buffer pool",
"type": "integer",
"name": "DFT_MON_BUFPOOL",
"restart_required": false
},
{
"description": "Default database monitor switch for Lock",
"type": "integer",
"name": "DFT_MON_LOCK",
"restart_required": false
},
{
"description": "Default database monitor switch for Sort",
"type": "integer",
"name": "DFT_MON_SORT",
"restart_required": false
},
{
"description": "Default database monitor switch for Statement",
"type": "integer",
"name": "DFT_MON_STMT",
"restart_required": false
},
{
"description": "Default database monitor switch for Table",
"type": "integer",
"name": "DFT_MON_TABLE",
"restart_required": false
},
{
"description": "Default database monitor switch for Timestamp",
"type": "integer",
"name": "DFT_MON_TIMESTAMP",
"restart_required": false
},
{
"description": "Default database monitor switch for Unit of work",
"type": "integer",
"name": "DFT_MON_UOW",
"restart_required": false
},
{
"description": "Monitor health of instance and databases",
"type": "integer",
"name": "HEALTH_MON",
"restart_required": false
},
{
"description": "SYSADM group name",
"type": "string",
"name": "SYSADM_GROUP",
"restart_required": true
},
{
"description": "SYSCTRL group name",
"type": "string",
"name": "SYSCTRL_GROUP",
"restart_required": true
},
{
"description": "SYSMAINT group name",
"type": "string",
"name": "SYSMAINT_GROUP",
"restart_required": true
},
{
"description": "SYSMON group name",
"type": "string",
"name": "SYSMAINT_GROUP",
"restart_required": true
},
{
"description": "Client Userid-Password Plugin",
"type": "string",
"name": "CLNT_PW_PLUGIN",
"restart_required": true
},
{
"description": "Client Kerberos Plugin",
"type": "string",
"name": "CLNT_KRB_PLUGIN",
"restart_required": true
},
{
"description": "Group Plugin",
"type": "string",
"name": "GROUP_PLUGIN",
"restart_required": true
},
{
"description": "GSS Plugin for Local Authorization",
"type": "string",
"name": "LOCAL_GSSPLUGIN",
"restart_required": true
},
{
"description": "Server Plugin Mode",
"type": "integer",
"name": "SRV_PLUGIN_MODE",
"restart_required": true
},
{
"description": "Server List of GSS Plugins",
"type": "string",
"name": "SRVCON_GSSPLUGIN_LIST",
"restart_required": true
},
{
"description": "Server Userid-Password Plugin",
"type": "string",
"name": "SRVCON_PW_PLUGIN",
"restart_required": true
},
{
"description": "Server Connection Authentication",
"type": "string",
"name": "SRVCON_AUTH",
"restart_required": true
},
{
"description": "Database manager authentication",
"type": "integer",
"name": "AUTHENTICATION",
"restart_required": true
},
{
"description": "Alternate authentication",
"type": "integer",
"name": "ALTERNATE_AUTH_ENC",
"restart_required": true
},
{
"description": "Cataloging allowed without authority",
"type": "integer",
"name": "CATALOG_NOAUTH",
"restart_required": false
},
{
"description": "Trust all clients",
"type": "integer",
"name": "TRUST_ALLCLNTS",
"restart_required": true
},
{
"description": "Trusted client authentication",
"type": "integer",
"name": "TRUST_CLNTAUTH",
"restart_required": true
},
{
"description": "Bypass federated authentication",
"type": "integer",
"name": "FED_NOAUTH",
"restart_required": false
},
{
"description": "Database monitor heap size(4KB)",
"type": "integer",
"name": "MON_HEAP_SZ",
"restart_required": false
},
{
"description": "Java Virtual Machine heap size(4KB)",
"type": "integer",
"name": "JAVA_HEAP_SZ",
"restart_required": true
},
{
"description": "Audit buffer size(4KB)",
"type": "integer",
"name": "AUDIT_BUF_SZ",
"restart_required": true
},
{
"description": "Agent stack size",
"type": "integer",
"name": "AGENT_STACK_SZ",
"restart_required": true
},
{
"description": "Sort heap threshold (4KB)",
"type": "integer",
"name": "SHEAPTHRES",
"restart_required": true
},
{
"description": "Directory cache support",
"type": "integer",
"name": "DIR_CACHE",
"restart_required": true
},
{
"description": "Application support layer heap size (4KB)",
"type": "integer",
"name": "ASLHEAPSZ",
"restart_required": true
},
{
"description": "Max requester I/O block size (bytes)",
"type": "integer",
"name": "RQRIOBLK",
"restart_required": true
},
{
"description": "Workload impact by throttled utilities",
"type": "integer",
"name": "UTIL_IMPACT_LIM",
"restart_required": true
},
{
"description": "Priority of agents",
"type": "integer",
"name": "AGENTPRI",
"restart_required": true
},
{
"description": "Agent pool size",
"type": "integer",
"name": "NUM_POOLAGENTS",
"restart_required": false
},
{
"description": "Initial number of agents in pool",
"type": "integer",
"name": "NUM_INITAGENTS",
"restart_required": true
},
{
"description": "Max number of coordinating agents",
"type": "integer",
"name": "MAX_COORDAGENTS",
"restart_required": false
},
{
"description": "Max number of client connections",
"type": "integer",
"name": "MAX_CONNECTIONS",
"restart_required": false
},
{
"description": "Keep fenced process",
"type": "integer",
"name": "KEEPFENCED",
"restart_required": true
},
{
"description": "Number of pooled fenced processes",
"type": "integer",
"name": "FENCED_POOL",
"restart_required": false
},
{
"description": "Initial number of fenced processes",
"type": "integer",
"name": "NUM_INITFENCED",
"restart_required": true
},
{
"description": "Index re-creation time and redo index build",
"type": "integer",
"name": "INDEXREC",
"restart_required": false
},
{
"description": "Transaction manager database name",
"type": "string",
"name": "TM_DATABASE",
"restart_required": true
},
{
"description": "Transaction resync interval (sec)",
"type": "integer",
"name": "RESYNC_INTERVAL",
"restart_required": true
},
{
"description": "SPM name",
"type": "string",
"name": "SPM_NAME",
"restart_required": true
},
{
"description": "SPM log size",
"type": "integer",
"name": "SPM_LOG_FILE_SZ",
"restart_required": true
},
{
"description": "SPM resync agent limit",
"type": "integer",
"name": "SPM_MAX_RESYNC",
"restart_required": true
},
{
"description": "Discovery mode",
"type": "integer",
"name": "DISCOVER",
"restart_required": true
},
{
"description": "Discover server instance",
"type": "integer",
"name": "DISCOVER_INST",
"restart_required": false
},
{
"description": "SSL server keydb file",
"type": "string",
"name": "SSL_SVR_KEYDB",
"restart_required": true
},
{
"description": "SSL server stash file",
"type": "string",
"name": "SSL_SVR_STASH",
"restart_required": true
},
{
"description": "SSL server certificate label",
"type": "string",
"name": "SSL_SVR_LABEL",
"restart_required": true
},
{
"description": "SSL cipher specs",
"type": "string",
"name": "SSL_CIPHERSPECS",
"restart_required": true
},
{
"description": "SSL versions",
"type": "string",
"name": "SSL_VERSIONS",
"restart_required": true
},
{
"description": "SSL client keydb file",
"type": "string",
"name": "SSL_CLNT_KEYDB",
"restart_required": true
},
{
"description": "SSL client stash file",
"type": "string",
"name": "SSL_CLNT_STASH",
"restart_required": true
},
{
"description": "Maximum query degree of parallelism",
"type": "integer",
"name": "MAX_QUERYDEGREE",
"restart_required": false
},
{
"description": "Enable intra-partition parallelism",
"type": "integer",
"name": "INTRA_PARALLEL",
"restart_required": true
},
{
"description": "No. of int. communication buffers(4KB)",
"type": "integer",
"name": "FCM_NUM_BUFFERS",
"restart_required": false
},
{
"description": "No. of int. communication channels",
"type": "integer",
"name": "FCM_NUM_CHANNELS",
"restart_required": false
},
{
"description": "db2start/db2stop timeout (min)",
"type": "integer",
"name": "START_STOP_TIME",
"restart_required": false
},
{
"description": "WLM dispatcher enabled",
"type": "integer",
"name": "WLM_DISPATCHER",
"restart_required": false
},
{
"description": "WLM dispatcher concurrency",
"type": "integer",
"name": "WLM_DISP_CONCUR",
"restart_required": false
},
{
"description": "WLM dispatcher CPU shares enabled",
"type": "integer",
"name": "WLM_DISP_CPU_SHARES",
"restart_required": false
},
{
"description": "WLM dispatcher min. utilization (%)",
"type": "integer",
"name": "WLM_DISP_MIN_UTIL",
"restart_required": false
},
{
"description": "Communication buffer exit library list",
"type": "string",
"name": "COMM_EXIT_LIST",
"restart_required": false
}
]
}

View File

@ -210,7 +210,7 @@ register(["user"], user_actions_groups)
# Register: Datastore based groups
# These should contain all functionality currently supported by the datastore
register(["db2_supported"], common_groups,
database_actions_groups, user_actions_groups)
database_actions_groups, user_actions_groups, configuration_groups)
register(["cassandra_supported"], common_groups,
user_actions_groups, database_actions_groups,
backup_groups, configuration_groups, cluster_actions_groups)

View File

@ -0,0 +1,44 @@
# Copyright 2016 IBM Corporation
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from trove.tests.scenario.helpers.test_helper import TestHelper
class Db2Helper(TestHelper):
def __init__(self, expected_override_name, report):
super(Db2Helper, self).__init__(expected_override_name, report)
def get_helper_credentials(self):
return {'name': 'lite', 'password': 'litepass', 'database': 'lite'}
def get_valid_user_definitions(self):
return [{'name': 'user1', 'password': 'password1', 'databases': []},
{'name': 'user2', 'password': 'password1',
'databases': [{'name': 'db1'}]},
{'name': 'user3', 'password': 'password1',
'databases': [{'name': 'db1'}, {'name': 'db2'}]}]
def get_dynamic_group(self):
return {'MON_HEAP_SZ': 40}
def get_non_dynamic_group(self):
return {'NUMDB': 30}
def get_invalid_groups(self):
return [{'timezone': 997},
{"max_worker_processes": 'string_value'},
{"standard_conforming_strings": 'string_value'}]

View File

@ -24,6 +24,8 @@ from trove.guestagent.common import operating_system
from trove.guestagent.datastore.experimental.cassandra import (
service as cass_service
)
from trove.guestagent.datastore.experimental.db2 import (
service as db2_service)
from trove.guestagent.strategies.backup import base as backupBase
from trove.guestagent.strategies.backup.experimental.postgresql_impl \
import PgBaseBackupUtil
@ -443,7 +445,12 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
DECRYPT + PIPE + UNZIP + PIPE + REDISBACKUP_RESTORE)
@patch.object(utils, 'execute_with_timeout')
def test_backup_encrypted_db2backup_command(self, *mock):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def test_backup_encrypted_db2backup_command(self, *mock, **kwargs):
backupBase.BackupRunner.is_encrypted = True
backupBase.BackupRunner.encrypt_key = CRYPTO_KEY
RunnerClass = utils.import_class(BACKUP_DB2_CLS)
@ -454,7 +461,12 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
self.assertIn("gz.enc", bkp.manifest)
@patch.object(utils, 'execute_with_timeout')
def test_backup_not_encrypted_db2backup_command(self, *mock):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def test_backup_not_encrypted_db2backup_command(self, *mock, **kwargs):
backupBase.BackupRunner.is_encrypted = False
backupBase.BackupRunner.encrypt_key = CRYPTO_KEY
RunnerClass = utils.import_class(BACKUP_DB2_CLS)
@ -463,7 +475,12 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
self.assertEqual(DB2BACKUP_CMD + PIPE + ZIP, bkp.command)
self.assertIn("gz", bkp.manifest)
def test_restore_decrypted_db2backup_command(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def test_restore_decrypted_db2backup_command(self, *args, **kwargs):
restoreBase.RestoreRunner.is_zipped = True
restoreBase.RestoreRunner.is_encrypted = False
RunnerClass = utils.import_class(RESTORE_DB2_CLS)
@ -471,7 +488,12 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
location="filename", checksum="md5")
self.assertEqual(restr.restore_cmd, UNZIP + PIPE + DB2BACKUP_RESTORE)
def test_restore_encrypted_db2backup_command(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def test_restore_encrypted_db2backup_command(self, *args, **kwargs):
restoreBase.RestoreRunner.is_zipped = True
restoreBase.RestoreRunner.is_encrypted = True
restoreBase.RestoreRunner.encrypt_key = CRYPTO_KEY
@ -946,7 +968,12 @@ class PostgresqlBackupTests(trove_testtools.TestCase):
class DB2BackupTests(trove_testtools.TestCase):
def setUp(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def setUp(self, *args, **kwargs):
super(DB2BackupTests, self).setUp()
self.exec_timeout_patch = patch.object(utils, 'execute_with_timeout')
self.exec_timeout_patch.start()
@ -983,7 +1010,12 @@ class DB2BackupTests(trove_testtools.TestCase):
class DB2RestoreTests(trove_testtools.TestCase):
def setUp(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service, 'run_command')
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def setUp(self, *args, **kwargs):
super(DB2RestoreTests, self).setUp()
self.restore_runner = utils.import_class(

View File

@ -12,12 +12,16 @@
# License for the specific language governing permissions and limitations
# under the License.
from mock import DEFAULT
from mock import MagicMock
from mock import patch
from testtools.matchers import Is, Equals, Not
from trove.common.instance import ServiceStatuses
from trove.guestagent import backup
from trove.guestagent.common import configuration
from trove.guestagent.common.configuration import ImportOverrideStrategy
from trove.guestagent.common import operating_system
from trove.guestagent.datastore.experimental.db2 import (
manager as db2_manager)
from trove.guestagent.datastore.experimental.db2 import (
@ -30,7 +34,11 @@ from trove.tests.unittests.guestagent.test_datastore_manager import \
class GuestAgentDB2ManagerTest(DatastoreManagerTest):
def setUp(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2_service.DB2App, 'process_default_dbm_config')
def setUp(self, *arg, **kwargs):
super(GuestAgentDB2ManagerTest, self).setUp('db2')
self.real_status = db2_service.DB2AppStatus.set_status
@ -58,6 +66,9 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
self.orig_delete_user = db2_service.DB2Admin.delete_user
self.orig_update_hostname = db2_service.DB2App.update_hostname
self.orig_backup_restore = backup.restore
self.orig_init_config = db2_service.DB2App.init_config
self.orig_update_overrides = db2_service.DB2App.update_overrides
self.orig_remove_overrides = db2_service.DB2App.remove_overrides
def tearDown(self):
super(GuestAgentDB2ManagerTest, self).tearDown()
@ -78,6 +89,9 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
db2_service.DB2Admin.delete_user = self.orig_delete_user
db2_service.DB2App.update_hostname = self.orig_update_hostname
backup.restore = self.orig_backup_restore
db2_service.DB2App.init_config = self.orig_init_config
db2_service.DB2App.update_overrides = self.orig_update_overrides
db2_service.DB2App.remove_overrides = self.orig_remove_overrides
def test_update_status(self):
mock_status = MagicMock()
@ -97,8 +111,9 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
def test_prepare_from_backup(self):
self._prepare_dynamic(['db2'], backup_id='123backup')
@patch.object(configuration.ConfigurationManager, 'save_configuration')
def _prepare_dynamic(self, packages=None, databases=None, users=None,
config_content=None, device_path='/dev/vdb',
config_content='MockContent', device_path='/dev/vdb',
is_db_installed=True, backup_id=None, overrides=None):
backup_info = {'id': backup_id,
@ -152,6 +167,9 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
backup.restore.assert_any_call(self.context,
backup_info,
'/home/db2inst1/db2inst1')
self.assertTrue(
self.manager.configuration_manager.save_configuration.called
)
def test_restart(self):
mock_status = MagicMock()
@ -171,6 +189,12 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
db2_service.DB2App.stop_db.assert_any_call(
do_not_start_on_reboot=False)
def test_start_db_with_conf_changes(self):
with patch.object(db2_service.DB2App, 'start_db_with_conf_changes'):
self.manager.start_db_with_conf_changes(self.context, 'something')
db2_service.DB2App.start_db_with_conf_changes.assert_any_call(
'something')
def test_create_database(self):
mock_status = MagicMock()
self.manager.appStatus = mock_status
@ -228,13 +252,19 @@ class GuestAgentDB2ManagerTest(DatastoreManagerTest):
self.assertThat(users, Equals(get_user_mock.return_value))
get_user_mock.assert_any_call(username, hostname)
def test_reset_configuration(self):
try:
configuration = {'config_contents': 'some junk'}
self.manager.reset_configuration(self.context, configuration)
except Exception:
self.fail("reset_configuration raised exception unexpectedly.")
def test_rpc_ping(self):
output = self.manager.rpc_ping(self.context)
self.assertTrue(output)
def test_update_update_overrides(self):
configuration = {"DIAGSIZE": 50}
db2_service.DB2App.update_overrides = MagicMock()
self.manager.update_overrides(self.context, configuration, False)
db2_service.DB2App.update_overrides.assert_any_call(self.context,
configuration)
def test_reset_update_overrides(self):
configuration = {}
db2_service.DB2App.remove_overrides = MagicMock()
self.manager.update_overrides(self.context, configuration, True)
db2_service.DB2App.remove_overrides.assert_any_call()

View File

@ -3247,7 +3247,12 @@ class VerticaAppTest(trove_testtools.TestCase):
class DB2AppTest(trove_testtools.TestCase):
def setUp(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2service, 'run_command')
@patch.object(db2service.DB2App, 'process_default_dbm_config')
def setUp(self, *args, **kwargs):
super(DB2AppTest, self).setUp()
self.orig_utils_execute_with_timeout = (
db2service.utils.execute_with_timeout)
@ -3258,6 +3263,7 @@ class DB2AppTest(trove_testtools.TestCase):
self.appStatus = FakeAppStatus(self.FAKE_ID,
rd_instance.ServiceStatuses.NEW)
self.db2App = db2service.DB2App(self.appStatus)
self.db2App.init_config()
dbaas.CONF.guest_id = self.FAKE_ID
def tearDown(self):
@ -3279,7 +3285,12 @@ class DB2AppTest(trove_testtools.TestCase):
self.db2App.stop_db()
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
def test_restart_server(self):
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
@patch.multiple(operating_system, exists=DEFAULT, write_file=DEFAULT,
chown=DEFAULT, chmod=DEFAULT)
@patch.object(db2service, 'run_command')
@patch.object(db2service.DB2App, 'process_default_dbm_config')
def test_restart_server(self, *args, **kwargs):
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
mock_status = MagicMock(return_value=None)
app = db2service.DB2App(mock_status)
@ -3302,6 +3313,35 @@ class DB2AppTest(trove_testtools.TestCase):
self.db2App.start_db()
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
@patch.object(ConfigurationManager, 'save_configuration')
@patch.object(db2service.DB2App, 'start_db')
def test_start_db_with_conf_changes(self, start_db, save_cfg):
config = {'DIAGSIZE': '10'}
self.db2App.start_db_with_conf_changes(config)
start_db.assert_called_once_with(True)
save_cfg.assert_called_once_with(config)
@patch.object(ConfigurationManager, 'apply_user_override')
@patch.object(db2service.DB2App, '_apply_config')
def test_update_overrides(self, apply_user_override, apply_config):
overrides = {'DIAGSIZE': 50}
context = MagicMock()
self.db2App.update_overrides(context, overrides)
apply_user_override.assert_called_once_with(overrides)
apply_config.assert_called_once_with(overrides)
@patch.object(ConfigurationManager, 'get_user_override')
@patch.object(ConfigurationManager, 'remove_user_override')
@patch.object(db2service.DB2App, '_reset_config')
def test_remove_overrides(self, reset_config, remove_user_override,
get_user_override):
overrides = {'DIAGSIZE': 50}
get_user_override.return_value = overrides
self.db2App.remove_overrides()
get_user_override.assert_called_once()
reset_config.assert_called_once_with(overrides)
remove_user_override.assert_called_once()
class DB2AdminTest(trove_testtools.TestCase):