trove/trove/tests/fakes/guestagent.py

385 lines
14 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(10, 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 post_processing_required_for_replication(self):
return False
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)