Files
trove/trove/tests/fakes/guestagent.py
T
Peter Stachowski 2a9fa44364 Persist error messages and display on 'show'
When an error occurs in Trove, it is very difficult
to determine the cause without access to the
server logs. To make these errors available to
the end user, they are now persisted in the database
and can be viewed using the standard 'show' command.

Also fixed TESTS_USE_INSTANCE_ID test path, as it
somehow got broken over time.

Change-Id: I84ed28ee73a24a2dd6bdbf895662d26e406e9fae
Depends-On: I5d3339e9cbfd6aeb0c3ff6936fefa8dbe9e841f8
Implements: blueprint persist-error-message
2016-07-01 22:27:30 +00:00

382 lines
13 KiB
Python

# Copyright 2010-2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re
import time
import eventlet
from oslo_log import log as logging
from trove.common import exception as rd_exception
from trove.common import instance as rd_instance
from trove.tests.util import unquote_user_host
DB = {}
LOG = logging.getLogger(__name__)
BACKUP_SIZE = 0.14
class FakeGuest(object):
def __init__(self, id):
self.id = id
self.users = {}
self.dbs = {}
self.root_was_enabled = False
self.version = 1
self.grants = {}
self.overrides = {}
# Our default admin user.
self._create_user({
"_name": "os_admin",
"_host": "%",
"_password": "12345",
"_databases": [],
})
def get_hwinfo(self):
return {'mem_total': 524288, 'num_cpus': 1}
def get_diagnostics(self):
return {
'version': str(self.version),
'fd_size': 64,
'vm_size': 29096,
'vm_peak': 29160,
'vm_rss': 2872,
'vm_hwm': 2872,
'threads': 2
}
def update_guest(self):
LOG.debug("Updating guest %s" % self.id)
self.version += 1
def _check_username(self, username):
unsupported_chars = re.compile("^\s|\s$|'|\"|;|`|,|/|\\\\")
if (not username or
unsupported_chars.search(username) or
("%r" % username).find("\\") != -1):
raise ValueError("'%s' is not a valid user name." % username)
if len(username) > 16:
raise ValueError("User name '%s' is too long. Max length = 16" %
username)
def change_passwords(self, users):
for user in users:
# Use the model to check validity.
username = user['name']
self._check_username(username)
hostname = user['host']
password = user['password']
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s@%s cannot be found on the instance."
% (username, hostname))
self.users[(username, hostname)]['password'] = password
def update_attributes(self, username, hostname, user_attrs):
LOG.debug("Updating attributes")
self._check_username(username)
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s@%s cannot be found on the instance."
% (username, hostname))
new_name = user_attrs.get('name')
new_host = user_attrs.get('host')
new_password = user_attrs.get('password')
old_name = username
old_host = hostname
name = new_name or old_name
host = new_host or old_host
if new_name or new_host:
old_grants = self.grants.get((old_name, old_host), set())
self._create_user({
"_name": name,
"_host": host,
"_password": self.users[(old_name, host)]['_password'],
"_databases": [],
})
self.grants[(name, host)] = old_grants
del self.users[(old_name, old_host)]
if new_password:
self.users[(name, host)]['_password'] = new_password
def create_database(self, databases):
for db in databases:
self.dbs[db['_name']] = db
def create_user(self, users):
for user in users:
self._create_user(user)
def _create_user(self, user):
username = user['_name']
self._check_username(username)
hostname = user['_host']
if hostname is None:
hostname = '%'
self.users[(username, hostname)] = user
print("CREATING %s @ %s" % (username, hostname))
databases = [db['_name'] for db in user['_databases']]
self.grant_access(username, hostname, databases)
return user
def delete_database(self, database):
if database['_name'] in self.dbs:
del self.dbs[database['_name']]
def enable_root(self):
self.root_was_enabled = True
return self._create_user({
"_name": "root",
"_host": "%",
"_password": "12345",
"_databases": [],
})
def enable_root_with_password(self, root_password=None):
self.root_was_enabled = True
return self._create_user({
"_name": "root",
"_host": "%",
"_password": "12345",
"_databases": [],
})
def disable_root(self):
self.delete_user({
"_name": "root",
"_host": "%"})
def delete_user(self, user):
username = user['_name']
self._check_username(username)
hostname = user['_host']
self.grants[(username, hostname)] = set()
if (username, hostname) in self.users:
del self.users[(username, hostname)]
def is_root_enabled(self):
return self.root_was_enabled
def _list_resource(self, resource, limit=None, marker=None,
include_marker=False):
names = sorted([name for name in resource])
if marker in names:
if not include_marker:
# Cut off everything left of and including the marker item.
names = names[names.index(marker) + 1:]
else:
names = names[names.index(marker):]
next_marker = None
if limit:
if len(names) > limit:
next_marker = names[limit - 1]
names = names[:limit]
return [resource[name] for name in names], next_marker
def list_databases(self, limit=None, marker=None, include_marker=False):
return self._list_resource(self.dbs, limit, marker, include_marker)
def list_users(self, limit=None, marker=None, include_marker=False):
# The markers for users are a composite of the username and hostname.
names = sorted(["%s@%s" % (name, host) for (name, host) in self.users])
if marker in names:
if not include_marker:
# Cut off everything left of and including the marker item.
names = names[names.index(marker) + 1:]
else:
names = names[names.index(marker):]
next_marker = None
if limit:
if len(names) > limit:
next_marker = names[limit - 1]
names = names[:limit]
return ([self.users[unquote_user_host(userhost)]
for userhost in names], next_marker)
def get_user(self, username, hostname):
self._check_username(username)
for (u, h) in self.users:
print("%r @ %r" % (u, h))
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s@%s cannot be found on the instance."
% (username, hostname))
return self.users.get((username, hostname), None)
def prepare(self, memory_mb, packages, databases, users, device_path=None,
mount_point=None, backup_info=None, config_contents=None,
root_password=None, overrides=None, cluster_config=None,
snapshot=None, modules=None):
from trove.guestagent.models import AgentHeartBeat
from trove.instance.models import DBInstance
from trove.instance.models import InstanceServiceStatus
LOG.debug("users... %s" % users)
LOG.debug("databases... %s" % databases)
instance_name = DBInstance.find_by(id=self.id).name
self.create_user(users)
self.create_database(databases)
self.overrides = overrides or {}
def update_db():
status = InstanceServiceStatus.find_by(instance_id=self.id)
if instance_name.endswith('GUEST_ERROR'):
status.status = rd_instance.ServiceStatuses.FAILED
else:
status.status = rd_instance.ServiceStatuses.RUNNING
status.save()
AgentHeartBeat.create(instance_id=self.id)
eventlet.spawn_after(3.5, update_db)
def _set_task_status(self, new_status='RUNNING'):
from trove.instance.models import InstanceServiceStatus
print("Setting status to %s" % new_status)
states = {'RUNNING': rd_instance.ServiceStatuses.RUNNING,
'SHUTDOWN': rd_instance.ServiceStatuses.SHUTDOWN,
}
status = InstanceServiceStatus.find_by(instance_id=self.id)
status.status = states[new_status]
status.save()
def restart(self):
# All this does is restart, and shut off the status updates while it
# does so. So there's actually nothing to do to fake this out except
# take a nap.
print("Sleeping for a second.")
time.sleep(1)
self._set_task_status('RUNNING')
def reset_configuration(self, config):
# There's nothing to do here, since there is no config to update.
pass
def start_db_with_conf_changes(self, config_contents):
time.sleep(2)
self._set_task_status('RUNNING')
def stop_db(self, do_not_start_on_reboot=False):
self._set_task_status('SHUTDOWN')
def get_volume_info(self):
"""Return used and total volume filesystem information in GB."""
return {'used': 0.16, 'total': 4.0}
def grant_access(self, username, hostname, databases):
"""Add a database to a users's grant list."""
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s cannot be found on the instance." % username)
current_grants = self.grants.get((username, hostname), set())
for db in databases:
current_grants.add(db)
self.grants[(username, hostname)] = current_grants
def revoke_access(self, username, hostname, database):
"""Remove a database from a users's grant list."""
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s cannot be found on the instance." % username)
if database not in self.grants.get((username, hostname), set()):
raise rd_exception.DatabaseNotFound(
"Database %s cannot be found on the instance." % database)
current_grants = self.grants.get((username, hostname), set())
if database in current_grants:
current_grants.remove(database)
self.grants[(username, hostname)] = current_grants
def list_access(self, username, hostname):
if (username, hostname) not in self.users:
raise rd_exception.UserNotFound(
"User %s cannot be found on the instance." % username)
current_grants = self.grants.get((username, hostname), set())
dbs = [{'_name': db,
'_collate': '',
'_character_set': '',
} for db in current_grants]
return dbs
def create_backup(self, backup_info):
from trove.backup.models import Backup
from trove.backup.state import BackupState
backup = Backup.get_by_id(context=None,
backup_id=backup_info['id'])
def finish_create_backup():
backup.state = BackupState.COMPLETED
backup.location = 'http://localhost/path/to/backup'
backup.checksum = 'fake-md5-sum'
backup.size = BACKUP_SIZE
backup.save()
eventlet.spawn_after(8, finish_create_backup)
def mount_volume(self, device_path=None, mount_point=None):
pass
def unmount_volume(self, device_path=None, mount_point=None):
pass
def resize_fs(self, device_path=None, mount_point=None):
pass
def update_overrides(self, overrides, remove=False):
self.overrides = overrides
def apply_overrides(self, overrides):
self.overrides = overrides
def get_replication_snapshot(self, snapshot_info,
replica_source_config=None):
self.create_backup(snapshot_info)
return {
'dataset':
{
'datastore_manager': 'mysql',
'dataset_size': '0.0',
'volume_size': '10.0',
'snapshot_id': None
},
'replication_strategy': 'replication_strategy',
'master': '1',
'log_position': '100'
}
def attach_replication_slave(self, snapshot, slave_config):
pass
def backup_required_for_replication(self):
return True
def module_list(self, context, include_contents=False):
return []
def module_apply(self, context, modules=None):
return []
def module_remove(self, context, module=None):
pass
def get_or_create(id):
if id not in DB:
DB[id] = FakeGuest(id)
return DB[id]
def fake_create_guest_client(context, id):
return get_or_create(id)