From ea51b7e625b6be4415a665cd36dab44742b0362e Mon Sep 17 00:00:00 2001 From: Denis Makogon Date: Thu, 27 Feb 2014 12:25:11 +0200 Subject: [PATCH] Fix inconsistent usage of mount_point Reasons: - multiple datastores; - inconsistent usage of mount_point at taskmanager and guestagent; Changes: - oslo groups; - mount point per datastore; - guest API not changed. Change-Id: I8044789c6ab2ed837ff0e1b16183dfeafbf955bb Closes-Bug: #1248212 --- etc/trove/trove-guestagent.conf.sample | 1 + etc/trove/trove.conf.sample | 1 - etc/trove/trove.conf.test | 2 - trove/common/cfg.py | 22 +- trove/guestagent/api.py | 2 +- trove/guestagent/backup/backupagent.py | 4 +- .../guestagent/datastore/cassandra/manager.py | 15 +- .../guestagent/datastore/cassandra/service.py | 9 +- .../guestagent/datastore/cassandra/system.py | 4 +- .../guestagent/datastore/couchbase/manager.py | 12 +- .../guestagent/datastore/couchbase/service.py | 3 +- .../guestagent/datastore/couchbase/system.py | 5 +- trove/guestagent/datastore/mysql/manager.py | 13 +- trove/guestagent/datastore/redis/manager.py | 12 +- trove/guestagent/datastore/redis/system.py | 1 - trove/taskmanager/models.py | 39 +- .../guestagent/test_cassandra_manager.py | 140 +++++ .../guestagent/test_couchbase_manager.py | 95 ++++ .../tests/unittests/guestagent/test_dbaas.py | 20 - .../unittests/guestagent/test_manager.py | 497 ------------------ .../guestagent/test_mysql_manager.py | 219 ++++++++ trove/tests/unittests/guestagent/test_pkg.py | 5 - .../guestagent/test_redis_manager.py | 97 ++++ .../unittests/taskmanager/test_models.py | 5 + 24 files changed, 646 insertions(+), 577 deletions(-) create mode 100644 trove/tests/unittests/guestagent/test_cassandra_manager.py create mode 100644 trove/tests/unittests/guestagent/test_couchbase_manager.py delete mode 100644 trove/tests/unittests/guestagent/test_manager.py create mode 100644 trove/tests/unittests/guestagent/test_mysql_manager.py create mode 100644 trove/tests/unittests/guestagent/test_redis_manager.py diff --git a/etc/trove/trove-guestagent.conf.sample b/etc/trove/trove-guestagent.conf.sample index 928b00f0dc..dc30b28170 100644 --- a/etc/trove/trove-guestagent.conf.sample +++ b/etc/trove/trove-guestagent.conf.sample @@ -69,3 +69,4 @@ backup_aes_cbc_key = "default_aes_cbc_key" backup_use_snet = False backup_chunk_size = 65536 backup_segment_max_size = 2147483648 + diff --git a/etc/trove/trove.conf.sample b/etc/trove/trove.conf.sample index bb447959ab..0381a12ece 100644 --- a/etc/trove/trove.conf.sample +++ b/etc/trove/trove.conf.sample @@ -59,7 +59,6 @@ network_label_regex = ^private$ trove_volume_support = True block_device_mapping = vdb device_path = /dev/vdb -mount_point = /var/lib/mysql # Maximum volume size for an instance max_accepted_volume_size = 10 max_instances_per_user = 5 diff --git a/etc/trove/trove.conf.test b/etc/trove/trove.conf.test index 68691b8b59..80155e5a86 100644 --- a/etc/trove/trove.conf.test +++ b/etc/trove/trove.conf.test @@ -84,7 +84,6 @@ trove_volume_support = True nova_volume_service_type = volume nova_volume_service_name = Volume Service device_path = /dev/vdb -mount_point = /var/lib/mysql max_accepted_volume_size = 25 max_instances_per_user = 55 max_volumes_per_user = 100 @@ -180,4 +179,3 @@ paste.app_factory = trove.common.api:app_factory #Add this filter to log request and response for debugging [filter:debug] paste.filter_factory = trove.common.wsgi:Debug - diff --git a/trove/common/cfg.py b/trove/common/cfg.py index 660c413313..7ae2a6ac58 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -116,7 +116,6 @@ common_opts = [ cfg.BoolOpt('use_nova_server_volume', default=False), cfg.BoolOpt('use_heat', default=False), cfg.StrOpt('device_path', default='/dev/vdb'), - cfg.StrOpt('mount_point', default='/var/lib/mysql'), cfg.StrOpt('default_datastore', default=None, help="The default datastore id or name to use if one is not " "provided by the user. If the default value is None, the field " @@ -276,7 +275,10 @@ mysql_opts = [ ' in the security group (only applicable ' 'if trove_security_groups_support is True)'), cfg.StrOpt('backup_strategy', default='InnoBackupEx', - help='Default strategy to perform backups.') + help='Default strategy to perform backups.'), + cfg.StrOpt('mount_point', default='/var/lib/mysql', + help="Filesystem path for mounting " + "volumes if volume support is enabled"), ] # Percona @@ -293,7 +295,10 @@ percona_opts = [ ' in the security group (only applicable ' 'if trove_security_groups_support is True)'), cfg.StrOpt('backup_strategy', default='InnoBackupEx', - help='Default strategy to perform backups.') + help='Default strategy to perform backups.'), + cfg.StrOpt('mount_point', default='/var/lib/mysql', + help="Filesystem path for mounting " + "volumes if volume support is enabled"), ] # Redis @@ -309,6 +314,11 @@ redis_opts = [ 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('backup_strategy', default=None, + help='Default strategy to perform backups.'), + cfg.StrOpt('mount_point', default='/var/lib/redis', + help="Filesystem path for mounting " + "volumes if volume support is enabled"), ] # Cassandra @@ -326,6 +336,9 @@ cassandra_opts = [ 'if trove_security_groups_support is True)'), cfg.StrOpt('backup_strategy', default=None, help='Default strategy to perform backups.'), + cfg.StrOpt('mount_point', default='/var/lib/cassandra', + help="Filesystem path for mounting " + "volumes if volume support is enabled"), ] #Couchbase @@ -345,6 +358,9 @@ couchbase_opts = [ 'if trove_security_groups_support is True)'), cfg.StrOpt('backup_strategy', default=None, help='Default strategy to perform backups.'), + cfg.StrOpt('mount_point', default='/var/lib/couchbase', + help="Filesystem path for mounting " + "volumes if volume support is enabled"), ] CONF = cfg.CONF diff --git a/trove/guestagent/api.py b/trove/guestagent/api.py index 700062c874..3a35dde740 100644 --- a/trove/guestagent/api.py +++ b/trove/guestagent/api.py @@ -268,7 +268,7 @@ class API(proxy.RpcProxy): LOG.debug(_("Check Volume Info on Instance %s"), self.id) # self._check_for_hearbeat() return self._call("get_filesystem_stats", AGENT_LOW_TIMEOUT, - fs_path=CONF.mount_point) + fs_path=None) def update_guest(self): """Make a synchronous call to update the guest agent.""" diff --git a/trove/guestagent/backup/backupagent.py b/trove/guestagent/backup/backupagent.py index 6d2ea0c775..0637c4976e 100644 --- a/trove/guestagent/backup/backupagent.py +++ b/trove/guestagent/backup/backupagent.py @@ -86,7 +86,9 @@ class BackupAgent(object): }) # Store the size of the filesystem before the backup. - stats = get_filesystem_volume_stats(CONF.mount_point) + mount_point = CONF.get('mysql' if not CONF.datastore_manager + else CONF.datastore_manager).mount_point + stats = get_filesystem_volume_stats(mount_point) backup = { 'backup_id': backup_id, 'size': stats.get('used', 0.0), diff --git a/trove/guestagent/datastore/cassandra/manager.py b/trove/guestagent/datastore/cassandra/manager.py index dc7aa0180f..5ba2e83cbd 100644 --- a/trove/guestagent/datastore/cassandra/manager.py +++ b/trove/guestagent/datastore/cassandra/manager.py @@ -19,7 +19,6 @@ from trove.common import cfg from trove.common import exception from trove.guestagent import volume from trove.guestagent.datastore.cassandra import service -from trove.guestagent.datastore.cassandra import system from trove.openstack.common import periodic_task from trove.openstack.common import log as logging from trove.openstack.common.gettextutils import _ @@ -30,6 +29,7 @@ LOG = logging.getLogger(__name__) USAGE_SLEEP_TIME = CONF.usage_sleep_time # seconds. USAGE_TIMEOUT = CONF.usage_timeout # seconds. ERROR_MSG = _("Not supported") +MANAGER = CONF.datastore_manager class Manager(periodic_task.PeriodicTasks): @@ -48,8 +48,9 @@ class Manager(periodic_task.PeriodicTasks): def get_filesystem_stats(self, context, fs_path): """Gets the filesystem stats for the path given. """ - return dbaas.get_filesystem_volume_stats( - system.CASSANDRA_MOUNT_POINT) + mount_point = CONF.get( + 'mysql' if not MANAGER else MANAGER).mount_point + return dbaas.get_filesystem_volume_stats(mount_point) def start_db_with_conf_changes(self, context, config_contents): self.app.start_db_with_conf_changes(config_contents) @@ -67,7 +68,7 @@ class Manager(periodic_task.PeriodicTasks): self.appStatus.begin_install() LOG.info("Installing cassandra") self.app.install_if_needed(packages) - self.app.init_storage_structure() + self.app.init_storage_structure(mount_point) if config_contents: LOG.info(_("Config processing")) self.app.write_config(config_contents) @@ -75,11 +76,11 @@ class Manager(periodic_task.PeriodicTasks): if device_path: device = volume.VolumeDevice(device_path) device.format() - if os.path.exists(system.CASSANDRA_MOUNT_POINT): + if os.path.exists(mount_point): #rsync exiting data - device.migrate_data(system.CASSANDRA_MOUNT_POINT) + device.migrate_data(mount_point) #mount the volume - device.mount(system.CASSANDRA_MOUNT_POINT) + device.mount(mount_point) LOG.debug(_("Mounting new volume.")) self.app.restart() diff --git a/trove/guestagent/datastore/cassandra/service.py b/trove/guestagent/datastore/cassandra/service.py index e910a74f8c..c1d9d4a3f9 100644 --- a/trove/guestagent/datastore/cassandra/service.py +++ b/trove/guestagent/datastore/cassandra/service.py @@ -58,8 +58,13 @@ class CassandraApp(object): utils.execute_with_timeout(system.DISABLE_CASSANDRA_ON_BOOT, shell=True) - def init_storage_structure(self): - utils.execute_with_timeout(system.INIT_FS, shell=True) + def init_storage_structure(self, mount_point): + try: + cmd = system.INIT_FS % mount_point + utils.execute_with_timeout(cmd, shell=True) + except exception.ProcessExecutionError as e: + LOG.error(_("Error while initiating storage structure.")) + LOG.error(e) def start_db(self, update_db=False): self._enable_db_on_boot() diff --git a/trove/guestagent/datastore/cassandra/system.py b/trove/guestagent/datastore/cassandra/system.py index 3783f9c89e..6a7edd6d13 100644 --- a/trove/guestagent/datastore/cassandra/system.py +++ b/trove/guestagent/datastore/cassandra/system.py @@ -20,13 +20,11 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF CASSANDRA_DATA_DIR = "/var/lib/cassandra/data" -CASSANDRA_MOUNT_POINT = "/var/lib/cassandra" CASSANDRA_CONF = "/etc/cassandra/cassandra.yaml" CASSANDRA_TEMP_CONF = "/tmp/cassandra.yaml" CASSANDRA_TEMP_DIR = "/tmp/cassandra" -INIT_FS = "sudo mkdir -p %s" % CASSANDRA_MOUNT_POINT - +INIT_FS = "sudo mkdir -p %s" ENABLE_CASSANDRA_ON_BOOT = "sudo update-rc.d cassandra enable" DISABLE_CASSANDRA_ON_BOOT = "sudo update-rc.d cassandra disable" diff --git a/trove/guestagent/datastore/couchbase/manager.py b/trove/guestagent/datastore/couchbase/manager.py index 1629460110..9891dcf3e4 100644 --- a/trove/guestagent/datastore/couchbase/manager.py +++ b/trove/guestagent/datastore/couchbase/manager.py @@ -17,7 +17,6 @@ from trove.common import cfg from trove.common import exception from trove.guestagent import dbaas from trove.guestagent import volume -from trove.guestagent.datastore.couchbase import system from trove.guestagent.datastore.couchbase import service from trove.openstack.common import log as logging from trove.openstack.common import periodic_task @@ -27,6 +26,7 @@ from trove.openstack.common.gettextutils import _ LOG = logging.getLogger(__name__) CONF = cfg.CONF ERROR_MSG = _("Not supported") +MANAGER = CONF.datastore_manager class Manager(periodic_task.PeriodicTasks): @@ -64,7 +64,7 @@ class Manager(periodic_task.PeriodicTasks): if device_path: device = volume.VolumeDevice(device_path) device.format() - device.mount(system.COUCHBASE_MOUNT_POINT) + device.mount(mount_point) LOG.debug(_('Mounted the volume.')) self.app.install_if_needed(packages) LOG.info(_('Securing couchbase now.')) @@ -91,10 +91,10 @@ class Manager(periodic_task.PeriodicTasks): self.app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot) def get_filesystem_stats(self, context, fs_path): - """ - Gets file system stats from the provided fs_path. - """ - return dbaas.get_filesystem_volume_stats(system.COUCHBASE_MOUNT_POINT) + """Gets the filesystem stats for the path given. """ + mount_point = CONF.get( + 'mysql' if not MANAGER else MANAGER).mount_point + return dbaas.get_filesystem_volume_stats(mount_point) def update_attributes(self, context, username, hostname, user_attrs): raise exception.TroveError(ERROR_MSG) diff --git a/trove/guestagent/datastore/couchbase/service.py b/trove/guestagent/datastore/couchbase/service.py index 98365284d7..4d1a3565ad 100644 --- a/trove/guestagent/datastore/couchbase/service.py +++ b/trove/guestagent/datastore/couchbase/service.py @@ -58,12 +58,13 @@ class CouchbaseApp(object): def initial_setup(self): self.ip_address = operating_system.get_ip_address() + mount_point = CONF.get('couchbase').mount_point try: LOG.info(_('Couchbase Server change data dir path')) utils.execute_with_timeout(system.cmd_own_data_dir, shell=True) utils.execute_with_timeout( (system.cmd_node_init - % {'data_path': system.COUCHBASE_MOUNT_POINT, + % {'data_path': mount_point, 'IP': self.ip_address}), shell=True) utils.execute_with_timeout( system.cmd_rm_old_data_dir, shell=True) diff --git a/trove/guestagent/datastore/couchbase/system.py b/trove/guestagent/datastore/couchbase/system.py index f8d9aa011a..75e42b0249 100644 --- a/trove/guestagent/datastore/couchbase/system.py +++ b/trove/guestagent/datastore/couchbase/system.py @@ -12,10 +12,11 @@ # 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 +CONF = cfg.CONF TIME_OUT = 1200 COUCHBASE_CONF_DIR = '/etc/couchbase' -COUCHBASE_MOUNT_POINT = '/var/lib/couchbase' SERVICE_CANDIDATES = ["couchbase-server"] cmd_couchbase_status = ('sudo /opt/couchbase/bin/couchbase-cli server-info ' '-c %(IP)s:8091 -u Administrator -p password') @@ -28,7 +29,7 @@ cmd_cluster_init = ('sudo /opt/couchbase/bin/couchbase-cli cluster-init ' '--cluster-init-port=8091') cmd_kill = 'sudo pkill -9 couchbase-server' cmd_own_data_dir = ('sudo chown couchbase:couchbase %s' % - COUCHBASE_MOUNT_POINT) + CONF.get('couchbase').mount_point) cmd_rm_old_data_dir = 'sudo rm -rf /opt/couchbase/var/lib/couchbase/data' """ For optimal couchbase operations, swappiness of vm should be set to 0. Reference link: http://docs.couchbase.com/couchbase-manual-2 diff --git a/trove/guestagent/datastore/mysql/manager.py b/trove/guestagent/datastore/mysql/manager.py index de907144a2..6bf99da500 100644 --- a/trove/guestagent/datastore/mysql/manager.py +++ b/trove/guestagent/datastore/mysql/manager.py @@ -32,6 +32,7 @@ from trove.openstack.common import periodic_task LOG = logging.getLogger(__name__) CONF = cfg.CONF +MANAGER = CONF.datastore_manager class Manager(periodic_task.PeriodicTasks): @@ -116,16 +117,16 @@ class Manager(periodic_task.PeriodicTasks): app.stop_db() device = volume.VolumeDevice(device_path) device.format() - if os.path.exists(CONF.mount_point): + if os.path.exists(mount_point): #rsync exiting data - device.migrate_data(CONF.mount_point) + device.migrate_data(mount_point) #mount the volume device.mount(mount_point) LOG.debug(_("Mounted the volume.")) app.start_mysql() if backup_info: self._perform_restore(backup_info, context, - CONF.mount_point, app) + mount_point, app) LOG.info(_("Securing mysql now.")) app.secure(config_contents, overrides) enable_root_on_restore = (backup_info and @@ -161,8 +162,10 @@ class Manager(periodic_task.PeriodicTasks): 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 """ - return dbaas.get_filesystem_volume_stats(fs_path) + """Gets the filesystem stats for the path given. """ + mount_point = CONF.get( + 'mysql' if not MANAGER else MANAGER).mount_point + return dbaas.get_filesystem_volume_stats(mount_point) def create_backup(self, context, backup_info): """ diff --git a/trove/guestagent/datastore/redis/manager.py b/trove/guestagent/datastore/redis/manager.py index 4cc18bc341..95d5dc0a5a 100644 --- a/trove/guestagent/datastore/redis/manager.py +++ b/trove/guestagent/datastore/redis/manager.py @@ -18,7 +18,6 @@ from trove.guestagent import dbaas from trove.guestagent import volume from trove.guestagent.datastore.redis.service import RedisAppStatus from trove.guestagent.datastore.redis.service import RedisApp -from trove.guestagent.datastore.redis import system from trove.openstack.common import log as logging from trove.openstack.common.gettextutils import _ from trove.openstack.common import periodic_task @@ -26,6 +25,7 @@ from trove.openstack.common import periodic_task LOG = logging.getLogger(__name__) CONF = cfg.CONF +MANAGER = CONF.datastore_manager class Manager(periodic_task.PeriodicTasks): @@ -77,7 +77,7 @@ class Manager(periodic_task.PeriodicTasks): if device_path: device = volume.VolumeDevice(device_path) device.format() - device.mount(system.REDIS_BASE_DIR) + device.mount(mount_point) LOG.debug(_('Mounted the volume.')) app.install_if_needed(packages) LOG.info(_('Securing redis now.')) @@ -111,10 +111,10 @@ class Manager(periodic_task.PeriodicTasks): app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot) def get_filesystem_stats(self, context, fs_path): - """ - Gets file system stats from the provided fs_path. - """ - return dbaas.get_filesystem_volume_stats(fs_path) + """Gets the filesystem stats for the path given. """ + mount_point = CONF.get( + 'mysql' if not MANAGER else MANAGER).mount_point + return dbaas.get_filesystem_volume_stats(mount_point) def create_backup(self, context, backup_info): """ diff --git a/trove/guestagent/datastore/redis/system.py b/trove/guestagent/datastore/redis/system.py index 92aef64501..fe4b7e3ccf 100644 --- a/trove/guestagent/datastore/redis/system.py +++ b/trove/guestagent/datastore/redis/system.py @@ -29,7 +29,6 @@ REDIS_CMD_DISABLE = 'update-rc.d redis-server disable' REDIS_CMD_START = 'service redis-server start || /bin/true' REDIS_CMD_STOP = 'service redis-server stop || /bin/true' REDIS_PACKAGE = 'redis-server' -REDIS_BASE_DIR = '/var/lib/redis' if OS is 'redhat': REDIS_BIN = '/usr/libexec/redis-server' diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index 5bcf2192db..7da136e0c6 100644 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -369,7 +369,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): self._log_and_raise(e, msg, err) device_path = CONF.device_path - mount_point = CONF.mount_point + mount_point = CONF.get(datastore_manager).mount_point volume_info = {'device_path': device_path, 'mount_point': mount_point} LOG.debug(_("end _create_server_volume for id: %s") % self.id) return volume_info @@ -441,7 +441,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): self._log_and_raise(e, msg, err) device_path = CONF.device_path - mount_point = CONF.mount_point + mount_point = CONF.get(datastore_manager).mount_point volume_info = {'device_path': device_path, 'mount_point': mount_point} LOG.debug(_("end _create_server_volume_heat for id: %s") % self.id) @@ -454,7 +454,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): LOG.debug(_("begin _create_server_volume_individually for id: %s") % self.id) server = None - volume_info = self._build_volume_info(volume_size) + volume_info = self._build_volume_info(datastore_manager, + volume_size=volume_size) block_device_mapping = volume_info['block_device'] try: server = self._create_server(flavor_id, image_id, security_groups, @@ -472,24 +473,26 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): self.id) return volume_info - def _build_volume_info(self, volume_size=None): + def _build_volume_info(self, datastore_manager, volume_size=None): volume_info = None volume_support = CONF.trove_volume_support LOG.debug(_("trove volume support = %s") % volume_support) if volume_support: try: - volume_info = self._create_volume(volume_size) + volume_info = self._create_volume( + volume_size, datastore_manager) except Exception as e: msg = _("Error provisioning volume for instance: %s") % self.id err = inst_models.InstanceTasks.BUILDING_ERROR_VOLUME self._log_and_raise(e, msg, err) else: LOG.debug(_("device_path = %s") % CONF.device_path) - LOG.debug(_("mount_point = %s") % CONF.mount_point) + LOG.debug(_("mount_point = %s") % + CONF.get(datastore_manager).mount_point) volume_info = { 'block_device': None, 'device_path': CONF.device_path, - 'mount_point': CONF.mount_point, + 'mount_point': CONF.get(datastore_manager).mount_point, 'volumes': None, } return volume_info @@ -501,7 +504,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): self.update_db(task_status=task_status) raise TroveError(message=message) - def _create_volume(self, volume_size): + def _create_volume(self, volume_size, datastore_manager): LOG.info("Entering create_volume") LOG.debug(_("begin _create_volume for id: %s") % self.id) volume_client = create_cinder_client(self.context) @@ -522,9 +525,9 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): if v_ref.status in ['error']: raise VolumeCreationFailure() LOG.debug(_("end _create_volume for id: %s") % self.id) - return self._build_volume(v_ref) + return self._build_volume(v_ref, datastore_manager) - def _build_volume(self, v_ref): + def _build_volume(self, v_ref, datastore_manager): LOG.debug(_("Created volume %s") % v_ref) # The mapping is in the format: # :[]:[]:[] @@ -538,7 +541,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): LOG.debug("volume = %s" % created_volumes) device_path = CONF.device_path - mount_point = CONF.mount_point + mount_point = CONF.get(datastore_manager).mount_point LOG.debug(_("device_path = %s") % device_path) LOG.debug(_("mount_point = %s") % mount_point) @@ -979,6 +982,11 @@ class ResizeVolumeAction(ConfigurationMixin): self.old_size = int(old_size) self.new_size = int(new_size) + def get_mount_point(self): + mount_point = CONF.get( + self.instance.datastore_version.manager).mount_point + return mount_point + def _fail(self, orig_func): LOG.exception(_("%(func)s encountered an error when attempting to " "resize the volume for instance %(id)s. Setting service " @@ -1023,8 +1031,9 @@ class ResizeVolumeAction(ConfigurationMixin): def _unmount_volume(self): LOG.debug(_("Unmounting the volume on instance %(id)s") % { 'id': self.instance.id}) + mount_point = self.get_mount_point() self.instance.guest.unmount_volume(device_path=CONF.device_path, - mount_point=CONF.mount_point) + mount_point=mount_point) LOG.debug(_("Successfully unmounted the volume %(vol_id)s for " "instance %(id)s") % {'vol_id': self.instance.volume_id, 'id': self.instance.id}) @@ -1073,8 +1082,9 @@ class ResizeVolumeAction(ConfigurationMixin): def _resize_fs(self): LOG.debug(_("Resizing the filesystem for instance %(id)s") % { 'id': self.instance.id}) + mount_point = self.get_mount_point() self.instance.guest.resize_fs(device_path=CONF.device_path, - mount_point=CONF.mount_point) + mount_point=mount_point) LOG.debug(_("Successfully resized volume %(vol_id)s filesystem for " "instance %(id)s") % {'vol_id': self.instance.volume_id, 'id': self.instance.id}) @@ -1083,8 +1093,9 @@ class ResizeVolumeAction(ConfigurationMixin): def _mount_volume(self): LOG.debug(_("Mount the volume on instance %(id)s") % { 'id': self.instance.id}) + mount_point = self.get_mount_point() self.instance.guest.mount_volume(device_path=CONF.device_path, - mount_point=CONF.mount_point) + mount_point=mount_point) LOG.debug(_("Successfully mounted the volume %(vol_id)s on instance " "%(id)s") % {'vol_id': self.instance.volume_id, 'id': self.instance.id}) diff --git a/trove/tests/unittests/guestagent/test_cassandra_manager.py b/trove/tests/unittests/guestagent/test_cassandra_manager.py new file mode 100644 index 0000000000..62a6391afc --- /dev/null +++ b/trove/tests/unittests/guestagent/test_cassandra_manager.py @@ -0,0 +1,140 @@ +# 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 os + +import testtools +from mock import Mock +from mockito import verify, when, unstub, any, mock +from trove.common.context import TroveContext +from trove.common.instance import ServiceStatuses +from trove.guestagent import volume +from trove.guestagent.common import operating_system +from trove.guestagent.datastore.cassandra import service as cass_service +from trove.guestagent.datastore.cassandra import manager as cass_manager +from trove.guestagent import pkg + + +class GuestAgentCassandraDBManagerTest(testtools.TestCase): + + def setUp(self): + super(GuestAgentCassandraDBManagerTest, self).setUp() + self.real_status = cass_service.CassandraAppStatus.set_status + + class FakeInstanceServiceStatus(object): + status = ServiceStatuses.NEW + + def save(self): + pass + + cass_service.CassandraAppStatus.set_status = Mock( + return_value=FakeInstanceServiceStatus()) + self.context = TroveContext() + self.manager = cass_manager.Manager() + self.pkg = cass_service.packager + self.real_db_app_status = cass_service.CassandraAppStatus + self.origin_os_path_exists = os.path.exists + self.origin_format = volume.VolumeDevice.format + self.origin_migrate_data = volume.VolumeDevice.migrate_data + self.origin_mount = volume.VolumeDevice.mount + self.origin_stop_db = cass_service.CassandraApp.stop_db + self.origin_start_db = cass_service.CassandraApp.start_db + self.origin_install_db = cass_service.CassandraApp._install_db + self.original_get_ip = operating_system.get_ip_address + self.orig_make_host_reachable = ( + cass_service.CassandraApp.make_host_reachable) + + def tearDown(self): + super(GuestAgentCassandraDBManagerTest, self).tearDown() + cass_service.packager = self.pkg + cass_service.CassandraAppStatus.set_status = self.real_db_app_status + os.path.exists = self.origin_os_path_exists + volume.VolumeDevice.format = self.origin_format + volume.VolumeDevice.migrate_data = self.origin_migrate_data + volume.VolumeDevice.mount = self.origin_mount + cass_service.CassandraApp.stop_db = self.origin_stop_db + cass_service.CassandraApp.start_db = self.origin_start_db + cass_service.CassandraApp._install_db = self.origin_install_db + operating_system.get_ip_address = self.original_get_ip + cass_service.CassandraApp.make_host_reachable = ( + self.orig_make_host_reachable) + unstub() + + def test_update_status(self): + mock_status = mock() + self.manager.appStatus = mock_status + self.manager.update_status(self.context) + verify(mock_status).update() + + def test_prepare_pkg(self): + self._prepare_dynamic(['cassandra']) + + def test_prepare_no_pkg(self): + self._prepare_dynamic([]) + + def test_prepare_db_not_installed(self): + self._prepare_dynamic([], is_db_installed=False) + + def test_prepare_db_not_installed_no_package(self): + self._prepare_dynamic([], + is_db_installed=True) + + def _prepare_dynamic(self, packages, + config_content=any(), device_path='/dev/vdb', + is_db_installed=True, backup_id=None, + is_root_enabled=False, + overrides=None): + # covering all outcomes is starting to cause trouble here + if not backup_id: + backup_info = {'id': backup_id, + 'location': 'fake-location', + 'type': 'InnoBackupEx', + 'checksum': 'fake-checksum', + } + + mock_status = mock() + self.manager.appStatus = mock_status + when(mock_status).begin_install().thenReturn(None) + + mock_app = mock() + self.manager.app = mock_app + + when(mock_app).install_if_needed(packages).thenReturn(None) + (when(pkg.Package).pkg_is_installed(any()). + thenReturn(is_db_installed)) + when(mock_app).init_storage_structure(any()).thenReturn(None) + when(mock_app).write_config(config_content).thenReturn(None) + when(mock_app).make_host_reachable().thenReturn(None) + when(mock_app).restart().thenReturn(None) + when(os.path).exists(any()).thenReturn(True) + + when(volume.VolumeDevice).format().thenReturn(None) + when(volume.VolumeDevice).migrate_data(any()).thenReturn(None) + when(volume.VolumeDevice).mount().thenReturn(None) + + # invocation + self.manager.prepare(context=self.context, packages=packages, + config_contents=config_content, + databases=None, + memory_mb='2048', users=None, + device_path=device_path, + mount_point="/var/lib/cassandra", + backup_info=backup_info, + overrides=None) + # verification/assertion + verify(mock_status).begin_install() + verify(mock_app).install_if_needed(packages) + verify(mock_app).init_storage_structure(any()) + verify(mock_app).make_host_reachable() + verify(mock_app).restart() diff --git a/trove/tests/unittests/guestagent/test_couchbase_manager.py b/trove/tests/unittests/guestagent/test_couchbase_manager.py new file mode 100644 index 0000000000..8416ad0698 --- /dev/null +++ b/trove/tests/unittests/guestagent/test_couchbase_manager.py @@ -0,0 +1,95 @@ +# 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 testtools +from mock import Mock +from mockito import verify, when, unstub, any, mock +from trove.common.context import TroveContext +from trove.guestagent import volume +from trove.guestagent.common import operating_system +from trove.guestagent.datastore.couchbase import service as couch_service +from trove.guestagent.datastore.couchbase import manager as couch_manager + + +class GuestAgentCouchbaseManagerTest(testtools.TestCase): + + def setUp(self): + super(GuestAgentCouchbaseManagerTest, self).setUp() + self.context = TroveContext() + self.manager = couch_manager.Manager() + self.packages = 'couchbase-server' + self.origin_CouchbaseAppStatus = couch_service.CouchbaseAppStatus + self.origin_format = volume.VolumeDevice.format + self.origin_mount = volume.VolumeDevice.mount + self.origin_stop_db = couch_service.CouchbaseApp.stop_db + self.origin_start_db = couch_service.CouchbaseApp.start_db + operating_system.get_ip_address = Mock() + + def tearDown(self): + super(GuestAgentCouchbaseManagerTest, self).tearDown() + couch_service.CouchbaseAppStatus = self.origin_CouchbaseAppStatus + volume.VolumeDevice.format = self.origin_format + volume.VolumeDevice.mount = self.origin_mount + couch_service.CouchbaseApp.stop_db = self.origin_stop_db + couch_service.CouchbaseApp.start_db = self.origin_start_db + unstub() + + def test_update_status(self): + mock_status = mock() + self.manager.appStatus = mock_status + self.manager.update_status(self.context) + verify(mock_status).update() + + def test_prepare_device_path_true(self): + self._prepare_dynamic() + + def _prepare_dynamic(self, device_path='/dev/vdb', is_db_installed=True, + backup_info=None): + mock_status = mock() + self.manager.appStatus = mock_status + when(mock_status).begin_install().thenReturn(None) + when(volume.VolumeDevice).format().thenReturn(None) + when(volume.VolumeDevice).mount().thenReturn(None) + when(couch_service.CouchbaseApp).install_if_needed().thenReturn(None) + when(couch_service.CouchbaseApp).complete_install_or_restart( + any()).thenReturn(None) + #invocation + self.manager.prepare(self.context, self.packages, None, 2048, + None, device_path=device_path, + mount_point='/var/lib/couchbase', + backup_info=backup_info) + #verification/assertion + verify(mock_status).begin_install() + verify(couch_service.CouchbaseApp).install_if_needed(self.packages) + verify(couch_service.CouchbaseApp).complete_install_or_restart() + + def test_restart(self): + mock_status = mock() + self.manager.appStatus = mock_status + when(couch_service.CouchbaseApp).restart().thenReturn(None) + #invocation + self.manager.restart(self.context) + #verification/assertion + verify(couch_service.CouchbaseApp).restart() + + def test_stop_db(self): + mock_status = mock() + self.manager.appStatus = mock_status + when(couch_service.CouchbaseApp).stop_db( + do_not_start_on_reboot=False).thenReturn(None) + #invocation + self.manager.stop_db(self.context) + #verification/assertion + verify(couch_service.CouchbaseApp).stop_db( + do_not_start_on_reboot=False) diff --git a/trove/tests/unittests/guestagent/test_dbaas.py b/trove/tests/unittests/guestagent/test_dbaas.py index 889e285902..9aeee61006 100644 --- a/trove/tests/unittests/guestagent/test_dbaas.py +++ b/trove/tests/unittests/guestagent/test_dbaas.py @@ -25,7 +25,6 @@ from mockito import verify from mockito import contains from mockito import never from mockito import matchers -from mockito import inorder, verifyNoMoreInteractions import sqlalchemy import testtools from testtools.matchers import Is @@ -792,25 +791,6 @@ class MySqlAppMockTest(testtools.TestCase): super(MySqlAppMockTest, self).tearDown() unstub() - def test_secure_with_mycnf_error(self): - mock_conn = mock_sql_connection() - when(mock_conn).execute(any()).thenReturn(None) - when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn( - None) - # skip writing the file for now - when(os.path).isfile(any()).thenReturn(False) - mock_status = mock() - when(mock_status).wait_for_real_status_to_change_to( - any(), any(), any()).thenReturn(True) - app = MySqlApp(mock_status) - when(dbaas).clear_expired_password().thenReturn(None) - self.assertRaises(TypeError, app.secure, None, None) - - verify(mock_conn, atleast=2).execute(any()) - inorder.verify(mock_status).wait_for_real_status_to_change_to( - rd_instance.ServiceStatuses.SHUTDOWN, any(), any()) - verifyNoMoreInteractions(mock_status) - def test_secure_keep_root(self): mock_conn = mock_sql_connection() diff --git a/trove/tests/unittests/guestagent/test_manager.py b/trove/tests/unittests/guestagent/test_manager.py deleted file mode 100644 index cfce2ff519..0000000000 --- a/trove/tests/unittests/guestagent/test_manager.py +++ /dev/null @@ -1,497 +0,0 @@ -# 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 os - -import testtools -from mock import Mock -from mockito import verify, when, unstub, any, mock, never -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.common import operating_system -from trove.guestagent.datastore.cassandra import service as cass_service -from trove.guestagent.datastore.cassandra import manager as cass_manager -from trove.guestagent.datastore.mysql.manager import Manager -import trove.guestagent.datastore.mysql.service as dbaas -from trove.guestagent.datastore.couchbase import service as couch_service -from trove.guestagent.datastore.couchbase import manager as couch_manager -from trove.guestagent.datastore.redis.manager import Manager as RedisManager -import trove.guestagent.datastore.redis.service as redis_service -import trove.guestagent.datastore.redis.system as redis_system -from trove.guestagent import backup -from trove.guestagent.volume import VolumeDevice -from trove.guestagent import pkg - - -class GuestAgentManagerTest(testtools.TestCase): - def setUp(self): - super(GuestAgentManagerTest, self).setUp() - self.context = TroveContext() - self.manager = Manager() - self.origin_MySqlAppStatus = dbaas.MySqlAppStatus - self.origin_os_path_exists = os.path.exists - self.origin_format = volume.VolumeDevice.format - self.origin_migrate_data = volume.VolumeDevice.migrate_data - self.origin_mount = volume.VolumeDevice.mount - self.origin_stop_mysql = dbaas.MySqlApp.stop_db - self.origin_start_mysql = dbaas.MySqlApp.start_mysql - self.origin_pkg_is_installed = pkg.Package.pkg_is_installed - self.origin_os_path_exists = os.path.exists - - def tearDown(self): - super(GuestAgentManagerTest, self).tearDown() - dbaas.MySqlAppStatus = self.origin_MySqlAppStatus - os.path.exists = self.origin_os_path_exists - volume.VolumeDevice.format = self.origin_format - volume.VolumeDevice.migrate_data = self.origin_migrate_data - volume.VolumeDevice.mount = self.origin_mount - dbaas.MySqlApp.stop_db = self.origin_stop_mysql - dbaas.MySqlApp.start_mysql = self.origin_start_mysql - pkg.Package.pkg_is_installed = self.origin_pkg_is_installed - os.path.exists = self.origin_os_path_exists - unstub() - - def test_update_status(self): - mock_status = mock() - when(dbaas.MySqlAppStatus).get().thenReturn(mock_status) - self.manager.update_status(self.context) - verify(dbaas.MySqlAppStatus).get() - verify(mock_status).update() - - def test_create_database(self): - when(dbaas.MySqlAdmin).create_database(['db1']).thenReturn(None) - self.manager.create_database(self.context, ['db1']) - verify(dbaas.MySqlAdmin).create_database(['db1']) - - def test_create_user(self): - when(dbaas.MySqlAdmin).create_user(['user1']).thenReturn(None) - self.manager.create_user(self.context, ['user1']) - verify(dbaas.MySqlAdmin).create_user(['user1']) - - def test_delete_database(self): - databases = ['db1'] - when(dbaas.MySqlAdmin).delete_database(databases).thenReturn(None) - self.manager.delete_database(self.context, databases) - verify(dbaas.MySqlAdmin).delete_database(databases) - - def test_delete_user(self): - user = ['user1'] - when(dbaas.MySqlAdmin).delete_user(user).thenReturn(None) - self.manager.delete_user(self.context, user) - verify(dbaas.MySqlAdmin).delete_user(user) - - def test_grant_access(self): - username = "test_user" - hostname = "test_host" - databases = ["test_database"] - when(dbaas.MySqlAdmin).grant_access(username, - hostname, - databases).thenReturn(None) - - self.manager.grant_access(self.context, - username, - hostname, - databases) - - verify(dbaas.MySqlAdmin).grant_access(username, hostname, databases) - - def test_list_databases(self): - when(dbaas.MySqlAdmin).list_databases(None, None, - False).thenReturn(['database1']) - databases = self.manager.list_databases(self.context) - self.assertThat(databases, Not(Is(None))) - self.assertThat(databases, Equals(['database1'])) - verify(dbaas.MySqlAdmin).list_databases(None, None, False) - - def test_list_users(self): - when(dbaas.MySqlAdmin).list_users(None, None, - False).thenReturn(['user1']) - users = self.manager.list_users(self.context) - self.assertThat(users, Equals(['user1'])) - verify(dbaas.MySqlAdmin).list_users(None, None, False) - - def test_get_users(self): - username = ['user1'] - hostname = ['host'] - when(dbaas.MySqlAdmin).get_user(username, - hostname).thenReturn(['user1']) - users = self.manager.get_user(self.context, username, hostname) - self.assertThat(users, Equals(['user1'])) - verify(dbaas.MySqlAdmin).get_user(username, hostname) - - def test_enable_root(self): - when(dbaas.MySqlAdmin).enable_root().thenReturn('user_id_stuff') - user_id = self.manager.enable_root(self.context) - self.assertThat(user_id, Is('user_id_stuff')) - verify(dbaas.MySqlAdmin).enable_root() - - def test_is_root_enabled(self): - when(dbaas.MySqlAdmin).is_root_enabled().thenReturn(True) - is_enabled = self.manager.is_root_enabled(self.context) - self.assertThat(is_enabled, Is(True)) - verify(dbaas.MySqlAdmin).is_root_enabled() - - def test_create_backup(self): - when(backup).backup(self.context, 'backup_id_123').thenReturn(None) - # entry point - Manager().create_backup(self.context, 'backup_id_123') - # assertions - verify(backup).backup(self.context, 'backup_id_123') - - 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_mysql_not_installed(self): - self._prepare_dynamic(is_mysql_installed=False) - - def test_prepare_mysql_from_backup(self): - self._prepare_dynamic(backup_id='backup_id_123abc') - - def test_prepare_mysql_from_backup_with_root(self): - self._prepare_dynamic(backup_id='backup_id_123abc', - is_root_enabled=True) - - def _prepare_dynamic(self, device_path='/dev/vdb', is_mysql_installed=True, - backup_id=None, is_root_enabled=False, - overrides=None): - # covering all outcomes is starting to cause trouble here - COUNT = 1 if device_path else 0 - backup_info = None - if backup_id is not None: - backup_info = {'id': backup_id, - 'location': 'fake-location', - 'type': 'InnoBackupEx', - 'checksum': 'fake-checksum', - } - - # TODO(juice): this should stub an instance of the MySqlAppStatus - mock_status = mock() - when(dbaas.MySqlAppStatus).get().thenReturn(mock_status) - when(mock_status).begin_install().thenReturn(None) - when(VolumeDevice).format().thenReturn(None) - when(VolumeDevice).migrate_data(any()).thenReturn(None) - when(VolumeDevice).mount().thenReturn(None) - when(dbaas.MySqlApp).stop_db().thenReturn(None) - when(dbaas.MySqlApp).start_mysql().thenReturn(None) - when(dbaas.MySqlApp).install_if_needed(any()).thenReturn(None) - when(backup).restore(self.context, - backup_info, - '/var/lib/mysql').thenReturn(None) - when(dbaas.MySqlApp).secure(any()).thenReturn(None) - when(dbaas.MySqlApp).secure_root(any()).thenReturn(None) - (when(pkg.Package).pkg_is_installed(any()). - thenReturn(is_mysql_installed)) - when(dbaas.MySqlAdmin).is_root_enabled().thenReturn(is_root_enabled) - when(dbaas.MySqlAdmin).create_user().thenReturn(None) - when(dbaas.MySqlAdmin).create_database().thenReturn(None) - - when(os.path).exists(any()).thenReturn(True) - # invocation - self.manager.prepare(context=self.context, - packages=None, - memory_mb='2048', - databases=None, - users=None, - device_path=device_path, - mount_point='/var/lib/mysql', - backup_info=backup_info, - overrides=overrides) - # verification/assertion - verify(mock_status).begin_install() - - verify(VolumeDevice, times=COUNT).format() - verify(dbaas.MySqlApp, times=COUNT).stop_db() - verify(VolumeDevice, times=COUNT).migrate_data( - any()) - if backup_info: - verify(backup).restore(self.context, backup_info, '/var/lib/mysql') - verify(dbaas.MySqlApp).install_if_needed(any()) - # We dont need to make sure the exact contents are there - verify(dbaas.MySqlApp).secure(any(), overrides) - verify(dbaas.MySqlAdmin, never).create_database() - verify(dbaas.MySqlAdmin, never).create_user() - verify(dbaas.MySqlApp).secure_root(secure_remote_root=any()) - - -class RedisGuestAgentManagerTest(testtools.TestCase): - - def setUp(self): - super(RedisGuestAgentManagerTest, self).setUp() - self.context = TroveContext() - self.manager = RedisManager() - self.packages = 'redis-server' - self.origin_RedisAppStatus = redis_service.RedisAppStatus - self.origin_stop_redis = redis_service.RedisApp.stop_db - self.origin_start_redis = redis_service.RedisApp.start_redis - self.origin_install_redis = redis_service.RedisApp._install_redis - - def tearDown(self): - super(RedisGuestAgentManagerTest, self).tearDown() - redis_service.RedisAppStatus = self.origin_RedisAppStatus - redis_service.RedisApp.stop_db = self.origin_stop_redis - redis_service.RedisApp.start_redis = self.origin_start_redis - redis_service.RedisApp._install_redis = self.origin_install_redis - unstub() - - def test_update_status(self): - mock_status = mock() - when(redis_service.RedisAppStatus).get().thenReturn(mock_status) - self.manager.update_status(self.context) - verify(redis_service.RedisAppStatus).get() - verify(mock_status).update() - - def test_prepare_device_path_true(self): - self._prepare_dynamic() - - def test_prepare_device_path_false(self): - self._prepare_dynamic(device_path=None) - - def test_prepare_redis_not_installed(self): - self._prepare_dynamic(is_redis_installed=False) - - def _prepare_dynamic(self, device_path='/dev/vdb', is_redis_installed=True, - backup_info=None, is_root_enabled=False): - - # covering all outcomes is starting to cause trouble here - dev_path = 1 if device_path else 0 - mock_status = mock() - when(redis_service.RedisAppStatus).get().thenReturn(mock_status) - when(mock_status).begin_install().thenReturn(None) - when(VolumeDevice).format().thenReturn(None) - when(VolumeDevice).mount().thenReturn(None) - when(redis_service.RedisApp).start_redis().thenReturn(None) - when(redis_service.RedisApp).install_if_needed().thenReturn(None) - when(backup).restore(self.context, backup_info).thenReturn(None) - when(redis_service.RedisApp).write_config(any()).thenReturn(None) - when(redis_service.RedisApp).complete_install_or_restart( - any()).thenReturn(None) - self.manager.prepare(self.context, self.packages, - None, '2048', - None, device_path=device_path, - mount_point='/var/lib/redis', - backup_info=backup_info) - verify(redis_service.RedisAppStatus, times=2).get() - verify(mock_status).begin_install() - verify(VolumeDevice, times=dev_path).mount(redis_system.REDIS_BASE_DIR) - verify(redis_service.RedisApp).install_if_needed(self.packages) - verify(redis_service.RedisApp).write_config(None) - verify(redis_service.RedisApp).complete_install_or_restart() - - def test_restart(self): - mock_status = mock() - when(redis_service.RedisAppStatus).get().thenReturn(mock_status) - when(redis_service.RedisApp).restart().thenReturn(None) - self.manager.restart(self.context) - verify(redis_service.RedisAppStatus).get() - verify(redis_service.RedisApp).restart() - - def test_stop_db(self): - mock_status = mock() - when(redis_service.RedisAppStatus).get().thenReturn(mock_status) - when(redis_service.RedisApp).stop_db(do_not_start_on_reboot= - False).thenReturn(None) - self.manager.stop_db(self.context) - verify(redis_service.RedisAppStatus).get() - verify(redis_service.RedisApp).stop_db(do_not_start_on_reboot=False) - - -class GuestAgentCassandraDBManagerTest(testtools.TestCase): - - def setUp(self): - super(GuestAgentCassandraDBManagerTest, self).setUp() - self.real_status = cass_service.CassandraAppStatus.set_status - - class FakeInstanceServiceStatus(object): - status = ServiceStatuses.NEW - - def save(self): - pass - - cass_service.CassandraAppStatus.set_status = Mock( - return_value=FakeInstanceServiceStatus()) - self.context = TroveContext() - self.manager = cass_manager.Manager() - self.pkg = cass_service.packager - self.real_db_app_status = cass_service.CassandraAppStatus - self.origin_os_path_exists = os.path.exists - self.origin_format = volume.VolumeDevice.format - self.origin_migrate_data = volume.VolumeDevice.migrate_data - self.origin_mount = volume.VolumeDevice.mount - self.origin_stop_db = cass_service.CassandraApp.stop_db - self.origin_start_db = cass_service.CassandraApp.start_db - self.origin_install_db = cass_service.CassandraApp._install_db - self.original_get_ip = operating_system.get_ip_address - self.orig_make_host_reachable = ( - cass_service.CassandraApp.make_host_reachable) - - def tearDown(self): - super(GuestAgentCassandraDBManagerTest, self).tearDown() - cass_service.packager = self.pkg - cass_service.CassandraAppStatus.set_status = self.real_db_app_status - os.path.exists = self.origin_os_path_exists - volume.VolumeDevice.format = self.origin_format - volume.VolumeDevice.migrate_data = self.origin_migrate_data - volume.VolumeDevice.mount = self.origin_mount - cass_service.CassandraApp.stop_db = self.origin_stop_db - cass_service.CassandraApp.start_db = self.origin_start_db - cass_service.CassandraApp._install_db = self.origin_install_db - operating_system.get_ip_address = self.original_get_ip - cass_service.CassandraApp.make_host_reachable = ( - self.orig_make_host_reachable) - - def test_update_status(self): - mock_status = mock() - self.manager.appStatus = mock_status - self.manager.update_status(self.context) - verify(mock_status).update() - - def test_prepare_pkg(self): - self._prepare_dynamic(['cassandra']) - - def test_prepare_no_pkg(self): - self._prepare_dynamic([]) - - def test_prepare_db_not_installed(self): - self._prepare_dynamic([], is_db_installed=False) - - def test_prepare_db_not_installed_no_package(self): - self._prepare_dynamic([], - is_db_installed=True) - - def _prepare_dynamic(self, packages, - config_content=any(), device_path='/dev/vdb', - is_db_installed=True, backup_id=None, - is_root_enabled=False, - overrides=None): - # covering all outcomes is starting to cause trouble here - if not backup_id: - backup_info = {'id': backup_id, - 'location': 'fake-location', - 'type': 'InnoBackupEx', - 'checksum': 'fake-checksum', - } - - mock_status = mock() - self.manager.appStatus = mock_status - when(mock_status).begin_install().thenReturn(None) - - mock_app = mock() - self.manager.app = mock_app - - when(mock_app).install_if_needed(packages).thenReturn(None) - (when(pkg.Package).pkg_is_installed(any()). - thenReturn(is_db_installed)) - when(mock_app).init_storage_structure().thenReturn(None) - when(mock_app).write_config(config_content).thenReturn(None) - when(mock_app).make_host_reachable().thenReturn(None) - when(mock_app).restart().thenReturn(None) - when(os.path).exists(any()).thenReturn(True) - - when(volume.VolumeDevice).format().thenReturn(None) - when(volume.VolumeDevice).migrate_data(any()).thenReturn(None) - when(volume.VolumeDevice).mount().thenReturn(None) - - # invocation - self.manager.prepare(context=self.context, packages=packages, - config_contents=config_content, - databases=None, - memory_mb='2048', users=None, - device_path=device_path, - mount_point="/var/lib/cassandra", - backup_info=backup_info, - overrides=None) - # verification/assertion - verify(mock_status).begin_install() - verify(mock_app).install_if_needed(packages) - verify(mock_app).init_storage_structure() - verify(mock_app).make_host_reachable() - verify(mock_app).restart() - - -class GuestAgentCouchbaseManagerTest(testtools.TestCase): - - def setUp(self): - super(GuestAgentCouchbaseManagerTest, self).setUp() - self.context = TroveContext() - self.manager = couch_manager.Manager() - self.packages = 'couchbase-server' - self.origin_CouchbaseAppStatus = couch_service.CouchbaseAppStatus - self.origin_format = volume.VolumeDevice.format - self.origin_mount = volume.VolumeDevice.mount - self.origin_stop_db = couch_service.CouchbaseApp.stop_db - self.origin_start_db = couch_service.CouchbaseApp.start_db - operating_system.get_ip_address = Mock() - - def tearDown(self): - super(GuestAgentCouchbaseManagerTest, self).tearDown() - couch_service.CouchbaseAppStatus = self.origin_CouchbaseAppStatus - volume.VolumeDevice.format = self.origin_format - volume.VolumeDevice.mount = self.origin_mount - couch_service.CouchbaseApp.stop_db = self.origin_stop_db - couch_service.CouchbaseApp.start_db = self.origin_start_db - unstub() - - def test_update_status(self): - mock_status = mock() - self.manager.appStatus = mock_status - self.manager.update_status(self.context) - verify(mock_status).update() - - def test_prepare_device_path_true(self): - self._prepare_dynamic() - - def _prepare_dynamic(self, device_path='/dev/vdb', is_db_installed=True, - backup_info=None): - mock_status = mock() - self.manager.appStatus = mock_status - when(mock_status).begin_install().thenReturn(None) - when(volume.VolumeDevice).format().thenReturn(None) - when(volume.VolumeDevice).mount().thenReturn(None) - when(couch_service.CouchbaseApp).install_if_needed().thenReturn(None) - when(couch_service.CouchbaseApp).complete_install_or_restart( - any()).thenReturn(None) - #invocation - self.manager.prepare(self.context, self.packages, None, 2048, - None, device_path=device_path, - mount_point='/var/lib/couchbase', - backup_info=backup_info) - #verification/assertion - verify(mock_status).begin_install() - verify(couch_service.CouchbaseApp).install_if_needed(self.packages) - verify(couch_service.CouchbaseApp).complete_install_or_restart() - - def test_restart(self): - mock_status = mock() - self.manager.appStatus = mock_status - when(couch_service.CouchbaseApp).restart().thenReturn(None) - #invocation - self.manager.restart(self.context) - #verification/assertion - verify(couch_service.CouchbaseApp).restart() - - def test_stop_db(self): - mock_status = mock() - self.manager.appStatus = mock_status - when(couch_service.CouchbaseApp).stop_db( - do_not_start_on_reboot=False).thenReturn(None) - #invocation - self.manager.stop_db(self.context) - #verification/assertion - verify(couch_service.CouchbaseApp).stop_db( - do_not_start_on_reboot=False) diff --git a/trove/tests/unittests/guestagent/test_mysql_manager.py b/trove/tests/unittests/guestagent/test_mysql_manager.py new file mode 100644 index 0000000000..ee0157fc46 --- /dev/null +++ b/trove/tests/unittests/guestagent/test_mysql_manager.py @@ -0,0 +1,219 @@ +# 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 os + +import testtools +from mockito import verify, when, unstub, any, mock, never +from testtools.matchers import Is, Equals, Not +from trove.common.context import TroveContext +from trove.guestagent import volume +from trove.guestagent.datastore.mysql.manager import Manager +import trove.guestagent.datastore.mysql.service as dbaas +from trove.guestagent import backup +from trove.guestagent.volume import VolumeDevice +from trove.guestagent import pkg + + +class GuestAgentManagerTest(testtools.TestCase): + def setUp(self): + super(GuestAgentManagerTest, self).setUp() + self.context = TroveContext() + self.manager = Manager() + self.origin_MySqlAppStatus = dbaas.MySqlAppStatus + self.origin_os_path_exists = os.path.exists + self.origin_format = volume.VolumeDevice.format + self.origin_migrate_data = volume.VolumeDevice.migrate_data + self.origin_mount = volume.VolumeDevice.mount + self.origin_stop_mysql = dbaas.MySqlApp.stop_db + self.origin_start_mysql = dbaas.MySqlApp.start_mysql + self.origin_pkg_is_installed = pkg.Package.pkg_is_installed + self.origin_os_path_exists = os.path.exists + + def tearDown(self): + super(GuestAgentManagerTest, self).tearDown() + dbaas.MySqlAppStatus = self.origin_MySqlAppStatus + os.path.exists = self.origin_os_path_exists + volume.VolumeDevice.format = self.origin_format + volume.VolumeDevice.migrate_data = self.origin_migrate_data + volume.VolumeDevice.mount = self.origin_mount + dbaas.MySqlApp.stop_db = self.origin_stop_mysql + dbaas.MySqlApp.start_mysql = self.origin_start_mysql + pkg.Package.pkg_is_installed = self.origin_pkg_is_installed + os.path.exists = self.origin_os_path_exists + unstub() + + def test_update_status(self): + mock_status = mock() + when(dbaas.MySqlAppStatus).get().thenReturn(mock_status) + self.manager.update_status(self.context) + verify(dbaas.MySqlAppStatus).get() + verify(mock_status).update() + + def test_create_database(self): + when(dbaas.MySqlAdmin).create_database(['db1']).thenReturn(None) + self.manager.create_database(self.context, ['db1']) + verify(dbaas.MySqlAdmin).create_database(['db1']) + + def test_create_user(self): + when(dbaas.MySqlAdmin).create_user(['user1']).thenReturn(None) + self.manager.create_user(self.context, ['user1']) + verify(dbaas.MySqlAdmin).create_user(['user1']) + + def test_delete_database(self): + databases = ['db1'] + when(dbaas.MySqlAdmin).delete_database(databases).thenReturn(None) + self.manager.delete_database(self.context, databases) + verify(dbaas.MySqlAdmin).delete_database(databases) + + def test_delete_user(self): + user = ['user1'] + when(dbaas.MySqlAdmin).delete_user(user).thenReturn(None) + self.manager.delete_user(self.context, user) + verify(dbaas.MySqlAdmin).delete_user(user) + + def test_grant_access(self): + username = "test_user" + hostname = "test_host" + databases = ["test_database"] + when(dbaas.MySqlAdmin).grant_access(username, + hostname, + databases).thenReturn(None) + + self.manager.grant_access(self.context, + username, + hostname, + databases) + + verify(dbaas.MySqlAdmin).grant_access(username, hostname, databases) + + def test_list_databases(self): + when(dbaas.MySqlAdmin).list_databases(None, None, + False).thenReturn(['database1']) + databases = self.manager.list_databases(self.context) + self.assertThat(databases, Not(Is(None))) + self.assertThat(databases, Equals(['database1'])) + verify(dbaas.MySqlAdmin).list_databases(None, None, False) + + def test_list_users(self): + when(dbaas.MySqlAdmin).list_users(None, None, + False).thenReturn(['user1']) + users = self.manager.list_users(self.context) + self.assertThat(users, Equals(['user1'])) + verify(dbaas.MySqlAdmin).list_users(None, None, False) + + def test_get_users(self): + username = ['user1'] + hostname = ['host'] + when(dbaas.MySqlAdmin).get_user(username, + hostname).thenReturn(['user1']) + users = self.manager.get_user(self.context, username, hostname) + self.assertThat(users, Equals(['user1'])) + verify(dbaas.MySqlAdmin).get_user(username, hostname) + + def test_enable_root(self): + when(dbaas.MySqlAdmin).enable_root().thenReturn('user_id_stuff') + user_id = self.manager.enable_root(self.context) + self.assertThat(user_id, Is('user_id_stuff')) + verify(dbaas.MySqlAdmin).enable_root() + + def test_is_root_enabled(self): + when(dbaas.MySqlAdmin).is_root_enabled().thenReturn(True) + is_enabled = self.manager.is_root_enabled(self.context) + self.assertThat(is_enabled, Is(True)) + verify(dbaas.MySqlAdmin).is_root_enabled() + + def test_create_backup(self): + when(backup).backup(self.context, 'backup_id_123').thenReturn(None) + # entry point + Manager().create_backup(self.context, 'backup_id_123') + # assertions + verify(backup).backup(self.context, 'backup_id_123') + + 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_mysql_not_installed(self): + self._prepare_dynamic(is_mysql_installed=False) + + def test_prepare_mysql_from_backup(self): + self._prepare_dynamic(backup_id='backup_id_123abc') + + def test_prepare_mysql_from_backup_with_root(self): + self._prepare_dynamic(backup_id='backup_id_123abc', + is_root_enabled=True) + + def _prepare_dynamic(self, device_path='/dev/vdb', is_mysql_installed=True, + backup_id=None, is_root_enabled=False, + overrides=None): + # covering all outcomes is starting to cause trouble here + COUNT = 1 if device_path else 0 + backup_info = None + if backup_id is not None: + backup_info = {'id': backup_id, + 'location': 'fake-location', + 'type': 'InnoBackupEx', + 'checksum': 'fake-checksum', + } + + # TODO(juice): this should stub an instance of the MySqlAppStatus + mock_status = mock() + when(dbaas.MySqlAppStatus).get().thenReturn(mock_status) + when(mock_status).begin_install().thenReturn(None) + when(VolumeDevice).format().thenReturn(None) + when(VolumeDevice).migrate_data(any()).thenReturn(None) + when(VolumeDevice).mount().thenReturn(None) + when(dbaas.MySqlApp).stop_db().thenReturn(None) + when(dbaas.MySqlApp).start_mysql().thenReturn(None) + when(dbaas.MySqlApp).install_if_needed(any()).thenReturn(None) + when(backup).restore(self.context, + backup_info, + '/var/lib/mysql').thenReturn(None) + when(dbaas.MySqlApp).secure(any()).thenReturn(None) + when(dbaas.MySqlApp).secure_root(any()).thenReturn(None) + (when(pkg.Package).pkg_is_installed(any()). + thenReturn(is_mysql_installed)) + when(dbaas.MySqlAdmin).is_root_enabled().thenReturn(is_root_enabled) + when(dbaas.MySqlAdmin).create_user().thenReturn(None) + when(dbaas.MySqlAdmin).create_database().thenReturn(None) + + when(os.path).exists(any()).thenReturn(True) + # invocation + self.manager.prepare(context=self.context, + packages=None, + memory_mb='2048', + databases=None, + users=None, + device_path=device_path, + mount_point='/var/lib/mysql', + backup_info=backup_info, + overrides=overrides) + # verification/assertion + verify(mock_status).begin_install() + + verify(VolumeDevice, times=COUNT).format() + verify(dbaas.MySqlApp, times=COUNT).stop_db() + verify(VolumeDevice, times=COUNT).migrate_data( + any()) + if backup_info: + verify(backup).restore(self.context, backup_info, '/var/lib/mysql') + verify(dbaas.MySqlApp).install_if_needed(any()) + # We dont need to make sure the exact contents are there + verify(dbaas.MySqlApp).secure(any(), overrides) + verify(dbaas.MySqlAdmin, never).create_database() + verify(dbaas.MySqlAdmin, never).create_user() + verify(dbaas.MySqlApp).secure_root(secure_remote_root=any()) diff --git a/trove/tests/unittests/guestagent/test_pkg.py b/trove/tests/unittests/guestagent/test_pkg.py index 384e9db69b..bce5b31588 100644 --- a/trove/tests/unittests/guestagent/test_pkg.py +++ b/trove/tests/unittests/guestagent/test_pkg.py @@ -447,11 +447,6 @@ class PkgRPMRemoveTestCase(testtools.TestCase): self.assertRaises(pkg.PkgNotFoundError, self.pkg.pkg_remove, self.pkgName, 5000) - def test_success_remove(self): - # test - pexpect.spawn.expect = Mock(return_value=2) - self.assertTrue(self.pkg.pkg_remove(self.pkgName, 5000) is None) - def test_timeout_error(self): # test timeout error pexpect.spawn.expect = Mock(side_effect=pexpect. diff --git a/trove/tests/unittests/guestagent/test_redis_manager.py b/trove/tests/unittests/guestagent/test_redis_manager.py new file mode 100644 index 0000000000..d3857f8944 --- /dev/null +++ b/trove/tests/unittests/guestagent/test_redis_manager.py @@ -0,0 +1,97 @@ +# 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 testtools +from mockito import verify, when, unstub, any, mock +from trove.common.context import TroveContext +from trove.guestagent.datastore.redis.manager import Manager as RedisManager +import trove.guestagent.datastore.redis.service as redis_service +from trove.guestagent import backup +from trove.guestagent.volume import VolumeDevice + + +class RedisGuestAgentManagerTest(testtools.TestCase): + + def setUp(self): + super(RedisGuestAgentManagerTest, self).setUp() + self.context = TroveContext() + self.manager = RedisManager() + self.packages = 'redis-server' + self.origin_RedisAppStatus = redis_service.RedisAppStatus + self.origin_stop_redis = redis_service.RedisApp.stop_db + self.origin_start_redis = redis_service.RedisApp.start_redis + self.origin_install_redis = redis_service.RedisApp._install_redis + + def tearDown(self): + super(RedisGuestAgentManagerTest, self).tearDown() + redis_service.RedisAppStatus = self.origin_RedisAppStatus + redis_service.RedisApp.stop_db = self.origin_stop_redis + redis_service.RedisApp.start_redis = self.origin_start_redis + redis_service.RedisApp._install_redis = self.origin_install_redis + unstub() + + def test_update_status(self): + mock_status = mock() + when(redis_service.RedisAppStatus).get().thenReturn(mock_status) + self.manager.update_status(self.context) + verify(redis_service.RedisAppStatus).get() + verify(mock_status).update() + + def test_prepare_redis_not_installed(self): + self._prepare_dynamic(is_redis_installed=False) + + def _prepare_dynamic(self, device_path='/dev/vdb', is_redis_installed=True, + backup_info=None, is_root_enabled=False, + mount_point='var/lib/redis'): + + # covering all outcomes is starting to cause trouble here + mock_status = mock() + when(redis_service.RedisAppStatus).get().thenReturn(mock_status) + when(mock_status).begin_install().thenReturn(None) + when(VolumeDevice).format().thenReturn(None) + when(VolumeDevice).mount().thenReturn(None) + when(redis_service.RedisApp).start_redis().thenReturn(None) + when(redis_service.RedisApp).install_if_needed().thenReturn(None) + when(backup).restore(self.context, backup_info).thenReturn(None) + when(redis_service.RedisApp).write_config(any()).thenReturn(None) + when(redis_service.RedisApp).complete_install_or_restart( + any()).thenReturn(None) + self.manager.prepare(self.context, self.packages, + None, '2048', + None, device_path=device_path, + mount_point='/var/lib/redis', + backup_info=backup_info) + verify(redis_service.RedisAppStatus, times=2).get() + verify(mock_status).begin_install() + verify(VolumeDevice).format() + verify(redis_service.RedisApp).install_if_needed(self.packages) + verify(redis_service.RedisApp).write_config(None) + verify(redis_service.RedisApp).complete_install_or_restart() + + def test_restart(self): + mock_status = mock() + when(redis_service.RedisAppStatus).get().thenReturn(mock_status) + when(redis_service.RedisApp).restart().thenReturn(None) + self.manager.restart(self.context) + verify(redis_service.RedisAppStatus).get() + verify(redis_service.RedisApp).restart() + + def test_stop_db(self): + mock_status = mock() + when(redis_service.RedisAppStatus).get().thenReturn(mock_status) + when(redis_service.RedisApp).stop_db(do_not_start_on_reboot= + False).thenReturn(None) + self.manager.stop_db(self.context) + verify(redis_service.RedisAppStatus).get() + verify(redis_service.RedisApp).stop_db(do_not_start_on_reboot=False) diff --git a/trove/tests/unittests/taskmanager/test_models.py b/trove/tests/unittests/taskmanager/test_models.py index 373c6e4fba..9ab4b43fb4 100644 --- a/trove/tests/unittests/taskmanager/test_models.py +++ b/trove/tests/unittests/taskmanager/test_models.py @@ -316,6 +316,11 @@ class ResizeVolumeTest(testtools.TestCase): self.old_vol_size, self.new_vol_size) + class FakeGroup(): + def __init__(self): + self.mount_point = 'var/lib/mysql' + taskmanager_models.CONF.get = Mock(return_value=FakeGroup()) + def tearDown(self): super(ResizeVolumeTest, self).tearDown()