Merge "Add support for DB2 datastore in Trove"
This commit is contained in:
commit
7c300679d5
@ -317,7 +317,8 @@ common_opts = [
|
||||
'mongodb': 'c8c907af-7375-456f-b929-b637ff9209ee',
|
||||
'postgresql': 'ac277e0d-4f21-40aa-b347-1ea31e571720',
|
||||
'couchdb': 'f0a9ab7b-66f7-4352-93d7-071521d44c7c',
|
||||
'vertica': 'a8d805ae-a3b2-c4fd-gb23-b62cee5201ae'},
|
||||
'vertica': 'a8d805ae-a3b2-c4fd-gb23-b62cee5201ae',
|
||||
'db2': 'e040cd37-263d-4869-aaa6-c62aa97523b5'},
|
||||
help='Unique ID to tag notification events.'),
|
||||
cfg.StrOpt('nova_proxy_admin_user', default='',
|
||||
help="Admin username used to connect to Nova.", secret=True),
|
||||
@ -873,6 +874,47 @@ vertica_opts = [
|
||||
'logic.'),
|
||||
]
|
||||
|
||||
# DB2
|
||||
db2_group = cfg.OptGroup(
|
||||
'db2', title='DB2 options',
|
||||
help="Oslo option group designed for DB2 datastore")
|
||||
db2_opts = [
|
||||
cfg.ListOpt('tcp_ports',
|
||||
default=["50000"],
|
||||
help='List of TCP ports and/or port ranges to open '
|
||||
'in the security group (only applicable '
|
||||
'if trove_security_groups_support is True).'),
|
||||
cfg.ListOpt('udp_ports', default=[],
|
||||
help='List of UDP ports and/or port ranges to open '
|
||||
'in the security group (only applicable '
|
||||
'if trove_security_groups_support is True).'),
|
||||
cfg.StrOpt('mount_point', default="/home/db2inst1/db2inst1",
|
||||
help="Filesystem path for mounting "
|
||||
"volumes if volume support is enabled."),
|
||||
cfg.BoolOpt('volume_support', default=True,
|
||||
help='Whether to provision a Cinder volume for datadir.'),
|
||||
cfg.StrOpt('device_path', default='/dev/vdb',
|
||||
help='Device path for volume if volume support is enabled.'),
|
||||
cfg.StrOpt('backup_strategy', default=None,
|
||||
help='Default strategy to perform backups.'),
|
||||
cfg.StrOpt('replication_strategy', default=None,
|
||||
help='Default strategy for replication.'),
|
||||
cfg.BoolOpt('root_on_create', default=False,
|
||||
help='Enable the automatic creation of the root user for the '
|
||||
'service during instance-create. The generated password for '
|
||||
'the root user is immediately returned in the response of '
|
||||
"instance-create as the 'password' field."),
|
||||
cfg.StrOpt('backup_namespace', default=None,
|
||||
help='Namespace to load backup strategies from.'),
|
||||
cfg.StrOpt('restore_namespace', default=None,
|
||||
help='Namespace to load restore strategies from.'),
|
||||
cfg.DictOpt('backup_incremental_strategy', default={},
|
||||
help='Incremental Backup Runner based on the default '
|
||||
'strategy. For strategies that do not implement an '
|
||||
'incremental, the runner will use the default full backup.'),
|
||||
cfg.ListOpt('ignore_users', default=['PUBLIC', 'DB2INST1']),
|
||||
]
|
||||
|
||||
# RPC version groups
|
||||
upgrade_levels = cfg.OptGroup(
|
||||
'upgrade_levels',
|
||||
@ -910,6 +952,7 @@ CONF.register_group(mongodb_group)
|
||||
CONF.register_group(postgresql_group)
|
||||
CONF.register_group(couchdb_group)
|
||||
CONF.register_group(vertica_group)
|
||||
CONF.register_group(db2_group)
|
||||
|
||||
CONF.register_opts(mysql_opts, mysql_group)
|
||||
CONF.register_opts(percona_opts, percona_group)
|
||||
@ -920,6 +963,7 @@ CONF.register_opts(mongodb_opts, mongodb_group)
|
||||
CONF.register_opts(postgresql_opts, postgresql_group)
|
||||
CONF.register_opts(couchdb_opts, couchdb_group)
|
||||
CONF.register_opts(vertica_opts, vertica_group)
|
||||
CONF.register_opts(db2_opts, db2_group)
|
||||
|
||||
CONF.register_opts(rpcapi_cap_opts, upgrade_levels)
|
||||
|
||||
|
205
trove/guestagent/datastore/experimental/db2/manager.py
Normal file
205
trove/guestagent/datastore/experimental/db2/manager.py
Normal file
@ -0,0 +1,205 @@
|
||||
# Copyright 2015 IBM Corp
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.openstack.common import periodic_task
|
||||
from trove.guestagent import dbaas
|
||||
from trove.guestagent import volume
|
||||
from trove.guestagent.datastore.experimental.db2 import service
|
||||
from trove.openstack.common.gettextutils import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
MANAGER = CONF.datastore_manager
|
||||
|
||||
|
||||
class Manager(periodic_task.PeriodicTasks):
|
||||
"""
|
||||
This is DB2 Manager class. It is dynamically loaded
|
||||
based off of the datastore of the Trove instance.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.appStatus = service.DB2AppStatus()
|
||||
self.app = service.DB2App(self.appStatus)
|
||||
self.admin = service.DB2Admin()
|
||||
|
||||
@periodic_task.periodic_task(ticks_between_runs=3)
|
||||
def update_status(self, context):
|
||||
"""
|
||||
Updates the status of DB2 Trove instance. It is decorated
|
||||
with perodic task so it is automatically called every 3 ticks.
|
||||
"""
|
||||
self.appStatus.update()
|
||||
|
||||
def prepare(self, context, packages, databases, memory_mb, users,
|
||||
device_path=None, mount_point=None, backup_info=None,
|
||||
config_contents=None, root_password=None, overrides=None,
|
||||
cluster_config=None, snapshot=None):
|
||||
"""
|
||||
This is called when the Trove instance first comes online.
|
||||
It is the first rpc message passed from the task manager.
|
||||
prepare handles all the base configuration of the DB2 instance.
|
||||
"""
|
||||
LOG.debug("Preparing the guest agent for DB2.")
|
||||
self.appStatus.begin_install()
|
||||
if device_path:
|
||||
device = volume.VolumeDevice(device_path)
|
||||
device.unmount_device(device_path)
|
||||
device.format()
|
||||
device.mount(mount_point)
|
||||
LOG.debug('Mounted the volume.')
|
||||
self.app.change_ownership(mount_point)
|
||||
self.app.start_db()
|
||||
|
||||
if databases:
|
||||
self.create_database(context, databases)
|
||||
|
||||
if users:
|
||||
self.create_user(context, users)
|
||||
|
||||
self.update_status(context)
|
||||
self.app.complete_install_or_restart()
|
||||
LOG.info(_('Completed setup of DB2 database instance.'))
|
||||
|
||||
def restart(self, context):
|
||||
"""
|
||||
Restart this DB2 instance.
|
||||
This method is called when the guest agent
|
||||
gets a restart message from the taskmanager.
|
||||
"""
|
||||
LOG.debug("Restart a DB2 server instance.")
|
||||
self.app.restart()
|
||||
|
||||
def stop_db(self, context, do_not_start_on_reboot=False):
|
||||
"""
|
||||
Stop this DB2 instance.
|
||||
This method is called when the guest agent
|
||||
gets a stop message from the taskmanager.
|
||||
"""
|
||||
LOG.debug("Stop a given DB2 server instance.")
|
||||
self.app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot)
|
||||
|
||||
def get_filesystem_stats(self, context, fs_path):
|
||||
"""Gets the filesystem stats for the path given."""
|
||||
LOG.debug("Get the filesystem stats.")
|
||||
mount_point = CONF.get(
|
||||
'db2' if not MANAGER else MANAGER).mount_point
|
||||
return dbaas.get_filesystem_volume_stats(mount_point)
|
||||
|
||||
def create_database(self, context, databases):
|
||||
LOG.debug("Creating database(s)." % databases)
|
||||
self.admin.create_database(databases)
|
||||
|
||||
def delete_database(self, context, database):
|
||||
LOG.debug("Deleting database %s." % database)
|
||||
return self.admin.delete_database(database)
|
||||
|
||||
def list_databases(self, context, limit=None, marker=None,
|
||||
include_marker=False):
|
||||
LOG.debug("Listing all databases.")
|
||||
return self.admin.list_databases(limit, marker, include_marker)
|
||||
|
||||
def create_user(self, context, users):
|
||||
LOG.debug("Create user(s).")
|
||||
self.admin.create_user(users)
|
||||
|
||||
def delete_user(self, context, user):
|
||||
LOG.debug("Delete a user %s." % user)
|
||||
self.admin.delete_user(user)
|
||||
|
||||
def get_user(self, context, username, hostname):
|
||||
LOG.debug("Show details of user %s." % username)
|
||||
return self.admin.get_user(username, hostname)
|
||||
|
||||
def list_users(self, context, limit=None, marker=None,
|
||||
include_marker=False):
|
||||
LOG.debug("List all users.")
|
||||
return self.admin.list_users(limit, marker, include_marker)
|
||||
|
||||
def list_access(self, context, username, hostname):
|
||||
LOG.debug("List all the databases the user has access to.")
|
||||
return self.admin.list_access(username, hostname)
|
||||
|
||||
def mount_volume(self, context, device_path=None, mount_point=None):
|
||||
device = volume.VolumeDevice(device_path)
|
||||
device.mount(mount_point, write_to_fstab=False)
|
||||
LOG.debug("Mounted the device %s at the mount point %s." %
|
||||
(device_path, mount_point))
|
||||
|
||||
def unmount_volume(self, context, device_path=None, mount_point=None):
|
||||
device = volume.VolumeDevice(device_path)
|
||||
device.unmount(mount_point)
|
||||
LOG.debug("Unmounted the device %s from the mount point %s." %
|
||||
(device_path, mount_point))
|
||||
|
||||
def resize_fs(self, context, device_path=None, mount_point=None):
|
||||
device = volume.VolumeDevice(device_path)
|
||||
device.resize_fs(mount_point)
|
||||
LOG.debug("Resized the filesystem %s." % mount_point)
|
||||
|
||||
def start_db_with_conf_changes(self, context, config_contents):
|
||||
LOG.debug("Starting DB2 with configuration changes.")
|
||||
self.app.start_db_with_conf_changes(config_contents)
|
||||
|
||||
def grant_access(self, context, username, hostname, databases):
|
||||
LOG.debug("Granting acccess.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='grant_access', datastore=MANAGER)
|
||||
|
||||
def revoke_access(self, context, username, hostname, database):
|
||||
LOG.debug("Revoking access.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='revoke_access', datastore=MANAGER)
|
||||
|
||||
def reset_configuration(self, context, configuration):
|
||||
LOG.debug("Resetting DB2 configuration.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='change_passwords', datastore=MANAGER)
|
||||
|
||||
def change_passwords(self, context, users):
|
||||
LOG.debug("Changing password.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='change_passwords', datastore=MANAGER)
|
||||
|
||||
def update_attributes(self, context, username, hostname, user_attrs):
|
||||
LOG.debug("Updating database attributes.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='update_attributes', datastore=MANAGER)
|
||||
|
||||
def enable_root(self, context):
|
||||
LOG.debug("Enabling root.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='enable_root', datastore=MANAGER)
|
||||
|
||||
def is_root_enabled(self, context):
|
||||
LOG.debug("Checking if root is enabled.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='is_root_enabled', datastore=MANAGER)
|
||||
|
||||
def _perform_restore(self, backup_info, context, restore_location, app):
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='_perform_restore', datastore=MANAGER)
|
||||
|
||||
def create_backup(self, context, backup_info):
|
||||
LOG.debug("Creating backup.")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='create_backup', datastore=MANAGER)
|
||||
|
||||
def get_config_changes(self, cluster_config, mount_point=None):
|
||||
LOG.debug("Get configuration changes")
|
||||
raise exception.DatastoreOperationNotSupported(
|
||||
operation='get_configuration_changes', datastore=MANAGER)
|
440
trove/guestagent/datastore/experimental/db2/service.py
Normal file
440
trove/guestagent/datastore/experimental/db2/service.py
Normal file
@ -0,0 +1,440 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.common import utils as utils
|
||||
from trove.guestagent.datastore import service
|
||||
from trove.guestagent.datastore.experimental.db2 import system
|
||||
from trove.guestagent.db import models
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.openstack.common.gettextutils import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
IGNORE_USERS_LIST = CONF.db2.ignore_users
|
||||
|
||||
|
||||
class DB2App(object):
|
||||
"""
|
||||
Handles installation and configuration of DB2
|
||||
on a Trove instance.
|
||||
"""
|
||||
def __init__(self, status, state_change_wait_time=None):
|
||||
LOG.debug("Initialize DB2App.")
|
||||
self.state_change_wait_time = (
|
||||
state_change_wait_time if state_change_wait_time else
|
||||
CONF.state_change_wait_time
|
||||
)
|
||||
LOG.debug("state_change_wait_time = %s." % self.state_change_wait_time)
|
||||
self.status = status
|
||||
|
||||
def complete_install_or_restart(self):
|
||||
self.status.end_install_or_restart()
|
||||
|
||||
def change_ownership(self, mount_point):
|
||||
"""
|
||||
When DB2 server instance is installed, it does not have the
|
||||
DB2 local database directory created (/home/db2inst1/db2inst1).
|
||||
This gets created when we mount the cinder volume. So we need
|
||||
to change ownership of this directory to the DB2 instance user
|
||||
- db2inst1.
|
||||
"""
|
||||
LOG.debug("Changing ownership of the DB2 data directory.")
|
||||
try:
|
||||
utils.execute_with_timeout(
|
||||
system.CHANGE_DB_DIR_OWNER % {'datadir': mount_point},
|
||||
shell=True)
|
||||
utils.execute_with_timeout(
|
||||
system.CHANGE_DB_DIR_GROUP_OWNER % {'datadir': mount_point},
|
||||
shell=True)
|
||||
except exception.ProcessExecutionError:
|
||||
raise RuntimeError(_(
|
||||
"Command to change ownership of DB2 data directory failed."))
|
||||
|
||||
def _enable_db_on_boot(self):
|
||||
LOG.debug("Enable DB on boot.")
|
||||
try:
|
||||
run_command(system.ENABLE_AUTOSTART)
|
||||
except exception.ProcessExecutionError:
|
||||
raise RuntimeError(_(
|
||||
"Command to enable DB2 server on boot failed."))
|
||||
|
||||
def _disable_db_on_boot(self):
|
||||
LOG.debug("Disable DB2 on boot.")
|
||||
try:
|
||||
run_command(system.DISABLE_AUTOSTART)
|
||||
except exception.ProcessExecutionError:
|
||||
raise RuntimeError(_(
|
||||
"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.start_db(True)
|
||||
|
||||
def start_db(self, update_db=False):
|
||||
LOG.debug("Start the DB2 server instance.")
|
||||
self._enable_db_on_boot()
|
||||
try:
|
||||
run_command(system.START_DB2)
|
||||
except exception.ProcessExecutionError:
|
||||
pass
|
||||
|
||||
if not self.status.wait_for_real_status_to_change_to(
|
||||
rd_instance.ServiceStatuses.RUNNING,
|
||||
self.state_change_wait_time, update_db):
|
||||
LOG.error(_("Start of DB2 server instance failed."))
|
||||
self.status.end_install_or_restart()
|
||||
raise RuntimeError(_("Could not start DB2."))
|
||||
|
||||
def stop_db(self, update_db=False, do_not_start_on_reboot=False):
|
||||
LOG.debug("Stop the DB2 server instance.")
|
||||
if do_not_start_on_reboot:
|
||||
self._disable_db_on_boot()
|
||||
try:
|
||||
run_command(system.STOP_DB2)
|
||||
except exception.ProcessExecutionError:
|
||||
pass
|
||||
|
||||
if not (self.status.wait_for_real_status_to_change_to(
|
||||
rd_instance.ServiceStatuses.SHUTDOWN,
|
||||
self.state_change_wait_time, update_db)):
|
||||
LOG.error(_("Could not stop DB2."))
|
||||
self.status.end_install_or_restart()
|
||||
raise RuntimeError(_("Could not stop DB2."))
|
||||
|
||||
def restart(self):
|
||||
LOG.debug("Restarting DB2 server instance.")
|
||||
try:
|
||||
self.status.begin_restart()
|
||||
self.stop_db()
|
||||
self.start_db()
|
||||
finally:
|
||||
self.status.end_install_or_restart()
|
||||
|
||||
|
||||
class DB2AppStatus(service.BaseDbStatus):
|
||||
"""
|
||||
Handles all of the status updating for the DB2 guest agent.
|
||||
"""
|
||||
def _get_actual_db_status(self):
|
||||
LOG.debug("Getting the status of the DB2 server instance.")
|
||||
try:
|
||||
out, err = utils.execute_with_timeout(
|
||||
system.DB2_STATUS, shell=True)
|
||||
if "0" not in out:
|
||||
return rd_instance.ServiceStatuses.RUNNING
|
||||
else:
|
||||
return rd_instance.ServiceStatuses.SHUTDOWN
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.exception(_("Error getting the DB2 server status."))
|
||||
return rd_instance.ServiceStatuses.CRASHED
|
||||
|
||||
|
||||
def run_command(command, superuser=system.DB2_INSTANCE_OWNER,
|
||||
timeout=system.TIMEOUT):
|
||||
return utils.execute_with_timeout("sudo", "su", "-", superuser, "-c",
|
||||
command, timeout=timeout)
|
||||
|
||||
|
||||
class DB2Admin(object):
|
||||
"""
|
||||
Handles administrative tasks on the DB2 instance.
|
||||
"""
|
||||
def create_database(self, databases):
|
||||
"""Create the given database(s)."""
|
||||
dbName = None
|
||||
db_create_failed = []
|
||||
LOG.debug("Creating DB2 databases.")
|
||||
for item in databases:
|
||||
mydb = models.ValidatedMySQLDatabase()
|
||||
mydb.deserialize(item)
|
||||
dbName = mydb.name
|
||||
LOG.debug("Creating DB2 database: %s." % dbName)
|
||||
try:
|
||||
run_command(system.CREATE_DB_COMMAND % {'dbname': dbName})
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.exception(_(
|
||||
"There was an error creating database: %s.") % dbName)
|
||||
db_create_failed.append(dbName)
|
||||
pass
|
||||
if len(db_create_failed) > 0:
|
||||
LOG.exception(_("Creating the following databases failed: %s.") %
|
||||
db_create_failed)
|
||||
|
||||
def delete_database(self, database):
|
||||
"""Delete the specified database."""
|
||||
dbName = None
|
||||
try:
|
||||
mydb = models.ValidatedMySQLDatabase()
|
||||
mydb.deserialize(database)
|
||||
dbName = mydb.name
|
||||
LOG.debug("Deleting DB2 database: %s." % dbName)
|
||||
run_command(system.DELETE_DB_COMMAND % {'dbname': dbName})
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.exception(_(
|
||||
"There was an error while deleting database:%s.") % dbName)
|
||||
raise exception.GuestError(_("Unable to delete database: %s.") %
|
||||
dbName)
|
||||
|
||||
def list_databases(self, limit=None, marker=None, include_marker=False):
|
||||
LOG.debug("Listing all the DB2 databases.")
|
||||
databases = []
|
||||
next_marker = None
|
||||
|
||||
try:
|
||||
out, err = run_command(system.LIST_DB_COMMAND)
|
||||
dblist = out.split()
|
||||
result = iter(dblist)
|
||||
count = 0
|
||||
|
||||
if marker is not None:
|
||||
try:
|
||||
item = result.next()
|
||||
while item != marker:
|
||||
item = result.next()
|
||||
|
||||
if item == marker:
|
||||
marker = None
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
try:
|
||||
item = result.next()
|
||||
while item:
|
||||
count = count + 1
|
||||
if (limit and count <= limit) or limit is None:
|
||||
db2_db = models.MySQLDatabase()
|
||||
db2_db.name = item
|
||||
LOG.debug("database = %s ." % item)
|
||||
db2_db.character_set = None
|
||||
db2_db.collate = None
|
||||
next_marker = db2_db.name
|
||||
databases.append(db2_db.serialize())
|
||||
item = result.next()
|
||||
else:
|
||||
next_marker = None
|
||||
break
|
||||
except StopIteration:
|
||||
next_marker = None
|
||||
LOG.debug("databases = %s." % str(databases))
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.exception(_("An error occured listing databases: %s.") %
|
||||
pe.message)
|
||||
pass
|
||||
return databases, next_marker
|
||||
|
||||
def create_user(self, users):
|
||||
LOG.debug("Creating user(s) for accessing DB2 database(s).")
|
||||
try:
|
||||
for item in users:
|
||||
user = models.MySQLUser()
|
||||
user.deserialize(item)
|
||||
try:
|
||||
LOG.debug("Creating OS user: %s." % user.name)
|
||||
utils.execute_with_timeout(
|
||||
system.CREATE_USER_COMMAND % {
|
||||
'login': user.name, 'login': user.name,
|
||||
'passwd': user.password}, shell=True)
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.exception(_("Error creating user: %s.") % user.name)
|
||||
continue
|
||||
|
||||
for database in user.databases:
|
||||
mydb = models.ValidatedMySQLDatabase()
|
||||
mydb.deserialize(database)
|
||||
try:
|
||||
LOG.debug("Granting user: %s access to database: %s."
|
||||
% (user.name, mydb.name))
|
||||
run_command(system.GRANT_USER_ACCESS % {
|
||||
'dbname': mydb.name, 'login': user.name})
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.debug(
|
||||
"Error granting user: %s access to database: %s."
|
||||
% (user.name, mydb.name))
|
||||
LOG.debug(pe)
|
||||
pass
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.exception(_("An error occured creating users: %s.") %
|
||||
pe.message)
|
||||
pass
|
||||
|
||||
def delete_user(self, user):
|
||||
LOG.debug("Delete a given user.")
|
||||
db2_user = models.MySQLUser()
|
||||
db2_user.deserialize(user)
|
||||
userName = db2_user.name
|
||||
user_dbs = db2_user.databases
|
||||
LOG.debug("For user %s, databases to be deleted = %r." % (
|
||||
userName, user_dbs))
|
||||
|
||||
if len(user_dbs) == 0:
|
||||
databases = self.list_access(db2_user.name, None)
|
||||
else:
|
||||
databases = user_dbs
|
||||
|
||||
LOG.debug("databases for user = %r." % databases)
|
||||
for database in databases:
|
||||
mydb = models.ValidatedMySQLDatabase()
|
||||
mydb.deserialize(database)
|
||||
try:
|
||||
run_command(system.REVOKE_USER_ACCESS % {
|
||||
'dbname': mydb.name,
|
||||
'login': userName})
|
||||
LOG.debug("Revoked access for user:%s on database:%s." % (
|
||||
userName, mydb.name))
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.debug("Error occured while revoking access to %s." %
|
||||
mydb.name)
|
||||
pass
|
||||
try:
|
||||
utils.execute_with_timeout(system.DELETE_USER_COMMAND % {
|
||||
'login': db2_user.name.lower()}, shell=True)
|
||||
except exception.ProcessExecutionError as pe:
|
||||
LOG.exception(_(
|
||||
"There was an error while deleting user: %s.") % pe)
|
||||
raise exception.GuestError(_("Unable to delete user: %s.") %
|
||||
userName)
|
||||
|
||||
def list_users(self, limit=None, marker=None, include_marker=False):
|
||||
LOG.debug(
|
||||
"List all users for all the databases in a DB2 server instance.")
|
||||
users = []
|
||||
user_map = {}
|
||||
next_marker = None
|
||||
count = 0
|
||||
|
||||
databases, marker = self.list_databases()
|
||||
for database in databases:
|
||||
db2_db = models.MySQLDatabase()
|
||||
db2_db.deserialize(database)
|
||||
out = None
|
||||
try:
|
||||
out, err = run_command(
|
||||
system.LIST_DB_USERS % {'dbname': db2_db.name})
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.debug(
|
||||
"There was an error while listing users for database: %s."
|
||||
% db2_db.name)
|
||||
continue
|
||||
|
||||
userlist = []
|
||||
for item in out.split('\n'):
|
||||
LOG.debug("item = %r" % item)
|
||||
user = item.split() if item != "" else None
|
||||
LOG.debug("user = %r" % (user))
|
||||
if user is not None and user[0] not in IGNORE_USERS_LIST \
|
||||
and user[1] == 'Y':
|
||||
userlist.append(user[0])
|
||||
result = iter(userlist)
|
||||
|
||||
if marker is not None:
|
||||
try:
|
||||
item = result.next()
|
||||
while item != marker:
|
||||
item = result.next()
|
||||
|
||||
if item == marker:
|
||||
marker = None
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
try:
|
||||
item = result.next()
|
||||
db2db = models.MySQLDatabase()
|
||||
db2db.name = db2_db.name
|
||||
|
||||
while item:
|
||||
'''
|
||||
Check if the user has already been discovered. If so,
|
||||
add this database to the database list for this user.
|
||||
'''
|
||||
if item in user_map:
|
||||
db2user = user_map.get(item)
|
||||
db2user.databases.append(db2db.serialize())
|
||||
item = result.next()
|
||||
continue
|
||||
'''
|
||||
If this user was not previously discovered, then add
|
||||
this to the user's list.
|
||||
'''
|
||||
count = count + 1
|
||||
if (limit and count <= limit) or limit is None:
|
||||
db2_user = models.MySQLUser()
|
||||
db2_user.name = item
|
||||
db2_user.databases.append(db2db.serialize())
|
||||
users.append(db2_user.serialize())
|
||||
user_map.update({item: db2_user})
|
||||
item = result.next()
|
||||
else:
|
||||
next_marker = None
|
||||
break
|
||||
except StopIteration:
|
||||
next_marker = None
|
||||
|
||||
if count == limit:
|
||||
break
|
||||
return users, next_marker
|
||||
|
||||
def get_user(self, username, hostname):
|
||||
LOG.debug("Get details of a given database user.")
|
||||
user = self._get_user(username, hostname)
|
||||
if not user:
|
||||
return None
|
||||
return user.serialize()
|
||||
|
||||
def _get_user(self, username, hostname):
|
||||
LOG.debug("Get details of a given database user %s." % username)
|
||||
user = models.MySQLUser()
|
||||
user.name = username
|
||||
databases, marker = self.list_databases()
|
||||
out = None
|
||||
for database in databases:
|
||||
db2_db = models.MySQLDatabase()
|
||||
db2_db.deserialize(database)
|
||||
try:
|
||||
out, err = run_command(
|
||||
system.LIST_DB_USERS % {'dbname': db2_db.name})
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.debug(
|
||||
"Error while trying to get the users for database: %s." %
|
||||
db2_db.name)
|
||||
continue
|
||||
|
||||
for item in out.split('\n'):
|
||||
user_access = item.split() if item != "" else None
|
||||
if (user_access is not None and
|
||||
user_access[0].lower() == username.lower() and
|
||||
user_access[1] == 'Y'):
|
||||
user.databases = db2_db.name
|
||||
break
|
||||
return user
|
||||
|
||||
def list_access(self, username, hostname):
|
||||
"""
|
||||
Show all the databases to which the user has more than
|
||||
USAGE granted.
|
||||
"""
|
||||
LOG.debug("Listing databases that user: %s has access to." % username)
|
||||
user = self._get_user(username, hostname)
|
||||
return user.databases
|
49
trove/guestagent/datastore/experimental/db2/system.py
Normal file
49
trove/guestagent/datastore/experimental/db2/system.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
# 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.
|
||||
|
||||
TIMEOUT = 1200
|
||||
DB2_INSTANCE_OWNER = "db2inst1"
|
||||
ENABLE_AUTOSTART = (
|
||||
"/opt/ibm/db2/V10.5/instance/db2iauto -on " + DB2_INSTANCE_OWNER)
|
||||
DISABLE_AUTOSTART = (
|
||||
"/opt/ibm/db2/V10.5/instance/db2iauto -off " + DB2_INSTANCE_OWNER)
|
||||
START_DB2 = "db2start"
|
||||
STOP_DB2 = "db2 force application all; db2 terminate; db2stop"
|
||||
DB2_STATUS = ("ps -ef | grep " + DB2_INSTANCE_OWNER + " | grep db2sysc |"
|
||||
"grep -v grep | wc -l")
|
||||
CHANGE_DB_DIR_OWNER = "sudo chown " + DB2_INSTANCE_OWNER + " %(datadir)s"
|
||||
CHANGE_DB_DIR_GROUP_OWNER = (
|
||||
"sudo chgrp " + DB2_INSTANCE_OWNER + " %(datadir)s")
|
||||
CREATE_DB_COMMAND = "db2 create database %(dbname)s"
|
||||
DELETE_DB_COMMAND = "db2 drop database %(dbname)s"
|
||||
LIST_DB_COMMAND = (
|
||||
"db2 list database directory | grep -B6 -i indirect | "
|
||||
"grep 'Database name' | sed 's/.*= //'")
|
||||
CREATE_USER_COMMAND = (
|
||||
'sudo useradd -m -d /home/%(login)s %(login)s;'
|
||||
'sudo echo %(login)s:%(passwd)s |sudo chpasswd')
|
||||
GRANT_USER_ACCESS = (
|
||||
"db2 connect to %(dbname)s; "
|
||||
"db2 GRANT DBADM,CREATETAB,BINDADD,CONNECT,DATAACCESS "
|
||||
"ON DATABASE TO USER %(login)s; db2 connect reset")
|
||||
DELETE_USER_COMMAND = 'sudo userdel -r %(login)s'
|
||||
REVOKE_USER_ACCESS = (
|
||||
"db2 connect to %(dbname)s; "
|
||||
"db2 REVOKE DBADM,CREATETAB,BINDADD,CONNECT,DATAACCESS "
|
||||
"ON DATABASE FROM USER %(login)s; db2 connect reset")
|
||||
LIST_DB_USERS = (
|
||||
"db2 +o connect to %(dbname)s; "
|
||||
"db2 -x select grantee, dataaccessauth from sysibm.sysdbauth; "
|
||||
"db2 connect reset")
|
@ -51,6 +51,8 @@ defaults = {
|
||||
'trove.guestagent.datastore.experimental.couchdb.manager.Manager',
|
||||
'vertica':
|
||||
'trove.guestagent.datastore.experimental.vertica.manager.Manager',
|
||||
'db2':
|
||||
'trove.guestagent.datastore.experimental.db2.manager.Manager',
|
||||
}
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
0
trove/templates/db2/config.template
Normal file
0
trove/templates/db2/config.template
Normal file
0
trove/templates/db2/override.config.template
Normal file
0
trove/templates/db2/override.config.template
Normal file
201
trove/tests/unittests/guestagent/test_db2_manager.py
Normal file
201
trove/tests/unittests/guestagent/test_db2_manager.py
Normal file
@ -0,0 +1,201 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
#
|
||||
# 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 testtools
|
||||
from mock import MagicMock
|
||||
from testtools.matchers import Is, Equals, Not
|
||||
from trove.common.context import TroveContext
|
||||
from trove.common.instance import ServiceStatuses
|
||||
from trove.guestagent import volume
|
||||
from trove.guestagent.datastore.experimental.db2 import (
|
||||
service as db2_service)
|
||||
from trove.guestagent.datastore.experimental.db2 import (
|
||||
manager as db2_manager)
|
||||
from trove.guestagent import pkg as pkg
|
||||
|
||||
|
||||
class GuestAgentDB2ManagerTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GuestAgentDB2ManagerTest, self).setUp()
|
||||
self.real_status = db2_service.DB2AppStatus.set_status
|
||||
|
||||
class FakeInstanceServiceStatus(object):
|
||||
status = ServiceStatuses.NEW
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
db2_service.DB2AppStatus.set_status = MagicMock(
|
||||
return_value=FakeInstanceServiceStatus())
|
||||
self.context = TroveContext()
|
||||
self.manager = db2_manager.Manager()
|
||||
self.real_db_app_status = db2_service.DB2AppStatus
|
||||
self.origin_format = volume.VolumeDevice.format
|
||||
self.origin_mount = volume.VolumeDevice.mount
|
||||
self.origin_mount_points = volume.VolumeDevice.mount_points
|
||||
self.origin_stop_db = db2_service.DB2App.stop_db
|
||||
self.origin_start_db = db2_service.DB2App.start_db
|
||||
self.orig_change_ownership = (db2_service.DB2App.change_ownership)
|
||||
self.orig_create_databases = db2_service.DB2Admin.create_database
|
||||
self.orig_list_databases = db2_service.DB2Admin.list_databases
|
||||
self.orig_delete_database = db2_service.DB2Admin.delete_database
|
||||
self.orig_create_users = db2_service.DB2Admin.create_user
|
||||
self.orig_list_users = db2_service.DB2Admin.list_users
|
||||
self.orig_delete_user = db2_service.DB2Admin.delete_user
|
||||
|
||||
def tearDown(self):
|
||||
super(GuestAgentDB2ManagerTest, self).tearDown()
|
||||
db2_service.DB2AppStatus.set_status = self.real_db_app_status
|
||||
volume.VolumeDevice.format = self.origin_format
|
||||
volume.VolumeDevice.mount = self.origin_mount
|
||||
volume.VolumeDevice.mount_points = self.origin_mount_points
|
||||
db2_service.DB2App.stop_db = self.origin_stop_db
|
||||
db2_service.DB2App.start_db = self.origin_start_db
|
||||
db2_service.DB2App.change_ownership = self.orig_change_ownership
|
||||
db2_service.DB2Admin.create_database = self.orig_create_databases
|
||||
db2_service.DB2Admin.create_user = self.orig_create_users
|
||||
db2_service.DB2Admin.create_database = self.orig_create_databases
|
||||
db2_service.DB2Admin.list_databases = self.orig_list_databases
|
||||
db2_service.DB2Admin.delete_database = self.orig_delete_database
|
||||
db2_service.DB2Admin.create_user = self.orig_create_users
|
||||
db2_service.DB2Admin.list_users = self.orig_list_users
|
||||
db2_service.DB2Admin.delete_user = self.orig_delete_user
|
||||
|
||||
def test_update_status(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
self.manager.update_status(self.context)
|
||||
mock_status.update.assert_any_call()
|
||||
|
||||
def test_prepare_device_path_true(self):
|
||||
self._prepare_dynamic()
|
||||
|
||||
def test_prepare_device_path_false(self):
|
||||
self._prepare_dynamic(device_path=None)
|
||||
|
||||
def test_prepare_database(self):
|
||||
self._prepare_dynamic(databases=['db1'])
|
||||
|
||||
def _prepare_dynamic(self, packages=None, databases=None, users=None,
|
||||
config_content=None, device_path='/dev/vdb',
|
||||
is_db_installed=True, backup_id=None, overrides=None):
|
||||
mock_status = MagicMock()
|
||||
mock_app = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
self.manager.app = mock_app
|
||||
|
||||
mock_status.begin_install = MagicMock(return_value=None)
|
||||
pkg.Package.pkg_is_installed = MagicMock(return_value=is_db_installed)
|
||||
mock_app.change_ownership = MagicMock(return_value=None)
|
||||
mock_app.restart = MagicMock(return_value=None)
|
||||
mock_app.start_db = MagicMock(return_value=None)
|
||||
mock_app.stop_db = MagicMock(return_value=None)
|
||||
volume.VolumeDevice.format = MagicMock(return_value=None)
|
||||
volume.VolumeDevice.mount = MagicMock(return_value=None)
|
||||
volume.VolumeDevice.mount_points = MagicMock(return_value=[])
|
||||
db2_service.DB2Admin.create_user = MagicMock(return_value=None)
|
||||
db2_service.DB2Admin.create_database = MagicMock(return_value=None)
|
||||
|
||||
self.manager.prepare(context=self.context, packages=packages,
|
||||
config_contents=config_content,
|
||||
databases=databases,
|
||||
memory_mb='2048', users=users,
|
||||
device_path=device_path,
|
||||
mount_point="/home/db2inst1/db2inst1",
|
||||
backup_info=None,
|
||||
overrides=None,
|
||||
cluster_config=None)
|
||||
mock_status.begin_install.assert_any_call()
|
||||
self.assertEqual(mock_app.change_ownership.call_count, 1)
|
||||
if databases:
|
||||
self.assertTrue(db2_service.DB2Admin.create_database.called)
|
||||
else:
|
||||
self.assertFalse(db2_service.DB2Admin.create_database.called)
|
||||
|
||||
if users:
|
||||
self.assertTrue(db2_service.DB2Admin.create_user.called)
|
||||
else:
|
||||
self.assertFalse(db2_service.DB2Admin.create_user.called)
|
||||
|
||||
def test_restart(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2App.restart = MagicMock(return_value=None)
|
||||
self.manager.restart(self.context)
|
||||
db2_service.DB2App.restart.assert_any_call()
|
||||
|
||||
def test_stop_db(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2App.stop_db = MagicMock(return_value=None)
|
||||
self.manager.stop_db(self.context)
|
||||
db2_service.DB2App.stop_db.assert_any_call(
|
||||
do_not_start_on_reboot=False)
|
||||
|
||||
def test_create_database(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.create_database = MagicMock(return_value=None)
|
||||
self.manager.create_database(self.context, ['db1'])
|
||||
db2_service.DB2Admin.create_database.assert_any_call(['db1'])
|
||||
|
||||
def test_create_user(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.create_user = MagicMock(return_value=None)
|
||||
self.manager.create_user(self.context, ['user1'])
|
||||
db2_service.DB2Admin.create_user.assert_any_call(['user1'])
|
||||
|
||||
def test_delete_database(self):
|
||||
databases = ['db1']
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.delete_database = MagicMock(return_value=None)
|
||||
self.manager.delete_database(self.context, databases)
|
||||
db2_service.DB2Admin.delete_database.assert_any_call(databases)
|
||||
|
||||
def test_delete_user(self):
|
||||
user = ['user1']
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.delete_user = MagicMock(return_value=None)
|
||||
self.manager.delete_user(self.context, user)
|
||||
db2_service.DB2Admin.delete_user.assert_any_call(user)
|
||||
|
||||
def test_list_databases(self):
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.list_databases = MagicMock(
|
||||
return_value=['database1'])
|
||||
databases = self.manager.list_databases(self.context)
|
||||
self.assertThat(databases, Not(Is(None)))
|
||||
self.assertThat(databases, Equals(['database1']))
|
||||
db2_service.DB2Admin.list_databases.assert_any_call(None, None, False)
|
||||
|
||||
def test_list_users(self):
|
||||
db2_service.DB2Admin.list_users = MagicMock(return_value=['user1'])
|
||||
users = self.manager.list_users(self.context)
|
||||
self.assertThat(users, Equals(['user1']))
|
||||
db2_service.DB2Admin.list_users.assert_any_call(None, None, False)
|
||||
|
||||
def test_get_users(self):
|
||||
username = ['user1']
|
||||
hostname = ['host']
|
||||
mock_status = MagicMock()
|
||||
self.manager.appStatus = mock_status
|
||||
db2_service.DB2Admin.get_user = MagicMock(return_value=['user1'])
|
||||
users = self.manager.get_user(self.context, username, hostname)
|
||||
self.assertThat(users, Equals(['user1']))
|
||||
db2_service.DB2Admin.get_user.assert_any_call(username, hostname)
|
@ -27,6 +27,7 @@ from testtools.matchers import Is
|
||||
from testtools.matchers import Equals
|
||||
from testtools.matchers import Not
|
||||
from trove.common.exception import ProcessExecutionError
|
||||
from trove.common.exception import GuestError
|
||||
from trove.common import utils
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.conductor import api as conductor_api
|
||||
@ -62,6 +63,8 @@ from trove.guestagent.datastore.experimental.vertica.service import (
|
||||
VerticaAppStatus)
|
||||
from trove.guestagent.datastore.experimental.vertica import (
|
||||
system as vertica_system)
|
||||
from trove.guestagent.datastore.experimental.db2 import (
|
||||
service as db2service)
|
||||
from trove.guestagent.db import models
|
||||
from trove.guestagent.volume import VolumeDevice
|
||||
from trove.instance.models import InstanceServiceStatus
|
||||
@ -79,7 +82,6 @@ FAKE_DB_2 = {"_name": "testDB2", "_character_set": "latin2",
|
||||
FAKE_USER = [{"_name": "random", "_password": "guesswhat",
|
||||
"_databases": [FAKE_DB]}]
|
||||
|
||||
|
||||
conductor_api.API.get_client = Mock()
|
||||
conductor_api.API.heartbeat = Mock()
|
||||
|
||||
@ -1005,6 +1007,9 @@ class ServiceRegistryTest(testtools.TestCase):
|
||||
self.assertEqual(test_dict.get('couchdb'),
|
||||
'trove.guestagent.datastore.experimental.couchdb.'
|
||||
'manager.Manager')
|
||||
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 = {
|
||||
@ -1038,6 +1043,9 @@ class ServiceRegistryTest(testtools.TestCase):
|
||||
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()
|
||||
@ -1068,6 +1076,9 @@ class ServiceRegistryTest(testtools.TestCase):
|
||||
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):
|
||||
@ -2314,3 +2325,189 @@ class VerticaAppTest(testtools.TestCase):
|
||||
# Verifying nu,ber of shell calls,
|
||||
# as command has already been tested in preceeding tests
|
||||
self.assertEqual(vertica_system.shell_execute.call_count, 5)
|
||||
|
||||
|
||||
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(args[0], expected,
|
||||
"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(args[0], expected,
|
||||
"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(
|
||||
args[0], expected,
|
||||
"Granting database access queries are not the same")
|
||||
self.assertEqual(db2service.run_command.call_count, 1)
|
||||
|
||||
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(
|
||||
args[0], expected,
|
||||
"Revoke database access queries are not the same")
|
||||
self.assertEqual(db2service.run_command.call_count, 1)
|
||||
|
||||
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(
|
||||
args[0], expected,
|
||||
"Revoke database access queries are not the same")
|
||||
self.assertEqual(db2service.run_command.call_count, 1)
|
||||
|
||||
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(args[0], expected,
|
||||
"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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user