Add support for root enable and show
Added an entry in the instance details panel to show whether root has ever been enabled for the instance. Added a manage root access screen to allow the user to enable the root user or reset the root password. The manage root access screen is accessed from a row menu option in the database instances table. Change-Id: I781f55f6184e0b83fe56fce6143269aa48b1c46d Co-Authored-By: Duk Loi <duk@tesora.com> Implements: blueprint trove-enable-root-support
This commit is contained in:
parent
2f1ea93f68
commit
b3c0f4dbfd
@ -203,6 +203,15 @@ def flavor_get(request, flavor_id):
|
||||
return troveclient(request).flavors.get(flavor_id)
|
||||
|
||||
|
||||
def root_enable(request, instance_ids):
|
||||
username, password = troveclient(request).root.create(instance_ids[0])
|
||||
return username, password
|
||||
|
||||
|
||||
def root_show(request, instance_id):
|
||||
return troveclient(request).root.is_root_enabled(instance_id)
|
||||
|
||||
|
||||
def users_list(request, instance_id):
|
||||
return troveclient(request).users.list(instance_id)
|
||||
|
||||
|
@ -23,6 +23,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.templatetags import sizeformat
|
||||
from horizon.utils import filters
|
||||
@ -409,6 +410,64 @@ class ResizeInstance(tables.LinkAction):
|
||||
return urlresolvers.reverse(self.url, args=[instance_id])
|
||||
|
||||
|
||||
class RootAction(tables.Action):
|
||||
def handle(self, table, request, obj_ids):
|
||||
try:
|
||||
username, password = api.trove.root_enable(request, obj_ids)
|
||||
table.data[0].enabled = True
|
||||
table.data[0].password = password
|
||||
except Exception:
|
||||
messages.error(request, _('There was a problem enabling root.'))
|
||||
|
||||
|
||||
class EnableRootAction(RootAction):
|
||||
name = "enable_root_action"
|
||||
verbose_name = _("Enable Root")
|
||||
|
||||
def allowed(self, request, instance):
|
||||
enabled = api.trove.root_show(request, instance.id)
|
||||
return not enabled.rootEnabled
|
||||
|
||||
|
||||
class ResetRootAction(RootAction):
|
||||
name = "reset_root_action"
|
||||
verbose_name = _("Reset Password")
|
||||
|
||||
def allowed(self, request, instance):
|
||||
enabled = api.trove.root_show(request, instance.id)
|
||||
return enabled.rootEnabled
|
||||
|
||||
|
||||
class ManageRoot(tables.LinkAction):
|
||||
name = "manage_root_action"
|
||||
verbose_name = _("Manage Root Access")
|
||||
url = "horizon:project:databases:manage_root"
|
||||
|
||||
def allowed(self, request, instance):
|
||||
return instance.status in ACTIVE_STATES
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
instance_id = self.table.get_object_id(datum)
|
||||
return urlresolvers.reverse(self.url, args=[instance_id])
|
||||
|
||||
|
||||
class ManageRootTable(tables.DataTable):
|
||||
name = tables.Column('name', verbose_name=_('Instance Name'))
|
||||
enabled = tables.Column('enabled', verbose_name=_('Root Enabled'),
|
||||
filters=(d_filters.yesno, d_filters.capfirst),
|
||||
help_text=_("Status if root was ever enabled "
|
||||
"for an instance."))
|
||||
password = tables.Column('password', verbose_name=_('Password'),
|
||||
help_text=_("Password is only visible "
|
||||
"immediately after the root is "
|
||||
"enabled or reset."))
|
||||
|
||||
class Meta(object):
|
||||
name = "manage_root"
|
||||
verbose_name = _("Manage Root")
|
||||
row_actions = (EnableRootAction, ResetRootAction,)
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
@ -531,6 +590,7 @@ class InstancesTable(tables.DataTable):
|
||||
row_actions = (CreateBackup,
|
||||
ResizeVolume,
|
||||
ResizeInstance,
|
||||
ManageRoot,
|
||||
RestartInstance,
|
||||
DetachReplica,
|
||||
DeleteInstance)
|
||||
|
@ -26,7 +26,16 @@ class OverviewTab(tabs.Tab):
|
||||
slug = "overview"
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"instance": self.tab_group.kwargs['instance']}
|
||||
instance = self.tab_group.kwargs['instance']
|
||||
context = {"instance": instance}
|
||||
try:
|
||||
root_show = api.trove.root_show(request, instance.id)
|
||||
context["root_enabled"] = template.defaultfilters.yesno(
|
||||
root_show.rootEnabled)
|
||||
except Exception:
|
||||
context["root_enabled"] = _('Unable to obtain information on '
|
||||
'root user')
|
||||
return context
|
||||
|
||||
def get_template_name(self, request):
|
||||
instance = self.tab_group.kwargs['instance']
|
||||
|
@ -12,6 +12,8 @@
|
||||
<dd>{{ instance.datastore.version }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ instance.status|title }}</dd>
|
||||
<dt>{% trans "Root Enabled" %}</dt>
|
||||
<dd>{{ root_enabled|capfirst }}</dd>
|
||||
</dl>
|
||||
|
||||
<h4>{% trans "Specs" %}</h4>
|
||||
|
@ -0,0 +1,14 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block main %}
|
||||
<hr>
|
||||
<div class="help_text">
|
||||
{% trans "Note: Enable root access on an instance. If the root user is already enabled then a new password is generated." %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -400,6 +400,141 @@ class DatabaseTests(test.TestCase):
|
||||
res = self.client.post(url, post)
|
||||
self.assertEqual(res.status_code, 302)
|
||||
|
||||
@test.create_stubs({api.trove: ('instance_get', 'root_show')})
|
||||
def test_show_root(self):
|
||||
database = self.databases.first()
|
||||
database.id = u'id'
|
||||
user = self.database_user_roots.first()
|
||||
|
||||
api.trove.instance_get(IsA(http.HttpRequest), IsA(unicode))\
|
||||
.AndReturn(database)
|
||||
|
||||
api.trove.root_show(IsA(http.HttpRequest), database.id) \
|
||||
.MultipleTimes().AndReturn(user)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
res = self.client.get(url)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/databases/manage_root.html')
|
||||
|
||||
@test.create_stubs({api.trove: ('instance_get', 'root_show')})
|
||||
def test_show_root_exception(self):
|
||||
database = self.databases.first()
|
||||
|
||||
api.trove.instance_get(IsA(http.HttpRequest), IsA(unicode))\
|
||||
.AndReturn(database)
|
||||
|
||||
api.trove.root_show(IsA(http.HttpRequest), u'id') \
|
||||
.AndRaise(self.exceptions.trove)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
res = self.client.get(url)
|
||||
self.assertRedirectsNoFollow(res, DETAILS_URL)
|
||||
|
||||
@test.create_stubs({api.trove: ('root_enable',)})
|
||||
def test_enable_root(self):
|
||||
api.trove.root_enable(IsA(http.HttpRequest), [u'id']) \
|
||||
.AndReturn(("root", "password"))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
form_data = {"action": "manage_root__enable_root_action__%s" % 'id'}
|
||||
req = self.factory.post(url, form_data)
|
||||
|
||||
kwargs = {'instance_id': 'id'}
|
||||
|
||||
enable_root_info_list = []
|
||||
enable_root_info = views.EnableRootInfo('id', 'inst1', False, '')
|
||||
enable_root_info_list.append(enable_root_info)
|
||||
|
||||
table = tables.ManageRootTable(req, enable_root_info_list, **kwargs)
|
||||
table.maybe_handle()
|
||||
|
||||
self.assertEqual(table.data[0].enabled, True)
|
||||
self.assertEqual(table.data[0].password, "password")
|
||||
|
||||
@test.create_stubs({api.trove: ('root_enable',)})
|
||||
def test_enable_root_exception(self):
|
||||
api.trove.root_enable(IsA(http.HttpRequest), [u'id']) \
|
||||
.AndRaise(self.exceptions.trove)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
form_data = {"action": "manage_root__enable_root_action__%s" % 'id'}
|
||||
req = self.factory.post(url, form_data)
|
||||
|
||||
kwargs = {'instance_id': 'id'}
|
||||
|
||||
enable_root_info_list = []
|
||||
enable_root_info = views.EnableRootInfo('id', 'inst1', False, '')
|
||||
enable_root_info_list.append(enable_root_info)
|
||||
|
||||
table = tables.ManageRootTable(req, enable_root_info_list, **kwargs)
|
||||
table.maybe_handle()
|
||||
|
||||
self.assertNotEqual(table.data[0].enabled, True)
|
||||
self.assertNotEqual(table.data[0].password, "password")
|
||||
|
||||
@test.create_stubs({api.trove: ('root_enable',)})
|
||||
def test_reset_root(self):
|
||||
api.trove.root_enable(IsA(http.HttpRequest), [u'id']) \
|
||||
.AndReturn(("root", "newpassword"))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
form_data = {"action": "manage_root__reset_root_action__%s" % 'id'}
|
||||
req = self.factory.post(url, form_data)
|
||||
|
||||
kwargs = {'instance_id': 'id'}
|
||||
|
||||
enable_root_info_list = []
|
||||
enable_root_info = views.EnableRootInfo(
|
||||
'id', 'inst1', True, 'password')
|
||||
enable_root_info_list.append(enable_root_info)
|
||||
|
||||
table = tables.ManageRootTable(req, enable_root_info_list, **kwargs)
|
||||
table.maybe_handle()
|
||||
|
||||
self.assertEqual(table.data[0].enabled, True)
|
||||
self.assertEqual(table.data[0].password, "newpassword")
|
||||
|
||||
@test.create_stubs({api.trove: ('root_enable',)})
|
||||
def test_reset_root_exception(self):
|
||||
api.trove.root_enable(IsA(http.HttpRequest), [u'id']) \
|
||||
.AndRaise(self.exceptions.trove)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:databases:manage_root',
|
||||
args=['id'])
|
||||
form_data = {"action": "manage_root__reset_root_action__%s" % 'id'}
|
||||
req = self.factory.post(url, form_data)
|
||||
|
||||
kwargs = {'instance_id': 'id'}
|
||||
|
||||
enable_root_info_list = []
|
||||
enable_root_info = views.EnableRootInfo(
|
||||
'id', 'inst1', True, 'password')
|
||||
enable_root_info_list.append(enable_root_info)
|
||||
|
||||
table = tables.ManageRootTable(req, enable_root_info_list, **kwargs)
|
||||
table.maybe_handle()
|
||||
|
||||
self.assertEqual(table.data[0].enabled, True)
|
||||
self.assertNotEqual(table.data[0].password, "newpassword")
|
||||
|
||||
@test.create_stubs(
|
||||
{api.trove: ('instance_get', 'flavor_get', 'users_list',
|
||||
'user_list_access', 'user_delete')})
|
||||
|
@ -39,4 +39,6 @@ urlpatterns = patterns(
|
||||
name='access_detail'),
|
||||
url(INSTANCES % 'create_database', views.CreateDatabaseView.as_view(),
|
||||
name='create_database'),
|
||||
url(INSTANCES % 'manage_root', views.ManageRootView.as_view(),
|
||||
name='manage_root'),
|
||||
)
|
||||
|
@ -355,3 +355,50 @@ class ResizeInstanceView(horizon_forms.ModalFormView):
|
||||
'flavor_name', ''),
|
||||
'flavors': self.get_flavors()})
|
||||
return initial
|
||||
|
||||
|
||||
class EnableRootInfo(object):
|
||||
def __init__(self, instance_id, instance_name, enabled, password=None):
|
||||
self.id = instance_id
|
||||
self.name = instance_name
|
||||
self.enabled = enabled
|
||||
self.password = password
|
||||
|
||||
|
||||
class ManageRootView(horizon_tables.DataTableView):
|
||||
table_class = tables.ManageRootTable
|
||||
template_name = 'project/databases/manage_root.html'
|
||||
page_title = _("Manage Root Access")
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_data(self):
|
||||
instance_id = self.kwargs['instance_id']
|
||||
try:
|
||||
instance = api.trove.instance_get(self.request, instance_id)
|
||||
except Exception:
|
||||
redirect = reverse('horizon:project:databases:detail',
|
||||
args=[instance_id])
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve instance details.'),
|
||||
redirect=redirect)
|
||||
try:
|
||||
enabled = api.trove.root_show(self.request, instance_id)
|
||||
except Exception:
|
||||
redirect = reverse('horizon:project:databases:detail',
|
||||
args=[instance_id])
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to determine if instance root '
|
||||
'is enabled.'),
|
||||
redirect=redirect)
|
||||
|
||||
root_enabled_list = []
|
||||
root_enabled_info = EnableRootInfo(instance.id,
|
||||
instance.name,
|
||||
enabled.rootEnabled)
|
||||
root_enabled_list.append(root_enabled_info)
|
||||
return root_enabled_list
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ManageRootView, self).get_context_data(**kwargs)
|
||||
context['instance_id'] = self.kwargs['instance_id']
|
||||
return context
|
||||
|
@ -213,6 +213,10 @@ USER_ONE = {
|
||||
"databases": [DATABASE_DATA_ONE["name"]],
|
||||
}
|
||||
|
||||
USER_ROOT_ONE = {
|
||||
"name": "root",
|
||||
"rootEnabled": True
|
||||
}
|
||||
|
||||
USER_DB_ONE = {
|
||||
"name": "db1",
|
||||
@ -335,6 +339,8 @@ def data(TEST):
|
||||
user1 = users.User(users.Users(None), USER_ONE)
|
||||
user_db1 = databases.Database(databases.Databases(None),
|
||||
USER_DB_ONE)
|
||||
user_root1 = databases.Database(databases.Databases(None),
|
||||
USER_ROOT_ONE)
|
||||
|
||||
datastore1 = datastores.Datastore(datastores.Datastores(None),
|
||||
DATASTORE_ONE)
|
||||
@ -368,6 +374,7 @@ def data(TEST):
|
||||
TEST.database_backups = utils.TestDataContainer()
|
||||
TEST.database_users = utils.TestDataContainer()
|
||||
TEST.database_user_dbs = utils.TestDataContainer()
|
||||
TEST.database_user_roots = utils.TestDataContainer()
|
||||
TEST.database_flavors = utils.TestDataContainer()
|
||||
|
||||
TEST.databases.add(database1)
|
||||
@ -377,6 +384,7 @@ def data(TEST):
|
||||
TEST.database_backups.add(bkup3)
|
||||
TEST.database_users.add(user1)
|
||||
TEST.database_user_dbs.add(user_db1)
|
||||
TEST.database_user_roots.add(user_root1)
|
||||
TEST.datastores = utils.TestDataContainer()
|
||||
TEST.datastores.add(datastore1)
|
||||
TEST.datastores.add(datastore_mongodb)
|
||||
|
Loading…
Reference in New Issue
Block a user