373 lines
13 KiB
Python
373 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):
|
|
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.0, 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(7.5, 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 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)
|