Merge "Restrict launch fields when restoring from backup"
This commit is contained in:
commit
f278d12a57
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- Adds support for restricting the launch instance datastore field
|
||||||
|
to the datastore and datastore version that the backup is
|
||||||
|
relevant to. It also populates the restored backup as the only
|
||||||
|
option in the advanced step backup field.
|
@ -12,19 +12,27 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django import http
|
from django import http
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from mox3.mox import IsA # noqa
|
from mox3.mox import IsA # noqa
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from openstack_auth import policy
|
from openstack_auth import policy
|
||||||
|
from openstack_dashboard import api as dash_api
|
||||||
|
|
||||||
|
from troveclient import common
|
||||||
|
|
||||||
from trove_dashboard import api
|
from trove_dashboard import api
|
||||||
|
from trove_dashboard.content.databases.workflows import create_instance
|
||||||
from trove_dashboard.test import helpers as test
|
from trove_dashboard.test import helpers as test
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:project:database_backups:index')
|
INDEX_URL = reverse('horizon:project:database_backups:index')
|
||||||
BACKUP_URL = reverse('horizon:project:database_backups:create')
|
BACKUP_URL = reverse('horizon:project:database_backups:create')
|
||||||
DETAILS_URL = reverse('horizon:project:database_backups:detail', args=['id'])
|
DETAILS_URL = reverse('horizon:project:database_backups:detail', args=['id'])
|
||||||
|
RESTORE_URL = reverse('horizon:project:databases:launch')
|
||||||
|
|
||||||
|
|
||||||
class DatabasesBackupsTests(test.TestCase):
|
class DatabasesBackupsTests(test.TestCase):
|
||||||
@ -191,3 +199,63 @@ class DatabasesBackupsTests(test.TestCase):
|
|||||||
args=[incr_backup.id])
|
args=[incr_backup.id])
|
||||||
res = self.client.get(url)
|
res = self.client.get(url)
|
||||||
self.assertTemplateUsed(res, 'project/database_backups/details.html')
|
self.assertTemplateUsed(res, 'project/database_backups/details.html')
|
||||||
|
|
||||||
|
@test.create_stubs({
|
||||||
|
api.trove: ('backup_get', 'backup_list', 'configuration_list',
|
||||||
|
'datastore_flavors', 'datastore_list',
|
||||||
|
'datastore_version_list', 'instance_list'),
|
||||||
|
dash_api.cinder: ('volume_type_list',),
|
||||||
|
dash_api.neutron: ('network_list',),
|
||||||
|
dash_api.nova: ('availability_zone_list',),
|
||||||
|
policy: ('check',),
|
||||||
|
})
|
||||||
|
def test_restore_backup(self):
|
||||||
|
policy.check((), IsA(http.HttpRequest)).MultipleTimes().AndReturn(True)
|
||||||
|
backup = self.database_backups.first()
|
||||||
|
api.trove.backup_get(IsA(http.HttpRequest), IsA(six.text_type)) \
|
||||||
|
.AndReturn(self.database_backups.first())
|
||||||
|
api.trove.backup_list(IsA(http.HttpRequest)).AndReturn(
|
||||||
|
self.database_backups.list())
|
||||||
|
api.trove.configuration_list(IsA(http.HttpRequest)) \
|
||||||
|
.AndReturn(self.database_configurations.list())
|
||||||
|
api.trove.datastore_flavors(IsA(http.HttpRequest),
|
||||||
|
IsA(six.string_types),
|
||||||
|
IsA(six.string_types)) \
|
||||||
|
.AndReturn(self.flavors.list())
|
||||||
|
api.trove.datastore_list(IsA(http.HttpRequest)) \
|
||||||
|
.AndReturn(self.datastores.list())
|
||||||
|
api.trove.datastore_version_list(IsA(http.HttpRequest),
|
||||||
|
backup.datastore['type']) \
|
||||||
|
.AndReturn(self.datastore_versions.list())
|
||||||
|
api.trove.instance_list(IsA(http.HttpRequest), marker=None) \
|
||||||
|
.AndReturn(common.Paginated(self.databases.list()))
|
||||||
|
dash_api.cinder.volume_type_list(IsA(http.HttpRequest)).AndReturn([])
|
||||||
|
dash_api.neutron.network_list(IsA(http.HttpRequest),
|
||||||
|
tenant_id=self.tenant.id,
|
||||||
|
shared=False).\
|
||||||
|
AndReturn(self.networks.list()[:1])
|
||||||
|
dash_api.nova.availability_zone_list(IsA(http.HttpRequest)) \
|
||||||
|
.AndReturn(self.availability_zones.list())
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
url = RESTORE_URL + '?backup=%s' % self.database_backups.first().id
|
||||||
|
res = self.client.get(url)
|
||||||
|
self.assertTemplateUsed(res, 'project/databases/launch.html')
|
||||||
|
|
||||||
|
set_instance_detail_step = \
|
||||||
|
[step for step in res.context_data['workflow'].steps
|
||||||
|
if isinstance(step, create_instance.SetInstanceDetails)][0]
|
||||||
|
fields = set_instance_detail_step.action.fields
|
||||||
|
self.assertTrue(len(fields['datastore'].choices), 1)
|
||||||
|
text = 'mysql - 5.6'
|
||||||
|
choice = fields['datastore'].choices[0]
|
||||||
|
self.assertTrue(choice[0], binascii.hexlify(text))
|
||||||
|
self.assertTrue(choice[1], text)
|
||||||
|
|
||||||
|
advanced_step = [step for step in res.context_data['workflow'].steps
|
||||||
|
if isinstance(step, create_instance.Advanced)][0]
|
||||||
|
fields = advanced_step.action.fields
|
||||||
|
self.assertTrue(len(fields['initial_state'].choices), 1)
|
||||||
|
choice = fields['initial_state'].choices[0]
|
||||||
|
self.assertTrue(choice[0], 'backup')
|
||||||
|
self.assertTrue(choice[1], _('Restore from Backup'))
|
||||||
|
@ -64,6 +64,11 @@ class SetInstanceDetailsAction(workflows.Action):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
if args:
|
||||||
|
self.backup_id = args[0].get('backup', None)
|
||||||
|
else:
|
||||||
|
self.backup_id = None
|
||||||
|
|
||||||
super(SetInstanceDetailsAction, self).__init__(request,
|
super(SetInstanceDetailsAction, self).__init__(request,
|
||||||
*args,
|
*args,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
@ -184,11 +189,24 @@ class SetInstanceDetailsAction(workflows.Action):
|
|||||||
LOG.exception("Exception while obtaining datastore version list")
|
LOG.exception("Exception while obtaining datastore version list")
|
||||||
self._datastore_versions = []
|
self._datastore_versions = []
|
||||||
|
|
||||||
|
@memoized.memoized_method
|
||||||
|
def get_backup(self, request, backup_id):
|
||||||
|
try:
|
||||||
|
return api.trove.backup_get(request, backup_id)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception("Exception while obtaining backup information")
|
||||||
|
return None
|
||||||
|
|
||||||
def populate_datastore_choices(self, request, context):
|
def populate_datastore_choices(self, request, context):
|
||||||
choices = ()
|
choices = ()
|
||||||
datastores = self.datastores(request)
|
datastores = self.datastores(request)
|
||||||
if datastores is not None:
|
if datastores is not None:
|
||||||
|
if self.backup_id:
|
||||||
|
backup = self.get_backup(request, self.backup_id)
|
||||||
for ds in datastores:
|
for ds in datastores:
|
||||||
|
if self.backup_id:
|
||||||
|
if ds.name != backup.datastore['type']:
|
||||||
|
continue
|
||||||
versions = self.datastore_versions(request, ds.name)
|
versions = self.datastore_versions(request, ds.name)
|
||||||
if versions:
|
if versions:
|
||||||
# only add to choices if datastore has at least one version
|
# only add to choices if datastore has at least one version
|
||||||
@ -196,6 +214,9 @@ class SetInstanceDetailsAction(workflows.Action):
|
|||||||
for v in versions:
|
for v in versions:
|
||||||
if hasattr(v, 'active') and not v.active:
|
if hasattr(v, 'active') and not v.active:
|
||||||
continue
|
continue
|
||||||
|
if self.backup_id:
|
||||||
|
if v.id != backup.datastore['version_id']:
|
||||||
|
continue
|
||||||
selection_text = self._build_datastore_display_text(
|
selection_text = self._build_datastore_display_text(
|
||||||
ds.name, v.name)
|
ds.name, v.name)
|
||||||
widget_text = self._build_widget_field_name(
|
widget_text = self._build_widget_field_name(
|
||||||
@ -350,6 +371,18 @@ class AdvancedAction(workflows.Action):
|
|||||||
'data-initial_state-master': _('Replica Count')
|
'data-initial_state-master': _('Replica Count')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
if args[0]:
|
||||||
|
self.backup_id = args[0].get('backup', None)
|
||||||
|
else:
|
||||||
|
self.backup_id = None
|
||||||
|
|
||||||
|
super(AdvancedAction, self).__init__(request, *args, **kwargs)
|
||||||
|
|
||||||
|
if self.backup_id:
|
||||||
|
self.fields['initial_state'].choices = [('backup',
|
||||||
|
_('Restore from Backup'))]
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
name = _("Advanced")
|
name = _("Advanced")
|
||||||
help_text_template = "project/databases/_launch_advanced_help.html"
|
help_text_template = "project/databases/_launch_advanced_help.html"
|
||||||
@ -374,9 +407,13 @@ class AdvancedAction(workflows.Action):
|
|||||||
|
|
||||||
def populate_backup_choices(self, request, context):
|
def populate_backup_choices(self, request, context):
|
||||||
try:
|
try:
|
||||||
|
choices = []
|
||||||
backups = api.trove.backup_list(request)
|
backups = api.trove.backup_list(request)
|
||||||
choices = [(b.id, b.name) for b in backups
|
for b in backups:
|
||||||
if b.status == 'COMPLETED']
|
if self.backup_id and b.id != self.backup_id:
|
||||||
|
continue
|
||||||
|
if b.status == 'COMPLETED':
|
||||||
|
choices.append((b.id, b.name))
|
||||||
except Exception:
|
except Exception:
|
||||||
choices = []
|
choices = []
|
||||||
|
|
||||||
|
@ -229,6 +229,11 @@ BACKUP_ONE = {
|
|||||||
"size": 0.13,
|
"size": 0.13,
|
||||||
"id": "0edb3c14-8919-4583-9add-00df9e524081",
|
"id": "0edb3c14-8919-4583-9add-00df9e524081",
|
||||||
"description": "Long description of backup",
|
"description": "Long description of backup",
|
||||||
|
"datastore": {
|
||||||
|
"type": "mysql",
|
||||||
|
"version": "5.6",
|
||||||
|
"version_id": "500a6d52-8347-4e00-8e4c-f4fa9cf96ae9"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
BACKUP_TWO = {
|
BACKUP_TWO = {
|
||||||
@ -241,6 +246,11 @@ BACKUP_TWO = {
|
|||||||
"size": 0.13,
|
"size": 0.13,
|
||||||
"id": "e4602a3c-2bca-478f-b059-b6c215510fb4",
|
"id": "e4602a3c-2bca-478f-b059-b6c215510fb4",
|
||||||
"description": "Longer description of backup",
|
"description": "Longer description of backup",
|
||||||
|
"datastore": {
|
||||||
|
"type": "mysql",
|
||||||
|
"version": "5.6",
|
||||||
|
"version_id": "500a6d52-8347-4e00-8e4c-f4fa9cf96ae9"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
BACKUP_TWO_INC = {
|
BACKUP_TWO_INC = {
|
||||||
@ -254,6 +264,11 @@ BACKUP_TWO_INC = {
|
|||||||
"id": "e4602a3c-2bca-478f-b059-b6c215510fb5",
|
"id": "e4602a3c-2bca-478f-b059-b6c215510fb5",
|
||||||
"description": "Longer description of backup",
|
"description": "Longer description of backup",
|
||||||
"parent_id": "e4602a3c-2bca-478f-b059-b6c215510fb4",
|
"parent_id": "e4602a3c-2bca-478f-b059-b6c215510fb4",
|
||||||
|
"datastore": {
|
||||||
|
"type": "mysql",
|
||||||
|
"version": "5.6",
|
||||||
|
"version_id": "500a6d52-8347-4e00-8e4c-f4fa9cf96ae9"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_ONE = {
|
CONFIG_ONE = {
|
||||||
|
Loading…
Reference in New Issue
Block a user