예제 #1
0
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)