trove/trove/common/db/mysql/models.py

171 lines
5.4 KiB
Python

# Copyright 2016 Tesora, Inc.
# 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 netaddr
from trove.common import cfg
from trove.common.db import models
from trove.common.db.mysql import data as mysql_settings
from trove.common.i18n import _
CONF = cfg.CONF
class MySQLSchema(models.DatastoreSchema):
"""Represents a MySQL database and its properties."""
# Defaults
__charset__ = "utf8"
__collation__ = "utf8_general_ci"
dbname = re.compile(r"^[A-Za-z0-9_-]+[\s\?\#\@]*[A-Za-z0-9_-]+$")
# Complete list of acceptable values
collation = mysql_settings.collation
charset = mysql_settings.charset
def __init__(self, name=None, collate=None, character_set=None,
deserializing=False):
super(MySQLSchema, self).__init__(name=name,
deserializing=deserializing)
if not deserializing:
if collate:
self.collate = collate
if character_set:
self.character_set = character_set
@property
def _max_schema_name_length(self):
return 64
def _is_valid_schema_name(self, value):
# must match the dbname regex, and
# cannot contain a '\' character.
return not any([
not self.dbname.match(value),
("%r" % value).find("\\") != -1
])
@property
def collate(self):
"""Get the appropriate collate value."""
if not self._collate and not self._character_set:
return self.__collation__
elif not self._collate:
return self.charset[self._character_set][0]
else:
return self._collate
@collate.setter
def collate(self, value):
"""Validate the collation and set it."""
if not value:
pass
elif self._character_set:
if value not in self.charset[self._character_set]:
msg = (_("%(val)s not a valid collation for charset %(char)s.")
% {'val': value, 'char': self._character_set})
raise ValueError(msg)
self._collate = value
else:
if value not in self.collation:
raise ValueError(_("'%s' not a valid collation.") % value)
self._collate = value
self._character_set = self.collation[value]
@property
def character_set(self):
"""Get the appropriate character set value."""
if not self._character_set:
return self.__charset__
else:
return self._character_set
@character_set.setter
def character_set(self, value):
"""Validate the character set and set it."""
if not value:
pass
elif value not in self.charset:
raise ValueError(_("'%s' not a valid character set.") % value)
else:
self._character_set = value
def verify_dict(self):
# Also check the collate and character_set values if set, initialize
# them if not.
super(MySQLSchema, self).verify_dict()
if self.__dict__.get('_collate'):
self.collate = self._collate
else:
self._collate = None
if self.__dict__.get('_character_set'):
self.character_set = self._character_set
else:
self._character_set = None
class MySQLUser(models.DatastoreUser):
"""Represents a MySQL User and its associated properties."""
not_supported_chars = re.compile(r"""^\s|\s$|'|"|;|`|,|/|\\""")
def _is_valid_string(self, value):
if (not value or
self.not_supported_chars.search(value) or
("%r" % value).find("\\") != -1):
return False
else:
return True
def _is_valid_user_name(self, value):
return self._is_valid_string(value)
def _is_valid_password(self, value):
return self._is_valid_string(value)
def _is_valid_host_name(self, value):
if value in [None, "%"]:
# % is MySQL shorthand for "everywhere". Always permitted.
# Null host defaults to % anyway.
return True
if CONF.hostname_require_valid_ip:
try:
# '%' works as a MySQL wildcard, but it is not a valid
# part of an IPNetwork
netaddr.IPNetwork(value.replace('%', '1'))
except (ValueError, netaddr.AddrFormatError):
return False
else:
return True
else:
# If it wasn't required, anything else goes.
return True
def _build_database_schema(self, name):
return MySQLSchema(name)
def deserialize_schema(self, value):
return MySQLSchema.deserialize(value)
@property
def _max_user_name_length(self):
return 16
@property
def schema_model(self):
return MySQLSchema