Add db-models and RootController for Postgres

Add Postgres guestagent db models.

Implement RootController extension for the Postgres datastore.
The extension is still using MySQL User models which
will likely cause troubles later (e.g. host parameter).

Change-Id: I1a2eb72fe192943e6843c0ea020d7747740e0714
Partial-Bug: 1543739
This commit is contained in:
Petr Malik 2015-09-30 14:19:25 -04:00
parent 5c364df2c1
commit 25ab8e2924
5 changed files with 118 additions and 1 deletions

View File

@ -0,0 +1,6 @@
---
fixes:
- Implement Postgres guestagent models for
databases and users.
- Implement RootController extension for the Postgres
datastore.

View File

@ -1043,7 +1043,8 @@ postgresql_opts = [
cfg.ListOpt('ignore_users', default=['os_admin', 'postgres', 'root']),
cfg.ListOpt('ignore_dbs', default=['postgres']),
cfg.StrOpt('root_controller',
default='trove.extensions.common.service.DefaultRootController',
default='trove.extensions.postgresql.service'
'.PostgreSQLRootController',
help='Root controller implementation for postgresql.'),
cfg.StrOpt('guest_log_exposed_logs', default='general',
help='List of Guest Logs to expose for publishing.'),

View File

View File

@ -0,0 +1,29 @@
# Copyright 2015 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.
from trove.extensions.common.service import DefaultRootController
from trove.extensions.mysql import models
from trove.guestagent.db import models as guest_models
class PostgreSQLRootController(DefaultRootController):
def _find_root_user(self, context, instance_id):
user = guest_models.PostgreSQLRootUser()
# This is currently using MySQL model.
# MySQL extension *should* work for now, but may lead to
# future bugs (incompatible input validation, unused field etc).
return models.User.load(
context, instance_id, user.name, user.host, root_user=True)

View File

@ -18,6 +18,7 @@ import re
import string
import netaddr
from six import u
from trove.common import cfg
from trove.common import exception
@ -231,6 +232,37 @@ class CouchDBSchema(DatastoreSchema):
return ['_name']
class PostgreSQLSchema(DatastoreSchema):
"""Represents a PostgreSQL schema and its associated properties.
Permitted characters in quoted identifiers include the full
Unicode Basic Multilingual Plane (BMP), except U+0000.
Database, table, and column names cannot end with space characters.
"""
name_regex = re.compile(u(r'^[\u0001-\u007F\u0080-\uFFFF]+[^\s]$'))
def __init__(self, name=None, deserializing=False):
super(PostgreSQLSchema, self).__init__()
if not (bool(deserializing) != bool(name)):
raise ValueError(_("Bad args. name: %(name)s, "
"deserializing %(deser)s.")
% ({'name': bool(name),
'deser': bool(deserializing)}))
if not deserializing:
self.name = name
@property
def _max_schema_name_length(self):
return 63
def _is_valid_schema_name(self, value):
return self.name_regex.match(value) is not None
@classmethod
def _dict_requirements(cls):
return ['_name']
class MySQLDatabase(Base):
"""Represents a Database and its properties."""
@ -1010,8 +1042,49 @@ class MySQLUser(Base):
self._host = value
class PostgreSQLUser(DatastoreUser):
"""Represents a PostgreSQL user and its associated properties."""
def __init__(self, name=None, password=None, deserializing=False):
super(PostgreSQLUser, self).__init__()
if ((not (bool(deserializing) != bool(name))) or
(bool(deserializing) and bool(password))):
raise ValueError(_("Bad args. name: %(name)s, "
"password %(pass)s, "
"deserializing %(deser)s.")
% ({'name': bool(name),
'pass': bool(password),
'deser': bool(deserializing)}))
if not deserializing:
self.name = name
self.password = password
def _build_database_schema(self, name):
return PostgreSQLSchema(name)
@property
def _max_username_length(self):
return 63
def _is_valid_name(self, value):
return True
def _is_valid_host_name(self, value):
return True
def _is_valid_password(self, value):
return True
@classmethod
def _dict_requirements(cls):
return ['name']
class RootUser(MySQLUser):
"""Overrides _ignore_users from the MySQLUser class."""
def __init__(self):
self._ignore_users = []
@ -1037,3 +1110,11 @@ class CassandraRootUser(CassandraUser):
password = utils.generate_random_password()
super(CassandraRootUser, self).__init__("cassandra", password=password,
*args, **kwargs)
class PostgreSQLRootUser(PostgreSQLUser):
"""Represents the PostgreSQL default superuser."""
def __init__(self, password=None):
password = password if not None else utils.generate_random_password()
super(PostgreSQLRootUser, self).__init__("postgres", password=password)