trove/reddwarf/extensions/mysql/models.py
Nikhil Manchanda 595125ac84 Backup and Restore for reddwarf instances.
Implemented backup and restore for reddwarf instances.

Implements spec/consistent-snapshots

Change-Id: Ic1e78545f9e90ab986665a58f524081dcde6894b
2013-05-20 14:16:00 -07:00

307 lines
11 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010-2011 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.
"""
Model classes that extend the instances functionality for MySQL instances.
"""
from reddwarf.common import cfg
from reddwarf.common import exception
from reddwarf.common import utils
from reddwarf.common.remote import create_guest_client
from reddwarf.db import get_db_api
from reddwarf.guestagent.db import models as guest_models
from reddwarf.instance import models as base_models
from reddwarf.openstack.common import log as logging
from reddwarf.openstack.common.gettextutils import _
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def persisted_models():
return {'root_enabled_history': RootHistory}
def load_and_verify(context, instance_id):
# Load InstanceServiceStatus to verify if its running
instance = base_models.Instance.load(context, instance_id)
if not instance.is_sql_running:
raise exception.UnprocessableEntity(
"Instance %s is not ready." % instance.id)
else:
return instance
class User(object):
_data_fields = ['name', 'host', 'password', 'databases']
def __init__(self, name, host, password, databases):
self.name = name
self.host = host
self.password = password
self.databases = databases
@classmethod
def load(cls, context, instance_id, username, hostname):
load_and_verify(context, instance_id)
validate = guest_models.MySQLUser()
validate.name = username
validate.host = hostname
client = create_guest_client(context, instance_id)
found_user = client.get_user(username=username, hostname=hostname)
if not found_user:
return None
database_names = [{'name': db['_name']}
for db in found_user['_databases']]
return cls(found_user['_name'],
found_user['_host'],
found_user['_password'],
database_names)
@classmethod
def create(cls, context, instance_id, users):
# Load InstanceServiceStatus to verify if it's running
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
for user in users:
user_name = user['_name']
host_name = user['_host']
if host_name is None:
host_name = '%'
userhost = "%s@%s" % (user_name, host_name)
existing_users, _nadda = Users.load_with_client(
client,
limit=1,
marker=userhost,
include_marker=True)
if (len(existing_users) > 0 and
str(existing_users[0].name) == str(user_name) and
str(existing_users[0].host) == str(host_name)):
raise exception.UserAlreadyExists(name=user_name,
host=host_name)
return client.create_user(users)
@classmethod
def delete(cls, context, instance_id, user):
load_and_verify(context, instance_id)
create_guest_client(context, instance_id).delete_user(user)
@classmethod
def access(cls, context, instance_id, username, hostname):
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
databases = client.list_access(username, hostname)
dbs = []
for db in databases:
dbs.append(Schema(name=db['_name'],
collate=db['_collate'],
character_set=db['_character_set']))
return UserAccess(dbs)
@classmethod
def grant(cls, context, instance_id, username, hostname, databases):
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
client.grant_access(username, hostname, databases)
@classmethod
def revoke(cls, context, instance_id, username, hostname, database):
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
client.revoke_access(username, hostname, database)
@classmethod
def change_password(cls, context, instance_id, users):
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
change_users = []
for user in users:
change_user = {'name': user.name,
'host': user.host,
'password': user.password,
}
change_users.append(change_user)
client.change_passwords(change_users)
class UserAccess(object):
_data_fields = ['databases']
def __init__(self, databases):
self.databases = databases
class Root(object):
@classmethod
def load(cls, context, instance_id):
load_and_verify(context, instance_id)
# TODO(pdmars): remove the is_root_enabled call from the guest agent,
# just check the database for this information.
# If the root history returns null or raises an exception, the root
# user hasn't been enabled.
try:
root_history = RootHistory.load(context, instance_id)
except exception.NotFound:
return False
if not root_history:
return False
return True
@classmethod
def create(cls, context, instance_id, user):
load_and_verify(context, instance_id)
root = create_guest_client(context, instance_id).enable_root()
root_user = guest_models.RootUser()
root_user.deserialize(root)
root_history = RootHistory.create(context, instance_id, user)
return root_user
class RootHistory(object):
_auto_generated_attrs = ['id']
_data_fields = ['instance_id', 'user', 'created']
_table_name = 'root_enabled_history'
def __init__(self, instance_id, user):
self.id = instance_id
self.user = user
self.created = utils.utcnow()
def save(self):
LOG.debug(_("Saving %s: %s") % (self.__class__.__name__,
self.__dict__))
return get_db_api().save(self)
@classmethod
def load(cls, context, instance_id):
history = get_db_api().find_by(cls, id=instance_id)
return history
@classmethod
def create(cls, context, instance_id, user):
history = cls.load(context, instance_id)
if history is not None:
return history
history = RootHistory(instance_id, user)
return history.save()
def load_via_context(cls, context, instance_id):
"""Creates guest and fetches pagination arguments from the context."""
load_and_verify(context, instance_id)
limit = int(context.limit or cls.DEFAULT_LIMIT)
limit = cls.DEFAULT_LIMIT if limit > cls.DEFAULT_LIMIT else limit
client = create_guest_client(context, instance_id)
# The REST API standard dictates that we *NEVER* include the marker.
return cls.load_with_client(client=client, limit=limit,
marker=context.marker, include_marker=False)
class Users(object):
DEFAULT_LIMIT = CONF.users_page_size
@classmethod
def load(cls, context, instance_id):
return load_via_context(cls, context, instance_id)
@classmethod
def load_with_client(cls, client, limit, marker, include_marker):
user_list, next_marker = client.list_users(
limit=limit,
marker=marker,
include_marker=include_marker)
model_users = []
ignore_users = CONF.ignore_users
for user in user_list:
mysql_user = guest_models.MySQLUser()
mysql_user.deserialize(user)
if mysql_user.name in ignore_users:
continue
# TODO(hub-cap): databases are not being returned in the
# reference agent
dbs = []
for db in mysql_user.databases:
dbs.append({'name': db['_name']})
model_users.append(User(mysql_user.name,
mysql_user.host,
mysql_user.password,
dbs))
return model_users, next_marker
class Schema(object):
_data_fields = ['name', 'collate', 'character_set']
def __init__(self, name, collate, character_set):
self.name = name
self.collate = collate
self.character_set = character_set
@classmethod
def create(cls, context, instance_id, schemas):
load_and_verify(context, instance_id)
client = create_guest_client(context, instance_id)
for schema in schemas:
schema_name = schema['_name']
existing_schema, _nadda = Schemas.load_with_client(
client,
limit=1,
marker=schema_name,
include_marker=True)
if (len(existing_schema) > 0 and
str(existing_schema[0].name) == str(schema_name)):
raise exception.DatabaseAlreadyExists(name=schema_name)
return client.create_database(schemas)
@classmethod
def delete(cls, context, instance_id, schema):
load_and_verify(context, instance_id)
create_guest_client(context, instance_id).delete_database(schema)
class Schemas(object):
DEFAULT_LIMIT = CONF.databases_page_size
@classmethod
def load(cls, context, instance_id):
return load_via_context(cls, context, instance_id)
@classmethod
def load_with_client(cls, client, limit, marker, include_marker):
schemas, next_marker = client.list_databases(
limit=limit,
marker=marker,
include_marker=include_marker)
model_schemas = []
ignore_dbs = CONF.ignore_dbs
for schema in schemas:
mysql_schema = guest_models.MySQLDatabase()
mysql_schema.deserialize(schema)
if mysql_schema.name in ignore_dbs:
continue
model_schemas.append(Schema(mysql_schema.name,
mysql_schema.collate,
mysql_schema.character_set))
return model_schemas, next_marker