summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2022-09-20 06:43:09 +0000
committerGerrit Code Review <review@openstack.org>2022-09-20 06:43:09 +0000
commit34b2e823438ceeeedb6b5c9b9e473d9327101110 (patch)
tree8c543cc2a5bf6b459f41857dfa6810fda8665f26
parent0850f71df666ec8ecefed80ef20b772e94cb3097 (diff)
parent61df006f4ab41aed23e614ac11874a92b1d10256 (diff)
downloadhorizon-23.0.0.tar.gz
Merge "volume-backup add incremental flag"23.0.0
-rw-r--r--openstack_dashboard/api/cinder.py13
-rw-r--r--openstack_dashboard/dashboards/project/backups/forms.py23
-rw-r--r--openstack_dashboard/dashboards/project/backups/tests.py143
3 files changed, 170 insertions, 9 deletions
diff --git a/openstack_dashboard/api/cinder.py b/openstack_dashboard/api/cinder.py
index 89319cd4a..e1daeb6a9 100644
--- a/openstack_dashboard/api/cinder.py
+++ b/openstack_dashboard/api/cinder.py
@@ -585,8 +585,9 @@ def volume_backup_get(request, backup_id):
return VolumeBackup(backup)
-def volume_backup_list(request):
- backups, _, __ = volume_backup_list_paged(request, paginate=False)
+def volume_backup_list(request, search_opts=None):
+ backups, _, __ = volume_backup_list_paged(request, paginate=False,
+ search_opts=search_opts)
return backups
@@ -625,7 +626,7 @@ def volume_backup_list_paged_with_page_menu(request, page_number=1,
@profiler.trace
def volume_backup_list_paged(request, marker=None, paginate=False,
- sort_dir="desc"):
+ sort_dir="desc", search_opts=None):
has_more_data = False
has_prev_data = False
backups = []
@@ -642,13 +643,13 @@ def volume_backup_list_paged(request, marker=None, paginate=False,
sort = 'created_at:' + sort_dir
for b in c_client.backups.list(limit=page_size + 1,
marker=marker,
- sort=sort):
+ sort=sort, search_opts=search_opts):
backups.append(VolumeBackup(b))
backups, has_more_data, has_prev_data = update_pagination(
backups, page_size, marker, sort_dir)
else:
- for b in c_client.backups.list():
+ for b in c_client.backups.list(search_opts=search_opts):
backups.append(VolumeBackup(b))
return backups, has_more_data, has_prev_data
@@ -661,6 +662,7 @@ def volume_backup_create(request,
name,
description,
force=False,
+ incremental=False,
snapshot_id=None):
# need to ensure the container name is not an empty
# string, but pass None to get the container name
@@ -671,6 +673,7 @@ def volume_backup_create(request,
name=name,
description=description,
snapshot_id=snapshot_id,
+ incremental=incremental,
force=force)
return VolumeBackup(backup)
diff --git a/openstack_dashboard/dashboards/project/backups/forms.py b/openstack_dashboard/dashboards/project/backups/forms.py
index 9a97e9a0c..c6f45d188 100644
--- a/openstack_dashboard/dashboards/project/backups/forms.py
+++ b/openstack_dashboard/dashboards/project/backups/forms.py
@@ -42,9 +42,30 @@ class CreateBackupForm(forms.SelfHandlingForm):
volume_id = forms.CharField(widget=forms.HiddenInput())
snapshot_id = forms.ThemableChoiceField(label=_("Backup Snapshot"),
required=False)
+ incremental = forms.BooleanField(
+ label=_("Incremental"),
+ required=False,
+ help_text=_("By default, a backup is created as a full backup. "
+ "Check this to do an incremental backup from latest "
+ "backup. Only available if a prior backup exists."))
def __init__(self, request, *args, **kwargs):
super().__init__(request, *args, **kwargs)
+ search_opts = {"volume_id": kwargs['initial']['volume_id'],
+ "status": "available"}
+ try:
+ if not api.cinder.volume_backup_list(request,
+ search_opts=search_opts):
+ self.fields.pop('incremental')
+ except Exception:
+ # Do not include incremental if list of prior backups fails
+ self.fields.pop('incremental')
+ msg = _('Unable to retrieve volume backup list '
+ 'for volume "%s", so incremental '
+ 'backup is disabled.') % search_opts['volume_id']
+
+ exceptions.handle(self.request, msg)
+
if kwargs['initial'].get('snapshot_id'):
snap_id = kwargs['initial']['snapshot_id']
try:
@@ -84,12 +105,14 @@ class CreateBackupForm(forms.SelfHandlingForm):
volume = api.cinder.volume_get(request, data['volume_id'])
snapshot_id = data['snapshot_id'] or None
force = False
+ incremental = data.get('incremental', False)
if volume.status == 'in-use':
force = True
backup = api.cinder.volume_backup_create(
request, data['volume_id'],
data['container_name'], data['name'],
data['description'], force=force,
+ incremental=incremental,
snapshot_id=snapshot_id
)
diff --git a/openstack_dashboard/dashboards/project/backups/tests.py b/openstack_dashboard/dashboards/project/backups/tests.py
index d769f10a8..89848cf9c 100644
--- a/openstack_dashboard/dashboards/project/backups/tests.py
+++ b/openstack_dashboard/dashboards/project/backups/tests.py
@@ -134,11 +134,13 @@ class VolumeBackupsViewTests(test.TestCase):
self.assertCountEqual(result, expected_backups)
@test.create_mocks({api.cinder: ('volume_backup_create',
+ 'volume_backup_list',
'volume_snapshot_list',
'volume_get')})
def test_create_backup_available(self):
volume = self.cinder_volumes.first()
backup = self.cinder_volume_backups.first()
+ self.mock_volume_backup_list.return_value = []
self.mock_volume_get.return_value = volume
self.mock_volume_backup_create.return_value = backup
@@ -168,17 +170,61 @@ class VolumeBackupsViewTests(test.TestCase):
backup.name,
backup.description,
force=False,
+ incremental=False,
+ snapshot_id=None)
+
+ @test.create_mocks({api.cinder: ('volume_backup_create',
+ 'volume_backup_list',
+ 'volume_snapshot_list',
+ 'volume_get')})
+ def test_create_backup_available_incremental(self):
+ volume = self.cinder_volumes.first()
+ backup = self.cinder_volume_backups.list()[1]
+ prior_backups = [self.cinder_volume_backups.list()[0]]
+ self.mock_volume_backup_list.return_value = prior_backups
+
+ self.mock_volume_get.return_value = volume
+ self.mock_volume_backup_create.return_value = backup
+
+ formData = {'method': 'CreateBackupForm',
+ 'tenant_id': self.tenant.id,
+ 'volume_id': volume.id,
+ 'container_name': backup.container_name,
+ 'name': backup.name,
+ 'incremental': True,
+ 'description': backup.description}
+ url = reverse('horizon:project:volumes:create_backup',
+ args=[volume.id])
+ res = self.client.post(url, formData)
+
+ self.assertNoFormErrors(res)
+ self.assertMessageCount(error=0, warning=0)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+ self.mock_volume_snapshot_list.assert_called_once_with(
+ test.IsHttpRequest(),
+ search_opts={'volume_id': volume.id})
+ self.mock_volume_get.assert_called_once_with(test.IsHttpRequest(),
+ volume.id)
+ self.mock_volume_backup_create.assert_called_once_with(
+ test.IsHttpRequest(),
+ volume.id,
+ backup.container_name,
+ backup.name,
+ backup.description,
+ force=False,
+ incremental=True,
snapshot_id=None)
@test.create_mocks(
{api.cinder: ('volume_backup_create', 'volume_snapshot_get',
- 'volume_get')})
+ 'volume_get', 'volume_backup_list')})
def test_create_backup_from_snapshot_table(self):
backup = self.cinder_volume_backups.list()[1]
volume = self.cinder_volumes.list()[4]
snapshot = self.cinder_volume_snapshots.list()[1]
self.mock_volume_backup_create.return_value = backup
self.mock_volume_get.return_value = volume
+ self.mock_volume_backup_list.return_value = []
self.mock_volume_snapshot_get.return_value = snapshot
formData = {'method': 'CreateBackupForm',
'tenant_id': self.tenant.id,
@@ -204,18 +250,20 @@ class VolumeBackupsViewTests(test.TestCase):
backup.name,
backup.description,
force=False,
+ incremental=False,
snapshot_id=backup.snapshot_id)
@test.create_mocks(
{api.cinder: ('volume_backup_create',
'volume_snapshot_list',
- 'volume_get')})
+ 'volume_get', 'volume_backup_list')})
def test_create_backup_from_snapshot_volume_table(self):
volume = self.cinder_volumes.list()[4]
backup = self.cinder_volume_backups.list()[1]
snapshots = self.cinder_volume_snapshots.list()[1:3]
self.mock_volume_backup_create.return_value = backup
self.mock_volume_get.return_value = volume
+ self.mock_volume_backup_list.return_value = []
self.mock_volume_snapshot_list.return_value = snapshots
formData = {'method': 'CreateBackupForm',
'tenant_id': self.tenant.id,
@@ -244,11 +292,12 @@ class VolumeBackupsViewTests(test.TestCase):
backup.name,
backup.description,
force=False,
+ incremental=False,
snapshot_id=backup.snapshot_id)
@test.create_mocks(
{api.cinder: ('volume_backup_create', 'volume_snapshot_list',
- 'volume_get')})
+ 'volume_get', 'volume_backup_list')})
def test_create_backup_in_use(self):
# The third volume in the cinder test volume data is in-use
volume = self.cinder_volumes.list()[2]
@@ -258,6 +307,7 @@ class VolumeBackupsViewTests(test.TestCase):
self.mock_volume_get.return_value = volume
self.mock_volume_backup_create.return_value = backup
self.mock_volume_snapshot_list.return_value = snapshots
+ self.mock_volume_backup_list.return_value = []
formData = {'method': 'CreateBackupForm',
'tenant_id': self.tenant.id,
'volume_id': volume.id,
@@ -283,7 +333,92 @@ class VolumeBackupsViewTests(test.TestCase):
backup.name,
backup.description,
force=True,
- snapshot_id=None)
+ snapshot_id=None,
+ incremental=False)
+
+ @test.create_mocks(
+ {api.cinder: ('volume_backup_create', 'volume_snapshot_list',
+ 'volume_get', 'volume_backup_list')})
+ def test_create_backup_in_use_incremental(self):
+ volume = self.cinder_volumes.list()[2]
+
+ backup = self.cinder_volume_backups.list()[1]
+ snapshots = []
+ self.mock_volume_get.return_value = volume
+ self.mock_volume_backup_create.return_value = backup
+ self.mock_volume_snapshot_list.return_value = snapshots
+ prior_backups = [self.cinder_volume_backups.list()[0]]
+ self.mock_volume_backup_list.return_value = prior_backups
+ formData = {'method': 'CreateBackupForm',
+ 'tenant_id': self.tenant.id,
+ 'volume_id': volume.id,
+ 'container_name': backup.container_name,
+ 'name': backup.name,
+ 'incremental': True,
+ 'description': backup.description}
+ url = reverse('horizon:project:volumes:create_backup',
+ args=[volume.id])
+
+ res = self.client.post(url, formData)
+ self.mock_volume_snapshot_list.assert_called_once_with(
+ test.IsHttpRequest(),
+ search_opts={'volume_id': volume.id})
+ self.assertNoFormErrors(res)
+ self.assertMessageCount(error=0, warning=0)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+ self.mock_volume_get.assert_called_once_with(test.IsHttpRequest(),
+ volume.id)
+ self.mock_volume_backup_create.assert_called_once_with(
+ test.IsHttpRequest(),
+ volume.id,
+ backup.container_name,
+ backup.name,
+ backup.description,
+ force=True,
+ snapshot_id=None,
+ incremental=True)
+
+ @test.create_mocks(
+ {api.cinder: ('volume_backup_create', 'volume_snapshot_list',
+ 'volume_get', 'volume_backup_list')})
+ def test_create_backup_in_use_incremental_set_false(self):
+ volume = self.cinder_volumes.list()[2]
+
+ backup = self.cinder_volume_backups.list()[1]
+ snapshots = []
+ self.mock_volume_get.return_value = volume
+ self.mock_volume_backup_create.return_value = backup
+ self.mock_volume_snapshot_list.return_value = snapshots
+ prior_backups = [self.cinder_volume_backups.list()[0]]
+ self.mock_volume_backup_list.return_value = prior_backups
+ formData = {'method': 'CreateBackupForm',
+ 'tenant_id': self.tenant.id,
+ 'volume_id': volume.id,
+ 'container_name': backup.container_name,
+ 'name': backup.name,
+ 'incremental': False,
+ 'description': backup.description}
+ url = reverse('horizon:project:volumes:create_backup',
+ args=[volume.id])
+
+ res = self.client.post(url, formData)
+ self.mock_volume_snapshot_list.assert_called_once_with(
+ test.IsHttpRequest(),
+ search_opts={'volume_id': volume.id})
+ self.assertNoFormErrors(res)
+ self.assertMessageCount(error=0, warning=0)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+ self.mock_volume_get.assert_called_once_with(test.IsHttpRequest(),
+ volume.id)
+ self.mock_volume_backup_create.assert_called_once_with(
+ test.IsHttpRequest(),
+ volume.id,
+ backup.container_name,
+ backup.name,
+ backup.description,
+ force=True,
+ snapshot_id=None,
+ incremental=False)
@test.create_mocks({api.cinder: ('volume_list',
'volume_snapshot_list',