Support Optional Super User in Instance Create
The creation of initial users and/or databases is supported in an instance-create request, but the creation of a super/root user is not. Granted, it's possible to create or reset the root user, but it must be done as a separate request, after the instance has completely initialized. Changeset: If CONF.root_on_create == True, a UUID is generated for the root user's password and is returned in the instance-create response as the "password" property/field. Change-Id: I300d3f019766445b90411d5ab8651b1aa34a77e9 Implements: blueprint instance-initial-super-user Closes-Bug: #1219624
This commit is contained in:
parent
c05fc3fbe0
commit
f77aab57e1
|
@ -75,6 +75,8 @@ taskmanager_queue = taskmanager
|
||||||
# Auth
|
# Auth
|
||||||
admin_roles = admin
|
admin_roles = admin
|
||||||
|
|
||||||
|
root_on_create = False
|
||||||
|
|
||||||
# Users to ignore for user create/list/delete operations
|
# Users to ignore for user create/list/delete operations
|
||||||
ignore_users = os_admin, root
|
ignore_users = os_admin, root
|
||||||
ignore_dbs = lost+found, mysql, information_schema
|
ignore_dbs = lost+found, mysql, information_schema
|
||||||
|
|
|
@ -136,6 +136,7 @@ if __name__ == "__main__":
|
||||||
from trove.tests.api import instances_resize
|
from trove.tests.api import instances_resize
|
||||||
from trove.tests.api import databases
|
from trove.tests.api import databases
|
||||||
from trove.tests.api import root
|
from trove.tests.api import root
|
||||||
|
from trove.tests.api import root_on_create
|
||||||
from trove.tests.api import users
|
from trove.tests.api import users
|
||||||
from trove.tests.api import user_access
|
from trove.tests.api import user_access
|
||||||
from trove.tests.api.mgmt import accounts
|
from trove.tests.api.mgmt import accounts
|
||||||
|
|
|
@ -118,6 +118,11 @@ common_opts = [
|
||||||
cfg.IntOpt('dns_time_out', default=60 * 2),
|
cfg.IntOpt('dns_time_out', default=60 * 2),
|
||||||
cfg.IntOpt('resize_time_out', default=60 * 10),
|
cfg.IntOpt('resize_time_out', default=60 * 10),
|
||||||
cfg.IntOpt('revert_time_out', default=60 * 10),
|
cfg.IntOpt('revert_time_out', default=60 * 10),
|
||||||
|
cfg.BoolOpt('root_on_create', default=False,
|
||||||
|
help='Enable the automatic creation of the root user for the '
|
||||||
|
' service during instance-create. The generated password for '
|
||||||
|
' the root user is immediately returned in the response of '
|
||||||
|
" instance-create as the 'password' field."),
|
||||||
cfg.ListOpt('root_grant', default=['ALL']),
|
cfg.ListOpt('root_grant', default=['ALL']),
|
||||||
cfg.BoolOpt('root_grant_option', default=True),
|
cfg.BoolOpt('root_grant_option', default=True),
|
||||||
cfg.IntOpt('http_get_rate', default=200),
|
cfg.IntOpt('http_get_rate', default=200),
|
||||||
|
|
|
@ -214,7 +214,7 @@ class API(proxy.RpcProxy):
|
||||||
|
|
||||||
def prepare(self, memory_mb, databases, users,
|
def prepare(self, memory_mb, databases, users,
|
||||||
device_path='/dev/vdb', mount_point='/mnt/volume',
|
device_path='/dev/vdb', mount_point='/mnt/volume',
|
||||||
backup_id=None, config_contents=None):
|
backup_id=None, config_contents=None, root_password=None):
|
||||||
"""Make an asynchronous call to prepare the guest
|
"""Make an asynchronous call to prepare the guest
|
||||||
as a database container optionally includes a backup id for restores
|
as a database container optionally includes a backup id for restores
|
||||||
"""
|
"""
|
||||||
|
@ -222,7 +222,8 @@ class API(proxy.RpcProxy):
|
||||||
self._cast_with_consumer(
|
self._cast_with_consumer(
|
||||||
"prepare", databases=databases, memory_mb=memory_mb,
|
"prepare", databases=databases, memory_mb=memory_mb,
|
||||||
users=users, device_path=device_path, mount_point=mount_point,
|
users=users, device_path=device_path, mount_point=mount_point,
|
||||||
backup_id=backup_id, config_contents=config_contents)
|
backup_id=backup_id, config_contents=config_contents,
|
||||||
|
root_password=root_password)
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
"""Restart the MySQL server."""
|
"""Restart the MySQL server."""
|
||||||
|
|
|
@ -85,7 +85,8 @@ class Manager(periodic_task.PeriodicTasks):
|
||||||
LOG.info(_("Restored database successfully"))
|
LOG.info(_("Restored database successfully"))
|
||||||
|
|
||||||
def prepare(self, context, databases, memory_mb, users, device_path=None,
|
def prepare(self, context, databases, memory_mb, users, device_path=None,
|
||||||
mount_point=None, backup_id=None, config_contents=None):
|
mount_point=None, backup_id=None, config_contents=None,
|
||||||
|
root_password=None):
|
||||||
"""Makes ready DBAAS on a Guest container."""
|
"""Makes ready DBAAS on a Guest container."""
|
||||||
MySqlAppStatus.get().begin_mysql_install()
|
MySqlAppStatus.get().begin_mysql_install()
|
||||||
# status end_mysql_install set with secure()
|
# status end_mysql_install set with secure()
|
||||||
|
@ -114,9 +115,16 @@ class Manager(periodic_task.PeriodicTasks):
|
||||||
LOG.info(_("Securing mysql now."))
|
LOG.info(_("Securing mysql now."))
|
||||||
app.secure(config_contents)
|
app.secure(config_contents)
|
||||||
enable_root_on_restore = (backup_id and MySqlAdmin().is_root_enabled())
|
enable_root_on_restore = (backup_id and MySqlAdmin().is_root_enabled())
|
||||||
if enable_root_on_restore:
|
if root_password and not backup_id:
|
||||||
|
app.secure_root(secure_remote_root=True)
|
||||||
|
MySqlAdmin().enable_root(root_password)
|
||||||
MySqlAdmin().report_root_enabled(context)
|
MySqlAdmin().report_root_enabled(context)
|
||||||
app.secure_root(secure_remote_root=not enable_root_on_restore)
|
elif enable_root_on_restore:
|
||||||
|
app.secure_root(secure_remote_root=False)
|
||||||
|
MySqlAdmin().report_root_enabled(context)
|
||||||
|
else:
|
||||||
|
app.secure_root(secure_remote_root=True)
|
||||||
|
|
||||||
app.complete_install_or_restart()
|
app.complete_install_or_restart()
|
||||||
|
|
||||||
if databases:
|
if databases:
|
||||||
|
|
|
@ -453,9 +453,9 @@ class MySqlAdmin(object):
|
||||||
"""Return True if root access is enabled; False otherwise."""
|
"""Return True if root access is enabled; False otherwise."""
|
||||||
return MySqlRootAccess.is_root_enabled()
|
return MySqlRootAccess.is_root_enabled()
|
||||||
|
|
||||||
def enable_root(self):
|
def enable_root(self, root_password=None):
|
||||||
"""Enable the root user global access and/or reset the root password"""
|
"""Enable the root user global access and/or reset the root password"""
|
||||||
return MySqlRootAccess.enable_root()
|
return MySqlRootAccess.enable_root(root_password)
|
||||||
|
|
||||||
def report_root_enabled(self, context=None):
|
def report_root_enabled(self, context=None):
|
||||||
"""Records in the Root History that the root is enabled"""
|
"""Records in the Root History that the root is enabled"""
|
||||||
|
@ -889,12 +889,12 @@ class MySqlRootAccess(object):
|
||||||
return result.rowcount != 0
|
return result.rowcount != 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enable_root(cls):
|
def enable_root(cls, root_password=None):
|
||||||
"""Enable the root user global access and/or reset the root password"""
|
"""Enable the root user global access and/or reset the root password"""
|
||||||
user = models.RootUser()
|
user = models.RootUser()
|
||||||
user.name = "root"
|
user.name = "root"
|
||||||
user.host = "%"
|
user.host = "%"
|
||||||
user.password = generate_random_password()
|
user.password = root_password or generate_random_password()
|
||||||
with LocalSqlClient(get_engine()) as client:
|
with LocalSqlClient(get_engine()) as client:
|
||||||
print(client)
|
print(client)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -36,6 +36,7 @@ from trove.instance.tasks import InstanceTask
|
||||||
from trove.instance.tasks import InstanceTasks
|
from trove.instance.tasks import InstanceTasks
|
||||||
from trove.taskmanager import api as task_api
|
from trove.taskmanager import api as task_api
|
||||||
from trove.openstack.common import log as logging
|
from trove.openstack.common import log as logging
|
||||||
|
from trove.openstack.common import uuidutils
|
||||||
from trove.openstack.common.gettextutils import _
|
from trove.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,10 +116,11 @@ class SimpleInstance(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context, db_info, service_status):
|
def __init__(self, context, db_info, service_status, root_password=None):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.db_info = db_info
|
self.db_info = db_info
|
||||||
self.service_status = service_status
|
self.service_status = service_status
|
||||||
|
self.root_pass = root_password
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def addresses(self):
|
def addresses(self):
|
||||||
|
@ -228,6 +230,10 @@ class SimpleInstance(object):
|
||||||
def service_type(self):
|
def service_type(self):
|
||||||
return self.db_info.service_type
|
return self.db_info.service_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def root_password(self):
|
||||||
|
return self.root_pass
|
||||||
|
|
||||||
|
|
||||||
class DetailInstance(SimpleInstance):
|
class DetailInstance(SimpleInstance):
|
||||||
"""A detailed view of an Instnace.
|
"""A detailed view of an Instnace.
|
||||||
|
@ -488,13 +494,19 @@ class Instance(BuiltInstance):
|
||||||
)
|
)
|
||||||
security_groups = [security_group["name"]]
|
security_groups = [security_group["name"]]
|
||||||
|
|
||||||
|
root_password = None
|
||||||
|
if CONF.root_on_create and not backup_id:
|
||||||
|
root_password = uuidutils.generate_uuid()
|
||||||
|
|
||||||
task_api.API(context).create_instance(db_info.id, name, flavor,
|
task_api.API(context).create_instance(db_info.id, name, flavor,
|
||||||
image_id, databases, users,
|
image_id, databases, users,
|
||||||
service_type, volume_size,
|
service_type, volume_size,
|
||||||
security_groups, backup_id,
|
security_groups, backup_id,
|
||||||
availability_zone)
|
availability_zone,
|
||||||
|
root_password)
|
||||||
|
|
||||||
return SimpleInstance(context, db_info, service_status)
|
return SimpleInstance(context, db_info, service_status,
|
||||||
|
root_password)
|
||||||
|
|
||||||
return run_with_quotas(context.tenant,
|
return run_with_quotas(context.tenant,
|
||||||
deltas,
|
deltas,
|
||||||
|
|
|
@ -93,8 +93,8 @@ class InstanceDetailView(InstanceView):
|
||||||
if ip is not None and len(ip) > 0:
|
if ip is not None and len(ip) > 0:
|
||||||
result['instance']['ip'] = ip
|
result['instance']['ip'] = ip
|
||||||
|
|
||||||
if isinstance(self.instance, models.DetailInstance) and \
|
if (isinstance(self.instance, models.DetailInstance) and
|
||||||
self.instance.volume_used:
|
self.instance.volume_used):
|
||||||
used = self.instance.volume_used
|
used = self.instance.volume_used
|
||||||
if CONF.trove_volume_support:
|
if CONF.trove_volume_support:
|
||||||
result['instance']['volume']['used'] = used
|
result['instance']['volume']['used'] = used
|
||||||
|
@ -102,6 +102,9 @@ class InstanceDetailView(InstanceView):
|
||||||
# either ephemeral or root partition
|
# either ephemeral or root partition
|
||||||
result['instance']['local_storage'] = {'used': used}
|
result['instance']['local_storage'] = {'used': used}
|
||||||
|
|
||||||
|
if self.instance.root_password:
|
||||||
|
result['instance']['password'] = self.instance.root_password
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -108,11 +108,12 @@ class API(ManagerAPI):
|
||||||
def create_instance(self, instance_id, name, flavor,
|
def create_instance(self, instance_id, name, flavor,
|
||||||
image_id, databases, users, service_type,
|
image_id, databases, users, service_type,
|
||||||
volume_size, security_groups, backup_id=None,
|
volume_size, security_groups, backup_id=None,
|
||||||
availability_zone=None):
|
availability_zone=None, root_password=None):
|
||||||
LOG.debug("Making async call to create instance %s " % instance_id)
|
LOG.debug("Making async call to create instance %s " % instance_id)
|
||||||
self._cast("create_instance", instance_id=instance_id, name=name,
|
self._cast("create_instance", instance_id=instance_id, name=name,
|
||||||
flavor=self._transform_obj(flavor), image_id=image_id,
|
flavor=self._transform_obj(flavor), image_id=image_id,
|
||||||
databases=databases, users=users,
|
databases=databases, users=users,
|
||||||
service_type=service_type, volume_size=volume_size,
|
service_type=service_type, volume_size=volume_size,
|
||||||
security_groups=security_groups, backup_id=backup_id,
|
security_groups=security_groups, backup_id=backup_id,
|
||||||
availability_zone=availability_zone)
|
availability_zone=availability_zone,
|
||||||
|
root_password=root_password)
|
||||||
|
|
|
@ -83,12 +83,13 @@ class Manager(periodic_task.PeriodicTasks):
|
||||||
def create_instance(self, context, instance_id, name, flavor,
|
def create_instance(self, context, instance_id, name, flavor,
|
||||||
image_id, databases, users, service_type,
|
image_id, databases, users, service_type,
|
||||||
volume_size, security_groups, backup_id,
|
volume_size, security_groups, backup_id,
|
||||||
availability_zone):
|
availability_zone, root_password):
|
||||||
instance_tasks = FreshInstanceTasks.load(context, instance_id)
|
instance_tasks = FreshInstanceTasks.load(context, instance_id)
|
||||||
instance_tasks.create_instance(flavor, image_id,
|
instance_tasks.create_instance(flavor, image_id,
|
||||||
databases, users, service_type,
|
databases, users, service_type,
|
||||||
volume_size, security_groups,
|
volume_size, security_groups,
|
||||||
backup_id, availability_zone)
|
backup_id, availability_zone,
|
||||||
|
root_password)
|
||||||
|
|
||||||
if CONF.exists_notification_transformer:
|
if CONF.exists_notification_transformer:
|
||||||
@periodic_task.periodic_task(
|
@periodic_task.periodic_task(
|
||||||
|
|
|
@ -135,7 +135,7 @@ class ConfigurationMixin(object):
|
||||||
class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||||
def create_instance(self, flavor, image_id, databases, users,
|
def create_instance(self, flavor, image_id, databases, users,
|
||||||
service_type, volume_size, security_groups,
|
service_type, volume_size, security_groups,
|
||||||
backup_id, availability_zone):
|
backup_id, availability_zone, root_password):
|
||||||
if use_heat:
|
if use_heat:
|
||||||
server, volume_info = self._create_server_volume_heat(
|
server, volume_info = self._create_server_volume_heat(
|
||||||
flavor,
|
flavor,
|
||||||
|
@ -173,7 +173,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||||
if server:
|
if server:
|
||||||
self._guest_prepare(server, flavor['ram'], volume_info,
|
self._guest_prepare(server, flavor['ram'], volume_info,
|
||||||
databases, users, backup_id,
|
databases, users, backup_id,
|
||||||
config.config_contents)
|
config.config_contents, root_password)
|
||||||
|
|
||||||
if not self.db_info.task_status.is_error:
|
if not self.db_info.task_status.is_error:
|
||||||
self.update_db(task_status=inst_models.InstanceTasks.NONE)
|
self.update_db(task_status=inst_models.InstanceTasks.NONE)
|
||||||
|
@ -416,14 +416,15 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||||
|
|
||||||
def _guest_prepare(self, server, flavor_ram, volume_info,
|
def _guest_prepare(self, server, flavor_ram, volume_info,
|
||||||
databases, users, backup_id=None,
|
databases, users, backup_id=None,
|
||||||
config_contents=None):
|
config_contents=None, root_password=None):
|
||||||
LOG.info("Entering guest_prepare.")
|
LOG.info("Entering guest_prepare.")
|
||||||
# Now wait for the response from the create to do additional work
|
# Now wait for the response from the create to do additional work
|
||||||
self.guest.prepare(flavor_ram, databases, users,
|
self.guest.prepare(flavor_ram, databases, users,
|
||||||
device_path=volume_info['device_path'],
|
device_path=volume_info['device_path'],
|
||||||
mount_point=volume_info['mount_point'],
|
mount_point=volume_info['mount_point'],
|
||||||
backup_id=backup_id,
|
backup_id=backup_id,
|
||||||
config_contents=config_contents)
|
config_contents=config_contents,
|
||||||
|
root_password=root_password)
|
||||||
|
|
||||||
def _create_dns_entry(self):
|
def _create_dns_entry(self):
|
||||||
LOG.debug("%s: Creating dns entry for instance: %s" %
|
LOG.debug("%s: Creating dns entry for instance: %s" %
|
||||||
|
|
|
@ -113,6 +113,7 @@ class InstanceTestInfo(object):
|
||||||
instance_info = InstanceTestInfo()
|
instance_info = InstanceTestInfo()
|
||||||
dbaas = None # Rich client used throughout this test.
|
dbaas = None # Rich client used throughout this test.
|
||||||
dbaas_admin = None # Same as above, with admin privs.
|
dbaas_admin = None # Same as above, with admin privs.
|
||||||
|
ROOT_ON_CREATE = CONFIG.get('root_on_create', False)
|
||||||
VOLUME_SUPPORT = CONFIG.get('trove_volume_support', False)
|
VOLUME_SUPPORT = CONFIG.get('trove_volume_support', False)
|
||||||
EPHEMERAL_SUPPORT = not VOLUME_SUPPORT and CONFIG.get('device_path',
|
EPHEMERAL_SUPPORT = not VOLUME_SUPPORT and CONFIG.get('device_path',
|
||||||
'/dev/vdb') is not None
|
'/dev/vdb') is not None
|
||||||
|
@ -390,6 +391,8 @@ class CreateInstance(object):
|
||||||
# Check these attrs only are returned in create response
|
# Check these attrs only are returned in create response
|
||||||
expected_attrs = ['created', 'flavor', 'addresses', 'id', 'links',
|
expected_attrs = ['created', 'flavor', 'addresses', 'id', 'links',
|
||||||
'name', 'status', 'updated']
|
'name', 'status', 'updated']
|
||||||
|
if ROOT_ON_CREATE:
|
||||||
|
expected_attrs.append('password')
|
||||||
if VOLUME_SUPPORT:
|
if VOLUME_SUPPORT:
|
||||||
expected_attrs.append('volume')
|
expected_attrs.append('volume')
|
||||||
if CONFIG.trove_dns_support:
|
if CONFIG.trove_dns_support:
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
# Copyright 2013 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.common import cfg
|
||||||
|
|
||||||
|
from proboscis import before_class
|
||||||
|
from proboscis import after_class
|
||||||
|
from proboscis import test
|
||||||
|
from proboscis.asserts import assert_equal
|
||||||
|
from proboscis.asserts import assert_not_equal
|
||||||
|
from proboscis.asserts import assert_true
|
||||||
|
|
||||||
|
from trove import tests
|
||||||
|
from trove.tests.api.users import TestUsers
|
||||||
|
from trove.tests.api.instances import instance_info
|
||||||
|
from trove.tests import util
|
||||||
|
from trove.tests.api.databases import TestMysqlAccess
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
GROUP = "dbaas.api.root.oncreate"
|
||||||
|
|
||||||
|
|
||||||
|
@test(depends_on_classes=[TestMysqlAccess],
|
||||||
|
runs_after=[TestUsers],
|
||||||
|
groups=[tests.DBAAS_API, GROUP, tests.INSTANCES])
|
||||||
|
class TestRootOnCreate(object):
|
||||||
|
"""
|
||||||
|
Test 'CONF.root_on_create', which if True, creates the root user upon
|
||||||
|
database instance initialization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
root_enabled_timestamp = 'Never'
|
||||||
|
|
||||||
|
@before_class
|
||||||
|
def setUp(self):
|
||||||
|
self.orig_conf_value = CONF.root_on_create
|
||||||
|
CONF.root_on_create = True
|
||||||
|
self.dbaas = util.create_dbaas_client(instance_info.user)
|
||||||
|
self.dbaas_admin = util.create_dbaas_client(instance_info.admin_user)
|
||||||
|
self.history = self.dbaas_admin.management.root_enabled_history
|
||||||
|
self.enabled = self.dbaas.root.is_root_enabled
|
||||||
|
|
||||||
|
@after_class
|
||||||
|
def tearDown(self):
|
||||||
|
CONF.root_on_create = self.orig_conf_value
|
||||||
|
|
||||||
|
@test
|
||||||
|
def test_root_on_create(self):
|
||||||
|
"""Test that root is enabled after instance creation"""
|
||||||
|
enabled = self.enabled(instance_info.id).rootEnabled
|
||||||
|
assert_equal(200, self.dbaas.last_http_code)
|
||||||
|
assert_true(enabled)
|
||||||
|
|
||||||
|
@test(depends_on=[test_root_on_create])
|
||||||
|
def test_history_after_root_on_create(self):
|
||||||
|
"""Test that the timestamp in the root enabled history is set"""
|
||||||
|
self.root_enabled_timestamp = self.history(instance_info.id).enabled
|
||||||
|
assert_equal(200, self.dbaas.last_http_code)
|
||||||
|
assert_not_equal(self.root_enabled_timestamp, 'Never')
|
||||||
|
|
||||||
|
@test(depends_on=[test_history_after_root_on_create])
|
||||||
|
def test_reset_root(self):
|
||||||
|
"""Test that root reset does not alter the timestamp"""
|
||||||
|
orig_timestamp = self.root_enabled_timestamp
|
||||||
|
self.dbaas.root.create(instance_info.id)
|
||||||
|
assert_equal(200, self.dbaas.last_http_code)
|
||||||
|
self.root_enabled_timestamp = self.history(instance_info.id).enabled
|
||||||
|
assert_equal(200, self.dbaas.last_http_code)
|
||||||
|
assert_equal(orig_timestamp, self.root_enabled_timestamp)
|
||||||
|
|
||||||
|
@test(depends_on=[test_reset_root])
|
||||||
|
def test_root_still_enabled(self):
|
||||||
|
"""Test that after root was reset, it's still enabled."""
|
||||||
|
enabled = self.enabled(instance_info.id).rootEnabled
|
||||||
|
assert_equal(200, self.dbaas.last_http_code)
|
||||||
|
assert_true(enabled)
|
|
@ -86,6 +86,7 @@ class TestConfig(object):
|
||||||
"trove_volume_support": True,
|
"trove_volume_support": True,
|
||||||
"trove_max_volumes_per_user": 100,
|
"trove_max_volumes_per_user": 100,
|
||||||
"usage_endpoint": USAGE_ENDPOINT,
|
"usage_endpoint": USAGE_ENDPOINT,
|
||||||
|
"root_on_create": False
|
||||||
}
|
}
|
||||||
self._frozen_values = FrozenDict(self._values)
|
self._frozen_values = FrozenDict(self._values)
|
||||||
self._users = None
|
self._users = None
|
||||||
|
|
|
@ -208,7 +208,8 @@ class FakeGuest(object):
|
||||||
return self.users.get((username, hostname), None)
|
return self.users.get((username, hostname), None)
|
||||||
|
|
||||||
def prepare(self, memory_mb, databases, users, device_path=None,
|
def prepare(self, memory_mb, databases, users, device_path=None,
|
||||||
mount_point=None, backup_id=None, config_contents=None):
|
mount_point=None, backup_id=None, config_contents=None,
|
||||||
|
root_password=None):
|
||||||
from trove.instance.models import DBInstance
|
from trove.instance.models import DBInstance
|
||||||
from trove.instance.models import InstanceServiceStatus
|
from trove.instance.models import InstanceServiceStatus
|
||||||
from trove.instance.models import ServiceStatuses
|
from trove.instance.models import ServiceStatuses
|
||||||
|
|
|
@ -254,12 +254,12 @@ class ApiTest(testtools.TestCase):
|
||||||
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
|
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
|
||||||
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
|
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
|
||||||
'device_path', 'mount_point', 'backup_id',
|
'device_path', 'mount_point', 'backup_id',
|
||||||
'config_contents')
|
'config_contents', 'root_password')
|
||||||
|
|
||||||
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
|
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
|
||||||
|
|
||||||
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
|
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
|
||||||
'bkup-1232', 'cont')
|
'bkup-1232', 'cont', '1-2-3-4')
|
||||||
|
|
||||||
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)
|
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)
|
||||||
|
|
||||||
|
@ -269,11 +269,11 @@ class ApiTest(testtools.TestCase):
|
||||||
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
|
when(mock_conn).create_consumer(any(), any(), any()).thenReturn(None)
|
||||||
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
|
exp_msg = RpcMsgMatcher('prepare', 'memory_mb', 'databases', 'users',
|
||||||
'device_path', 'mount_point', 'backup_id',
|
'device_path', 'mount_point', 'backup_id',
|
||||||
'config_contents')
|
'config_contents', 'root_password')
|
||||||
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
|
when(rpc).cast(any(), any(), exp_msg).thenReturn(None)
|
||||||
|
|
||||||
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
|
self.api.prepare('2048', 'db1', 'user1', '/dev/vdt', '/mnt/opt',
|
||||||
'backup_id_123', 'cont')
|
'backup_id_123', 'cont', '1-2-3-4')
|
||||||
|
|
||||||
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)
|
self._verify_rpc_connection_and_cast(rpc, mock_conn, exp_msg)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue