Skip to content

Commit

Permalink
volume-backup add incremental flag
Browse files Browse the repository at this point in the history
Signed-off-by: Jesper Schmitz Mouridsen <jesper@schmitz.computer>
Change-Id: Ia99d045eda9a138cb0466930ccedf865d7a6b54f
  • Loading branch information
jsm222 committed Sep 2, 2022
1 parent 0a77f42 commit 61df006
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 9 deletions.
13 changes: 8 additions & 5 deletions openstack_dashboard/api/cinder.py
Expand Up @@ -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


Expand Down Expand Up @@ -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 = []
Expand All @@ -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
Expand All @@ -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
Expand All @@ -671,6 +673,7 @@ def volume_backup_create(request,
name=name,
description=description,
snapshot_id=snapshot_id,
incremental=incremental,
force=force)
return VolumeBackup(backup)

Expand Down
23 changes: 23 additions & 0 deletions openstack_dashboard/dashboards/project/backups/forms.py
Expand Up @@ -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:
Expand Down Expand Up @@ -84,12 +105,14 @@ def handle(self, request, data):
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
)

Expand Down
143 changes: 139 additions & 4 deletions openstack_dashboard/dashboards/project/backups/tests.py
Expand Up @@ -132,11 +132,13 @@ def test_backups_index_paginated_prev_page(self):
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
Expand Down Expand Up @@ -166,17 +168,61 @@ def test_create_backup_available(self):
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,
Expand All @@ -202,18 +248,20 @@ def test_create_backup_from_snapshot_table(self):
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,
Expand Down Expand Up @@ -242,11 +290,12 @@ def test_create_backup_from_snapshot_volume_table(self):
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]
Expand All @@ -256,6 +305,7 @@ def test_create_backup_in_use(self):
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,
Expand All @@ -281,7 +331,92 @@ def test_create_backup_in_use(self):
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',
Expand Down

0 comments on commit 61df006

Please sign in to comment.