def purge_old_workspaces(): """ Purge workspaces older than X days @return: count of workspaces deleted """ database = helpers.connect_db() config = helpers.get_db_config() projects_in_db = Database.get_documents_by_type(database, doc_type='project') if not projects_in_db: return 0 count = 0 deleted_workspaces = list() for project in projects_in_db: workspaces_in_project = Database.get_workspaces_by_project(database, project=project['name']) for workspace in workspaces_in_project: # ontap doesn't provide last_access_timestamp for volumes # hence, snapdiff latest snapshot with snapshot X days older \ # to find if workspace is active ontap = OntapService(config['ontap_api'], config['ontap_apiuser'], config['ontap_apipass'], config['ontap_svm_name'], config['ontap_aggr_name'], config['ontap_data_ip']) deleted, error = ontap.get_snapdiff_and_delete( volume_name=workspace.value, count=project['workspace_purge_limit']) # delete inconsistent or old workspace that exceeded purge limit if error is not None or deleted is True: workspace_doc = Database.get_document_by_name(database, workspace.value) database.delete(workspace_doc) deleted_workspaces.append(workspace.value) logging.info("Purge: deleted workspace %s from DB", workspace.value) count += 1 return count, deleted_workspaces
class TestONTAPService(unittest.TestCase): ''' Test ONTAP Service ''' def setUp(self): api_credentials = {'api_server': 'ip-address.com', 'username': '******', 'password': '******'} self.ontap = OntapService(api_credentials, 'vserver-test', 'aggregate-test') @patch('web_service.ontap.ontap_service.Volume.delete_snapshot') def test_delete_snapshot(self, mock_delete_snapshot): mock_delete_snapshot.return_value = 'COMPLETED', '' logging.basicConfig(level='INFO') volume_name = 'test_volume_for_ontap_services' [response] = self.ontap.delete_snapshot(volume_name, 'test_snapshot') self.assertEqual(response['code'], 201) @patch('logging.warning') @patch('web_service.ontap.ontap_service.Volume.delete_snapshot') def test_delete_snapshot_fail(self, mock_delete_snapshot, mock_logger): ''' Log ONTAP delete error if snapshot is active ''' mock_delete_snapshot.return_value = 'FAILED', 'snapshot has not expired or is locked' logging.basicConfig(level='WARNING') volume_name = 'test_volume_for_ontap_services' [response] = self.ontap.delete_snapshot(volume_name, 'test_snapshot') mock_logger.assert_called_with( 'Failed to delete snapshot %s. Most likely clone is in use. error: %s', 'test_snapshot', 'snapshot has not expired or is locked' ) self.assertEqual(response['code'], 400) @patch('logging.error') @patch('web_service.ontap.ontap_service.Volume.delete_snapshot') def test_delete_snapshot_fail_other(self, mock_delete_snapshot, mock_logger): ''' Log ONTAP delete error for other reasons ''' mock_delete_snapshot.return_value = 'FAILED', 'other error' logging.basicConfig(level='WARNING') volume_name = 'test_volume_for_ontap_services' [response] = self.ontap.delete_snapshot(volume_name, 'test_snapshot') mock_logger.assert_called_with( 'Failed to delete snapshot %s, unexpected error: %s', 'test_snapshot', 'other error' ) self.assertEqual(response['code'], 400) @patch('web_service.ontap.ontap_service.OntapService.delete_volume') @patch('web_service.ontap.ontap_service.Volume.get_snapdiff') @patch('web_service.ontap.ontap_service.OntapService.get_oldest_and_latest_snapshots') def test_get_snapdiff_and_delete(self, mock_get_oldest_latest_snapshots, mock_get_snapdiff, mock_delete_volume): mock_delete_volume.return_value = mocks.CREATE_VOL_RETURN_VAL mock_get_snapdiff.return_value = 0 mock_get_oldest_latest_snapshots.return_value = ('weekly.5678', '1503002079'), ('weekly.1234', '1503002065') deleted, message = self.ontap.get_snapdiff_and_delete('test', 100) self.assertEqual(deleted, True) self.assertTrue("test has been inactive for" in message) @patch('web_service.ontap.ontap_service.Volume.get_snapdiff') @patch('web_service.ontap.ontap_service.OntapService.get_oldest_and_latest_snapshots') def test_get_snapdiff_and_delete_active(self, mock_get_oldest_latest_snapshots, mock_get_snapdiff): mock_get_snapdiff.return_value = 1 mock_get_oldest_latest_snapshots.return_value = ('weekly.5678', '1503002079'), ('weekly.1234', '1503002065') deleted, message = self.ontap.get_snapdiff_and_delete('test', 2) self.assertEqual(deleted, False) self.assertTrue("test is active" in message) @patch('web_service.ontap.ontap_service.OntapService.get_oldest_and_latest_snapshots') def test_get_snapdiff_and_delete_new(self, mock_get_oldest_latest_snapshots): mock_get_oldest_latest_snapshots.return_value = None, None deleted, message = self.ontap.get_snapdiff_and_delete('test', 2) self.assertEqual(deleted, False) self.assertTrue("Workspace is less than" in message) @patch('web_service.ontap.ontap_service.OntapService.get_snapshot_list') def test_get_oldest_and_latest_snapshots(self, mock_get_snapshot_list): today = datetime.now() two_days_old_epoch = (today - timedelta(days=2)).strftime('%s') today_epoch = today.strftime('%s') two_days_snap = ('two_days_old', two_days_old_epoch) today_snap = ('today', today_epoch) mock_get_snapshot_list.return_value = [ two_days_snap, today_snap, ], "" recent, old = self.ontap.get_oldest_and_latest_snapshots('test', 1) self.assertEqual(two_days_snap, old) self.assertEqual(today_snap, recent) @patch('web_service.ontap.ontap_service.OntapService.get_snapshot_list') def test_get_oldest_and_latest_snapshots_none(self, mock_get_snapshot_list): today = datetime.now() one_day_old_epoch = (today - timedelta(days=1)).strftime('%s') today_epoch = today.strftime('%s') one_day_snap = ('one_day_old', one_day_old_epoch) today_snap = ('today', today_epoch) mock_get_snapshot_list.return_value = [ one_day_snap, today_snap, ], "" recent, old = self.ontap.get_oldest_and_latest_snapshots('test', 1) self.assertEqual(None, old) self.assertEqual(today_snap, recent) @patch('web_service.ontap.ontap_service.OntapService.get_snapshot_list') def test_get_oldest_and_latest_snapshots_empty(self, mock_get_snapshot_list): today = datetime.now() one_day_old_epoch = (today - timedelta(days=1)).strftime('%s') today_epoch = today.strftime('%s') one_day_snap = ('one_day_old', one_day_old_epoch) today_snap = ('today', today_epoch) mock_get_snapshot_list.return_value = None, "some error message" recent, old = self.ontap.get_oldest_and_latest_snapshots('test', 1) self.assertIsNone(old) self.assertIsNone(recent)