From c504029b3992fe63c89da81873907e68a5dcbe16 Mon Sep 17 00:00:00 2001
From: Ben Patterson <bpatterson@edx.org>
Date: Tue, 2 Feb 2016 18:47:10 -0500
Subject: [PATCH] Create a third shard for lms unit tests.

This will leverage the nose attrib plugin to allow us
to break tests apart on our build system to run
in parallel on separate nodes.
---
 lms/djangoapps/discussion_api/tests/test_api.py | 12 ++++++++++++
 .../django_comment_client/base/tests.py         | 14 ++++++++++++++
 lms/djangoapps/mobile_api/users/tests.py        |  8 ++++++++
 .../mobile_api/video_outlines/tests.py          |  4 ++++
 .../verify_student/tests/test_views.py          | 17 +++++++++++++++++
 .../core/djangoapps/bookmarks/tests/test_api.py |  2 ++
 .../djangoapps/bookmarks/tests/test_models.py   |  3 +++
 .../djangoapps/bookmarks/tests/test_services.py |  2 ++
 .../djangoapps/bookmarks/tests/test_tasks.py    |  2 ++
 .../djangoapps/bookmarks/tests/test_views.py    |  3 +++
 .../djangoapps/ccxcon/tests/test_signals.py     |  2 ++
 .../core/djangoapps/ccxcon/tests/test_tasks.py  |  2 ++
 .../tests/test_generate_course_overview.py      |  2 ++
 .../content/course_structures/tests.py          |  2 ++
 .../tests/test_post_cohort_membership_fix.py    |  2 ++
 .../course_groups/tests/test_cohorts.py         |  4 ++++
 .../tests/test_partition_scheme.py              |  5 +++++
 .../course_groups/tests/test_views.py           |  8 ++++++++
 .../djangoapps/credentials/tests/test_models.py |  3 ++-
 .../djangoapps/credentials/tests/test_utils.py  |  2 ++
 .../core/djangoapps/credit/tests/test_api.py    |  3 +++
 .../core/djangoapps/credit/tests/test_models.py |  2 ++
 .../djangoapps/credit/tests/test_partition.py   |  2 ++
 .../djangoapps/credit/tests/test_serializers.py |  3 +++
 .../djangoapps/credit/tests/test_services.py    |  3 +++
 .../djangoapps/credit/tests/test_signals.py     |  2 ++
 .../djangoapps/credit/tests/test_signature.py   |  3 +++
 .../core/djangoapps/credit/tests/test_tasks.py  |  2 ++
 .../credit/tests/test_verification_access.py    |  3 +++
 .../core/djangoapps/credit/tests/test_views.py  |  6 ++++++
 .../models/tests/test_course_details.py         |  2 ++
 .../profile_images/tests/test_images.py         |  4 ++++
 .../profile_images/tests/test_views.py          |  6 ++++++
 .../djangoapps/programs/tests/test_models.py    |  2 ++
 .../djangoapps/programs/tests/test_signals.py   |  2 ++
 .../djangoapps/programs/tests/test_utils.py     |  2 ++
 .../safe_sessions/tests/test_middleware.py      |  4 ++++
 .../tests/test_safe_cookie_data.py              |  2 ++
 .../user_api/accounts/tests/test_api.py         |  4 ++++
 .../accounts/tests/test_image_helpers.py        |  2 ++
 .../user_api/accounts/tests/test_views.py       |  3 +++
 .../user_api/course_tag/tests/test_api.py       |  2 ++
 .../management/tests/test_email_opt_in_list.py  |  2 ++
 .../user_api/preferences/tests/test_api.py      |  3 +++
 openedx/core/djangolib/tests/test_js_utils.py   |  2 ++
 openedx/core/djangolib/tests/test_markup.py     |  2 ++
 .../core/lib/api/tests/test_authentication.py   |  3 +++
 openedx/core/lib/api/tests/test_exceptions.py   |  3 +++
 openedx/core/lib/api/tests/test_paginators.py   |  3 +++
 openedx/core/lib/api/tests/test_parsers.py      |  2 ++
 openedx/core/lib/api/tests/test_permissions.py  |  4 ++++
 .../tests/test_block_structure.py               |  3 +++
 .../lib/block_structure/tests/test_cache.py     |  2 ++
 .../lib/block_structure/tests/test_factory.py   |  2 ++
 .../lib/block_structure/tests/test_manager.py   |  2 ++
 .../tests/test_transformer_registry.py          |  2 ++
 .../block_structure/tests/test_transformers.py  |  2 ++
 openedx/core/lib/gating/tests/test_api.py       |  2 ++
 openedx/core/lib/tests/test_course_tab_api.py   |  2 ++
 openedx/core/lib/tests/test_course_tabs.py      |  4 ++++
 openedx/core/lib/tests/test_courses.py          |  2 ++
 openedx/core/lib/tests/test_edx_api_utils.py    |  2 ++
 openedx/core/lib/tests/test_graph_traversals.py |  2 ++
 openedx/core/lib/tests/test_token_utils.py      |  2 ++
 openedx/core/lib/tests/test_xblock_utils.py     |  2 ++
 scripts/generic-ci-tests.sh                     |  5 ++++-
 66 files changed, 221 insertions(+), 2 deletions(-)

diff --git a/lms/djangoapps/discussion_api/tests/test_api.py b/lms/djangoapps/discussion_api/tests/test_api.py
index 5decbe898ee..6bdcf514ab7 100644
--- a/lms/djangoapps/discussion_api/tests/test_api.py
+++ b/lms/djangoapps/discussion_api/tests/test_api.py
@@ -9,6 +9,7 @@ from urllib import urlencode
 import ddt
 import httpretty
 import mock
+from nose.plugins.attrib import attr
 from pytz import UTC
 
 from django.core.exceptions import ValidationError
@@ -82,6 +83,7 @@ def _discussion_disabled_course_for(user):
     return course_with_disabled_forums
 
 
+@attr('shard_2')
 @ddt.ddt
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class GetCourseTest(UrlResetMixin, SharedModuleStoreTestCase):
@@ -155,6 +157,7 @@ class GetCourseTest(UrlResetMixin, SharedModuleStoreTestCase):
         self.assertEqual(result["blackouts"], [])
 
 
+@attr('shard_2')
 @mock.patch.dict("django.conf.settings.FEATURES", {"DISABLE_START_DATES": False})
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class GetCourseTopicsTest(UrlResetMixin, ModuleStoreTestCase):
@@ -481,6 +484,7 @@ class GetCourseTopicsTest(UrlResetMixin, ModuleStoreTestCase):
         self.assertEqual(staff_actual, staff_expected)
 
 
+@attr('shard_2')
 @ddt.ddt
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class GetThreadListTest(CommentsServiceMockMixin, UrlResetMixin, SharedModuleStoreTestCase):
@@ -930,6 +934,7 @@ class GetThreadListTest(CommentsServiceMockMixin, UrlResetMixin, SharedModuleSto
         })
 
 
+@attr('shard_2')
 @ddt.ddt
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class GetCommentListTest(CommentsServiceMockMixin, SharedModuleStoreTestCase):
@@ -1353,6 +1358,7 @@ class GetCommentListTest(CommentsServiceMockMixin, SharedModuleStoreTestCase):
             self.get_comment_list(thread, endorsed=True, page=2, page_size=10)
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'thread_created')
 @disable_signal(api, 'thread_voted')
@@ -1604,6 +1610,7 @@ class CreateThreadTest(
             create_thread(self.request, data)
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'comment_created')
 @disable_signal(api, 'comment_voted')
@@ -1870,6 +1877,7 @@ class CreateCommentTest(
             create_comment(self.request, data)
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'thread_edited')
 @disable_signal(api, 'thread_voted')
@@ -2278,6 +2286,7 @@ class UpdateThreadTest(
         )
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'comment_edited')
 @disable_signal(api, 'comment_voted')
@@ -2681,6 +2690,7 @@ class UpdateCommentTest(
             )
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'thread_deleted')
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
@@ -2820,6 +2830,7 @@ class DeleteThreadTest(
             self.assertTrue(expected_error)
 
 
+@attr('shard_2')
 @ddt.ddt
 @disable_signal(api, 'comment_deleted')
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
@@ -2978,6 +2989,7 @@ class DeleteCommentTest(
             self.assertTrue(expected_error)
 
 
+@attr('shard_2')
 @ddt.ddt
 @mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
 class RetrieveThreadTest(
diff --git a/lms/djangoapps/django_comment_client/base/tests.py b/lms/djangoapps/django_comment_client/base/tests.py
index abda87d29cd..1df1b28ffb0 100644
--- a/lms/djangoapps/django_comment_client/base/tests.py
+++ b/lms/djangoapps/django_comment_client/base/tests.py
@@ -13,6 +13,7 @@ from django.core.urlresolvers import reverse
 from request_cache.middleware import RequestCache
 from mock import patch, ANY, Mock
 from nose.tools import assert_true, assert_equal
+from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 from lms.lib.comment_client import Thread
 
@@ -48,6 +49,7 @@ class MockRequestSetupMixin(object):
         mock_request.return_value = self._create_response_mock(data)
 
 
+@attr('shard_2')
 @patch('lms.lib.comment_client.utils.requests.request', autospec=True)
 class CreateThreadGroupIdTestCase(
         MockRequestSetupMixin,
@@ -83,6 +85,7 @@ class CreateThreadGroupIdTestCase(
         self._assert_json_response_contains_group_info(response)
 
 
+@attr('shard_2')
 @patch('lms.lib.comment_client.utils.requests.request', autospec=True)
 @disable_signal(views, 'thread_edited')
 @disable_signal(views, 'thread_voted')
@@ -340,6 +343,7 @@ class ViewsTestCaseMixin(object):
         self.assertEqual(data['commentable_id'], 'some_topic')
 
 
+@attr('shard_2')
 @ddt.ddt
 @patch('lms.lib.comment_client.utils.requests.request', autospec=True)
 @disable_signal(views, 'thread_created')
@@ -389,6 +393,7 @@ class ViewsQueryCountTestCase(UrlResetMixin, ModuleStoreTestCase, MockRequestSet
         self.update_thread_helper(mock_request)
 
 
+@attr('shard_2')
 @ddt.ddt
 @patch('lms.lib.comment_client.utils.requests.request', autospec=True)
 class ViewsTestCase(
@@ -1016,6 +1021,7 @@ class ViewsTestCase(
         self.assertEqual(response.status_code, 200)
 
 
+@attr('shard_2')
 @patch("lms.lib.comment_client.utils.requests.request", autospec=True)
 @disable_signal(views, 'comment_endorsed')
 class ViewPermissionsTestCase(UrlResetMixin, SharedModuleStoreTestCase, MockRequestSetupMixin):
@@ -1125,6 +1131,7 @@ class ViewPermissionsTestCase(UrlResetMixin, SharedModuleStoreTestCase, MockRequ
         self.assertEqual(response.status_code, 200)
 
 
+@attr('shard_2')
 class CreateThreadUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, MockRequestSetupMixin):
 
     @classmethod
@@ -1160,6 +1167,7 @@ class CreateThreadUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, M
         self.assertEqual(mock_request.call_args[1]["data"]["title"], text)
 
 
+@attr('shard_2')
 @disable_signal(views, 'thread_edited')
 class UpdateThreadUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, MockRequestSetupMixin):
 
@@ -1197,6 +1205,7 @@ class UpdateThreadUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, M
         self.assertEqual(mock_request.call_args[1]["data"]["commentable_id"], "test_commentable")
 
 
+@attr('shard_2')
 @disable_signal(views, 'comment_created')
 class CreateCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, MockRequestSetupMixin):
 
@@ -1239,6 +1248,7 @@ class CreateCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin,
             del Thread.commentable_id
 
 
+@attr('shard_2')
 @disable_signal(views, 'comment_edited')
 class UpdateCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, MockRequestSetupMixin):
 
@@ -1272,6 +1282,7 @@ class UpdateCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin,
         self.assertEqual(mock_request.call_args[1]["data"]["body"], text)
 
 
+@attr('shard_2')
 @disable_signal(views, 'comment_created')
 class CreateSubCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin, MockRequestSetupMixin):
     """
@@ -1318,6 +1329,7 @@ class CreateSubCommentUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixi
             del Thread.commentable_id
 
 
+@attr('shard_2')
 @ddt.ddt
 @patch("lms.lib.comment_client.utils.requests.request", autospec=True)
 @disable_signal(views, 'thread_voted')
@@ -1589,6 +1601,7 @@ class TeamsPermissionsTestCase(UrlResetMixin, SharedModuleStoreTestCase, MockReq
 TEAM_COMMENTABLE_ID = 'test-team-discussion'
 
 
+@attr('shard_2')
 @disable_signal(views, 'comment_created')
 @ddt.ddt
 class ForumEventTestCase(SharedModuleStoreTestCase, MockRequestSetupMixin):
@@ -1774,6 +1787,7 @@ class ForumEventTestCase(SharedModuleStoreTestCase, MockRequestSetupMixin):
         self.assertEqual(event['vote_value'], 'up')
 
 
+@attr('shard_2')
 class UsersEndpointTestCase(SharedModuleStoreTestCase, MockRequestSetupMixin):
 
     @classmethod
diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py
index 270587e8b76..671872b7ca9 100644
--- a/lms/djangoapps/mobile_api/users/tests.py
+++ b/lms/djangoapps/mobile_api/users/tests.py
@@ -5,6 +5,7 @@ Tests for users API
 import datetime
 import ddt
 from mock import patch
+from nose.plugins.attrib import attr
 import pytz
 
 from django.conf import settings
@@ -34,6 +35,7 @@ from ..testutils import MobileAPITestCase, MobileAuthTestMixin, MobileAuthUserTe
 from .serializers import CourseEnrollmentSerializer
 
 
+@attr('shard_2')
 class TestUserDetailApi(MobileAPITestCase, MobileAuthUserTestMixin):
     """
     Tests for /api/mobile/v0.5/users/<user_name>...
@@ -48,6 +50,7 @@ class TestUserDetailApi(MobileAPITestCase, MobileAuthUserTestMixin):
         self.assertEqual(response.data['email'], self.user.email)
 
 
+@attr('shard_2')
 class TestUserInfoApi(MobileAPITestCase, MobileAuthTestMixin):
     """
     Tests for /api/mobile/v0.5/my_user_info
@@ -63,6 +66,7 @@ class TestUserInfoApi(MobileAPITestCase, MobileAuthTestMixin):
         self.assertTrue(self.username in response['location'])
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTestMixin,
                             MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
@@ -262,6 +266,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
         self.assertIn('/api/discussion/v1/courses/{}'.format(self.course.id), response_discussion_url)
 
 
+@attr('shard_2')
 class CourseStatusAPITestCase(MobileAPITestCase):
     """
     Base test class for /api/mobile/v0.5/users/<user_name>/course_status_info/{course_id}
@@ -296,6 +301,7 @@ class CourseStatusAPITestCase(MobileAPITestCase):
         )
 
 
+@attr('shard_2')
 class TestCourseStatusGET(CourseStatusAPITestCase, MobileAuthUserTestMixin,
                           MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
     """
@@ -315,6 +321,7 @@ class TestCourseStatusGET(CourseStatusAPITestCase, MobileAuthUserTestMixin,
         )
 
 
+@attr('shard_2')
 class TestCourseStatusPATCH(CourseStatusAPITestCase, MobileAuthUserTestMixin,
                             MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
     """
@@ -418,6 +425,7 @@ class TestCourseStatusPATCH(CourseStatusAPITestCase, MobileAuthUserTestMixin,
         )
 
 
+@attr('shard_2')
 class TestCourseEnrollmentSerializer(MobileAPITestCase):
     """
     Test the course enrollment serializer
diff --git a/lms/djangoapps/mobile_api/video_outlines/tests.py b/lms/djangoapps/mobile_api/video_outlines/tests.py
index 48b61b7adcb..9a0217d5d34 100644
--- a/lms/djangoapps/mobile_api/video_outlines/tests.py
+++ b/lms/djangoapps/mobile_api/video_outlines/tests.py
@@ -5,6 +5,7 @@ Tests for video outline API
 
 import ddt
 import itertools
+from nose.plugins.attrib import attr
 from uuid import uuid4
 from collections import namedtuple
 
@@ -199,6 +200,7 @@ class TestVideoAPIMixin(object):
         return sub_block_a, sub_block_b
 
 
+@attr('shard_2')
 class TestNonStandardCourseStructure(MobileAPITestCase, TestVideoAPIMixin):
     """
     Tests /api/mobile/v0.5/video_outlines/courses/{course_id} with no course set
@@ -408,6 +410,7 @@ class TestNonStandardCourseStructure(MobileAPITestCase, TestVideoAPIMixin):
         )
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestVideoSummaryList(TestVideoAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin,
                            TestVideoAPIMixin, MilestonesTestCaseMixin):
@@ -864,6 +867,7 @@ class TestVideoSummaryList(TestVideoAPITestCase, MobileAuthTestMixin, MobileCour
             )
 
 
+@attr('shard_2')
 class TestTranscriptsDetail(TestVideoAPITestCase, MobileAuthTestMixin, MobileCourseAccessTestMixin,
                             TestVideoAPIMixin, MilestonesTestCaseMixin):
     """
diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py
index 2a876035ed4..7acc7194c51 100644
--- a/lms/djangoapps/verify_student/tests/test_views.py
+++ b/lms/djangoapps/verify_student/tests/test_views.py
@@ -11,6 +11,7 @@ from uuid import uuid4
 import ddt
 import httpretty
 import mock
+from nose.plugins.attrib import attr
 import boto
 import moto
 import pytz
@@ -67,7 +68,12 @@ render_mock = Mock(side_effect=mock_render_to_response)
 PAYMENT_DATA_KEYS = {'payment_processor_name', 'payment_page_url', 'payment_form_data'}
 
 
+@attr('shard_2')
 class StartView(TestCase):
+    """
+    This view is for the first time student is
+    attempting a Photo Verification.
+    """
     def start_url(self, course_id=""):
         return "/verify_student/{0}".format(urllib.quote(course_id))
 
@@ -83,6 +89,7 @@ class StartView(TestCase):
         self.assertHttpForbidden(self.client.get(self.start_url()))
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
     """
@@ -1173,6 +1180,7 @@ class CheckoutTestMixin(object):
         self.assertEqual(data, {'foo': 'bar'})
 
 
+@attr('shard_2')
 @patch('lms.djangoapps.verify_student.views.checkout_with_shoppingcart', return_value=TEST_PAYMENT_DATA, autospec=True)
 class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
     """ Test view behavior when the shoppingcart is used. """
@@ -1186,6 +1194,7 @@ class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
         return dict(zip(('request', 'user', 'course_key', 'course_mode', 'amount'), patched_create_order.call_args[0]))
 
 
+@attr('shard_2')
 @override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
 @patch(
     'lms.djangoapps.verify_student.views.checkout_with_ecommerce_service',
@@ -1204,6 +1213,7 @@ class TestCreateOrderEcommerceService(CheckoutTestMixin, ModuleStoreTestCase):
         return dict(zip(('user', 'course_key', 'course_mode', 'processor'), patched_create_order.call_args[0]))
 
 
+@attr('shard_2')
 class TestCheckoutWithEcommerceService(ModuleStoreTestCase):
     """
     Ensures correct behavior in the function `checkout_with_ecommerce_service`.
@@ -1249,6 +1259,7 @@ class TestCheckoutWithEcommerceService(ModuleStoreTestCase):
         self.assertEqual(actual_payment_data, expected_payment_data)
 
 
+@attr('shard_2')
 class TestCreateOrderView(ModuleStoreTestCase):
     """
     Tests for the create_order view of verified course enrollment process.
@@ -1352,6 +1363,7 @@ class TestCreateOrderView(ModuleStoreTestCase):
         return response
 
 
+@attr('shard_2')
 @ddt.ddt
 @patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
 class TestSubmitPhotosForVerification(TestCase):
@@ -1593,6 +1605,7 @@ class TestSubmitPhotosForVerification(TestCase):
         return json.loads(last_request.body)
 
 
+@attr('shard_2')
 class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
     """
     Tests for the results_callback view.
@@ -1954,6 +1967,7 @@ class TestPhotoVerificationResultsCallback(ModuleStoreTestCase):
         VerificationStatus.add_verification_status(checkpoint, self.user, "submitted")
 
 
+@attr('shard_2')
 class TestReverifyView(TestCase):
     """
     Tests for the reverification view.
@@ -2048,6 +2062,7 @@ class TestReverifyView(TestCase):
         self.assertContains(response, "reverify-blocked")
 
 
+@attr('shard_2')
 class TestInCourseReverifyView(ModuleStoreTestCase):
     """
     Tests for the incourse reverification views.
@@ -2247,6 +2262,7 @@ class TestInCourseReverifyView(ModuleStoreTestCase):
         return self.client.post(url, data)
 
 
+@attr('shard_2')
 class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
     """
     Test email sending on re-verification
@@ -2451,6 +2467,7 @@ class TestEmailMessageWithCustomICRVBlock(ModuleStoreTestCase):
         )
 
 
+@attr('shard_2')
 class TestEmailMessageWithDefaultICRVBlock(ModuleStoreTestCase):
     """
     Test for In-course Re-verification
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_api.py b/openedx/core/djangoapps/bookmarks/tests/test_api.py
index f9e6f36dfe5..6484047eba5 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_api.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_api.py
@@ -3,6 +3,7 @@ Tests for bookmarks api.
 """
 import ddt
 from mock import patch
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 
 from django.conf import settings
@@ -35,6 +36,7 @@ class BookmarkApiEventTestMixin(object):
         self.assertFalse(mock_tracker.called)  # pylint: disable=maybe-no-member
 
 
+@attr('shard_2')
 @ddt.ddt
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class BookmarksAPITests(BookmarkApiEventTestMixin, BookmarksTestsBase):
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_models.py b/openedx/core/djangoapps/bookmarks/tests/test_models.py
index 8166c75d649..a2b762a2bb4 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_models.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_models.py
@@ -6,6 +6,7 @@ import datetime
 import ddt
 from freezegun import freeze_time
 import mock
+from nose.plugins.attrib import attr
 import pytz
 from unittest import skipUnless
 
@@ -223,6 +224,7 @@ class BookmarksTestsBase(ModuleStoreTestCase):
             self.assertEqual(bookmark_data['path'], bookmark.path)
 
 
+@attr('shard_2')
 @ddt.ddt
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class BookmarkModelTests(BookmarksTestsBase):
@@ -407,6 +409,7 @@ class BookmarkModelTests(BookmarksTestsBase):
             self.assertEqual(bookmark.path, [])
 
 
+@attr('shard_2')
 @ddt.ddt
 class XBlockCacheModelTest(ModuleStoreTestCase):
     """
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_services.py b/openedx/core/djangoapps/bookmarks/tests/test_services.py
index d55ab4d37b8..4be7d32207c 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_services.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_services.py
@@ -1,6 +1,7 @@
 """
 Tests for bookmark services.
 """
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 
 from django.conf import settings
@@ -11,6 +12,7 @@ from ..services import BookmarksService
 from .test_models import BookmarksTestsBase
 
 
+@attr('shard_2')
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class BookmarksServiceTests(BookmarksTestsBase):
     """
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_tasks.py b/openedx/core/djangoapps/bookmarks/tests/test_tasks.py
index 8aefa79201d..c8a789d74a7 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_tasks.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_tasks.py
@@ -2,6 +2,7 @@
 Tests for tasks.
 """
 import ddt
+from nose.plugins.attrib import attr
 
 from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.tests.factories import check_mongo_calls, ItemFactory
@@ -11,6 +12,7 @@ from ..tasks import _calculate_course_xblocks_data, _update_xblocks_cache
 from .test_models import BookmarksTestsBase
 
 
+@attr('shard_2')
 @ddt.ddt
 class XBlockCacheTaskTests(BookmarksTestsBase):
     """
diff --git a/openedx/core/djangoapps/bookmarks/tests/test_views.py b/openedx/core/djangoapps/bookmarks/tests/test_views.py
index 3583cac1282..2b63b1fb7fa 100644
--- a/openedx/core/djangoapps/bookmarks/tests/test_views.py
+++ b/openedx/core/djangoapps/bookmarks/tests/test_views.py
@@ -4,6 +4,7 @@ Tests for bookmark views.
 
 import ddt
 import json
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 import urllib
 
@@ -63,6 +64,7 @@ class BookmarksViewsTestsBase(BookmarksTestsBase, BookmarkApiEventTestMixin):
         return response
 
 
+@attr('shard_2')
 @ddt.ddt
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class BookmarksListViewTests(BookmarksViewsTestsBase):
@@ -367,6 +369,7 @@ class BookmarksListViewTests(BookmarksViewsTestsBase):
         )
 
 
+@attr('shard_2')
 @ddt.ddt
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class BookmarksDetailViewTests(BookmarksViewsTestsBase):
diff --git a/openedx/core/djangoapps/ccxcon/tests/test_signals.py b/openedx/core/djangoapps/ccxcon/tests/test_signals.py
index 559a28e307b..5de4c74d68e 100644
--- a/openedx/core/djangoapps/ccxcon/tests/test_signals.py
+++ b/openedx/core/djangoapps/ccxcon/tests/test_signals.py
@@ -3,12 +3,14 @@ Test for contentstore signals receiver
 """
 
 import mock
+from nose.plugins.attrib import attr
 
 from django.test import TestCase
 from opaque_keys.edx.keys import CourseKey
 from xmodule.modulestore.django import modulestore, SignalHandler
 
 
+@attr('shard_2')
 class CCXConSignalTestCase(TestCase):
     """
     The only tests currently implemented are for verifying that
diff --git a/openedx/core/djangoapps/ccxcon/tests/test_tasks.py b/openedx/core/djangoapps/ccxcon/tests/test_tasks.py
index 68d54c3c620..de42bf20795 100644
--- a/openedx/core/djangoapps/ccxcon/tests/test_tasks.py
+++ b/openedx/core/djangoapps/ccxcon/tests/test_tasks.py
@@ -3,6 +3,7 @@ Tests for the CCXCon celery tasks
 """
 
 import mock
+from nose.plugins.attrib import attr
 
 from django.test import TestCase
 
@@ -10,6 +11,7 @@ from opaque_keys.edx.keys import CourseKey
 from openedx.core.djangoapps.ccxcon import api, tasks
 
 
+@attr('shard_2')
 class CCXConTaskTestCase(TestCase):
     """
     Tests for CCXCon tasks.
diff --git a/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_generate_course_overview.py b/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_generate_course_overview.py
index 025617e1ea0..248c034816e 100644
--- a/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_generate_course_overview.py
+++ b/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_generate_course_overview.py
@@ -1,12 +1,14 @@
 # pylint: disable=missing-docstring
 from django.core.management.base import CommandError
 from mock import patch
+from nose.plugins.attrib import attr
 from openedx.core.djangoapps.content.course_overviews.management.commands import generate_course_overview
 from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
+@attr('shard_2')
 class TestGenerateCourseOverview(ModuleStoreTestCase):
     """
     Tests course overview management command.
diff --git a/openedx/core/djangoapps/content/course_structures/tests.py b/openedx/core/djangoapps/content/course_structures/tests.py
index 8db13b51050..b87385a9df3 100644
--- a/openedx/core/djangoapps/content/course_structures/tests.py
+++ b/openedx/core/djangoapps/content/course_structures/tests.py
@@ -2,6 +2,7 @@
 Course Structure Content sub-application test cases
 """
 import json
+from nose.plugins.attrib import attr
 
 from xmodule_django.models import UsageKey
 from xmodule.modulestore.django import SignalHandler
@@ -22,6 +23,7 @@ class SignalDisconnectTestMixin(object):
         SignalHandler.course_published.disconnect(listen_for_course_publish)
 
 
+@attr('shard_2')
 class CourseStructureTaskTests(ModuleStoreTestCase):
     """
     Test cases covering Course Structure task-related workflows
diff --git a/openedx/core/djangoapps/course_groups/management/commands/tests/test_post_cohort_membership_fix.py b/openedx/core/djangoapps/course_groups/management/commands/tests/test_post_cohort_membership_fix.py
index 893d0e777c4..bd6ca8efbc3 100644
--- a/openedx/core/djangoapps/course_groups/management/commands/tests/test_post_cohort_membership_fix.py
+++ b/openedx/core/djangoapps/course_groups/management/commands/tests/test_post_cohort_membership_fix.py
@@ -3,6 +3,7 @@ Test for the post-migration fix commands that are included with this djangoapp
 """
 from django.core.management import call_command
 from django.test.client import RequestFactory
+from nose.plugins.attrib import attr
 
 from openedx.core.djangoapps.course_groups.views import cohort_handler
 from openedx.core.djangoapps.course_groups.cohorts import get_cohort_by_name
@@ -13,6 +14,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
+@attr('shard_2')
 class TestPostMigrationFix(ModuleStoreTestCase):
     """
     Base class for testing post-migration fix commands
diff --git a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py
index 1782a9c62f1..1a0986df9c5 100644
--- a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py
+++ b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py
@@ -4,6 +4,7 @@ Tests for cohorts
 # pylint: disable=no-member
 import ddt
 from mock import call, patch
+from nose.plugins.attrib import attr
 import before_after
 
 from django.contrib.auth.models import User
@@ -25,6 +26,7 @@ from ..tests.helpers import (
 )
 
 
+@attr('shard_2')
 @patch("openedx.core.djangoapps.course_groups.cohorts.tracker", autospec=True)
 class TestCohortSignals(TestCase):
     """
@@ -130,6 +132,7 @@ class TestCohortSignals(TestCase):
         self.assertFalse(mock_tracker.emit.called)
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestCohorts(ModuleStoreTestCase):
     """
@@ -723,6 +726,7 @@ class TestCohorts(ModuleStoreTestCase):
             )
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestCohortsAndPartitionGroups(ModuleStoreTestCase):
     """
diff --git a/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py b/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py
index 9bcdf18baca..df76894633d 100644
--- a/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py
+++ b/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py
@@ -7,6 +7,7 @@ import json
 from django.conf import settings
 import django.test
 from mock import patch
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 
 from courseware.masquerade import handle_ajax, setup_masquerade
@@ -25,6 +26,7 @@ from ..cohorts import add_user_to_cohort, remove_user_from_cohort, get_course_co
 from .helpers import CohortFactory, config_course_cohorts
 
 
+@attr('shard_2')
 class TestCohortPartitionScheme(ModuleStoreTestCase):
     """
     Test the logic for linking a user to a partition group based on their cohort.
@@ -258,6 +260,7 @@ class TestCohortPartitionScheme(ModuleStoreTestCase):
             self.assertRegexpMatches(mock_log.warn.call_args[0][0], 'partition mismatch')
 
 
+@attr('shard_2')
 class TestExtension(django.test.TestCase):
     """
     Ensure that the scheme extension is correctly plugged in (via entry point
@@ -270,6 +273,7 @@ class TestExtension(django.test.TestCase):
             UserPartition.get_scheme('other')
 
 
+@attr('shard_2')
 class TestGetCohortedUserPartition(ModuleStoreTestCase):
     """
     Test that `get_cohorted_user_partition` returns the first user_partition with scheme `CohortPartitionScheme`.
@@ -327,6 +331,7 @@ class TestGetCohortedUserPartition(ModuleStoreTestCase):
         self.assertIsNone(get_cohorted_user_partition(self.course))
 
 
+@attr('shard_2')
 class TestMasqueradedGroup(StaffMasqueradeTestCase):
     """
     Check for staff being able to masquerade as belonging to a group.
diff --git a/openedx/core/djangoapps/course_groups/tests/test_views.py b/openedx/core/djangoapps/course_groups/tests/test_views.py
index 92b307575fb..227cb23f35d 100644
--- a/openedx/core/djangoapps/course_groups/tests/test_views.py
+++ b/openedx/core/djangoapps/course_groups/tests/test_views.py
@@ -7,6 +7,7 @@ import json
 
 from collections import namedtuple
 from datetime import datetime
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 
 from django.conf import settings
@@ -35,6 +36,7 @@ from .helpers import (
 )
 
 
+@attr('shard_2')
 class CohortViewsTestCase(ModuleStoreTestCase):
     """
     Base class which sets up a course and staff/non-staff users.
@@ -174,6 +176,7 @@ class CohortViewsTestCase(ModuleStoreTestCase):
         return json.loads(response.content)
 
 
+@attr('shard_2')
 class CourseCohortSettingsHandlerTestCase(CohortViewsTestCase):
     """
     Tests the `course_cohort_settings_handler` view.
@@ -323,6 +326,7 @@ class CourseCohortSettingsHandlerTestCase(CohortViewsTestCase):
         )
 
 
+@attr('shard_2')
 class CohortHandlerTestCase(CohortViewsTestCase):
     """
     Tests the `cohort_handler` view.
@@ -675,6 +679,7 @@ class CohortHandlerTestCase(CohortViewsTestCase):
         )
 
 
+@attr('shard_2')
 class UsersInCohortTestCase(CohortViewsTestCase):
     """
     Tests the `users_in_cohort` view.
@@ -807,6 +812,7 @@ class UsersInCohortTestCase(CohortViewsTestCase):
         self.request_users_in_cohort(cohort, self.course, -1, should_return_bad_request=True)
 
 
+@attr('shard_2')
 class AddUsersToCohortTestCase(CohortViewsTestCase):
     """
     Tests the `add_users_to_cohort` view.
@@ -1105,6 +1111,7 @@ class AddUsersToCohortTestCase(CohortViewsTestCase):
         )
 
 
+@attr('shard_2')
 class RemoveUserFromCohortTestCase(CohortViewsTestCase):
     """
     Tests the `remove_user_from_cohort` view.
@@ -1198,6 +1205,7 @@ class RemoveUserFromCohortTestCase(CohortViewsTestCase):
         self.verify_removed_user_from_cohort(user.username, response_dict, cohort)
 
 
+@attr('shard_2')
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
 class CourseCohortDiscussionTopicsTestCase(CohortViewsTestCase):
     """
diff --git a/openedx/core/djangoapps/credentials/tests/test_models.py b/openedx/core/djangoapps/credentials/tests/test_models.py
index d1556c43f53..697482200a1 100644
--- a/openedx/core/djangoapps/credentials/tests/test_models.py
+++ b/openedx/core/djangoapps/credentials/tests/test_models.py
@@ -4,11 +4,12 @@ import unittest
 
 from django.conf import settings
 from django.test import TestCase
-
+from nose.plugins.attrib import attr
 from openedx.core.djangoapps.credentials.tests.mixins import CredentialsApiConfigMixin
 
 
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+@attr('shard_2')
 class TestCredentialsApiConfig(CredentialsApiConfigMixin, TestCase):
     """Tests covering the CredentialsApiConfig model."""
     def test_url_construction(self):
diff --git a/openedx/core/djangoapps/credentials/tests/test_utils.py b/openedx/core/djangoapps/credentials/tests/test_utils.py
index e3116b8989f..171d59d8053 100644
--- a/openedx/core/djangoapps/credentials/tests/test_utils.py
+++ b/openedx/core/djangoapps/credentials/tests/test_utils.py
@@ -4,6 +4,7 @@ import unittest
 from django.conf import settings
 from django.core.cache import cache
 from django.test import TestCase
+from nose.plugins.attrib import attr
 import httpretty
 from oauth2_provider.tests.factories import ClientFactory
 from provider.constants import CONFIDENTIAL
@@ -19,6 +20,7 @@ from student.tests.factories import UserFactory
 
 
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+@attr('shard_2')
 class TestCredentialsRetrieval(ProgramsApiConfigMixin, CredentialsApiConfigMixin, CredentialsDataMixin,
                                ProgramsDataMixin, TestCase):
     """ Tests covering the retrieval of user credentials from the Credentials
diff --git a/openedx/core/djangoapps/credit/tests/test_api.py b/openedx/core/djangoapps/credit/tests/test_api.py
index 987a78f8fe1..d58d151d5c5 100644
--- a/openedx/core/djangoapps/credit/tests/test_api.py
+++ b/openedx/core/djangoapps/credit/tests/test_api.py
@@ -9,6 +9,7 @@ from django.conf import settings
 from django.core import mail
 from django.test.utils import override_settings
 from django.db import connection, transaction
+from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 import pytz
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -86,6 +87,7 @@ class CreditApiTestBase(ModuleStoreTestCase):
         return credit_course
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
 @ddt.ddt
 class CreditRequirementApiTests(CreditApiTestBase):
@@ -550,6 +552,7 @@ class CreditRequirementApiTests(CreditApiTestBase):
         self.assertEqual(req_status[0]["status"], None)
 
 
+@attr('shard_2')
 @ddt.ddt
 class CreditProviderIntegrationApiTests(CreditApiTestBase):
     """
diff --git a/openedx/core/djangoapps/credit/tests/test_models.py b/openedx/core/djangoapps/credit/tests/test_models.py
index 733876f198f..f731915b84f 100644
--- a/openedx/core/djangoapps/credit/tests/test_models.py
+++ b/openedx/core/djangoapps/credit/tests/test_models.py
@@ -5,11 +5,13 @@ Tests for credit course models.
 
 import ddt
 from django.test import TestCase
+from nose.plugins.attrib import attr
 from opaque_keys.edx.keys import CourseKey
 
 from openedx.core.djangoapps.credit.models import CreditCourse, CreditRequirement
 
 
+@attr('shard_2')
 @ddt.ddt
 class CreditEligibilityModelTests(TestCase):
     """
diff --git a/openedx/core/djangoapps/credit/tests/test_partition.py b/openedx/core/djangoapps/credit/tests/test_partition.py
index f6d74f51b04..96fe676ac1c 100644
--- a/openedx/core/djangoapps/credit/tests/test_partition.py
+++ b/openedx/core/djangoapps/credit/tests/test_partition.py
@@ -4,6 +4,7 @@ Tests for In-Course Reverification Access Control Partition scheme
 """
 
 import ddt
+from nose.plugins.attrib import attr
 import unittest
 
 from django.conf import settings
@@ -21,6 +22,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class ReverificationPartitionTest(ModuleStoreTestCase):
diff --git a/openedx/core/djangoapps/credit/tests/test_serializers.py b/openedx/core/djangoapps/credit/tests/test_serializers.py
index 9526627ac2f..06aed067173 100644
--- a/openedx/core/djangoapps/credit/tests/test_serializers.py
+++ b/openedx/core/djangoapps/credit/tests/test_serializers.py
@@ -5,11 +5,13 @@ from __future__ import unicode_literals
 
 from django.test import TestCase
 
+from nose.plugins.attrib import attr
 from openedx.core.djangoapps.credit import serializers
 from openedx.core.djangoapps.credit.tests.factories import CreditProviderFactory, CreditEligibilityFactory
 from student.tests.factories import UserFactory
 
 
+@attr('shard_2')
 class CreditProviderSerializerTests(TestCase):
     """ CreditProviderSerializer tests. """
 
@@ -30,6 +32,7 @@ class CreditProviderSerializerTests(TestCase):
         self.assertDictEqual(serializer.data, expected)
 
 
+@attr('shard_2')
 class CreditEligibilitySerializerTests(TestCase):
     """ CreditEligibilitySerializer tests. """
 
diff --git a/openedx/core/djangoapps/credit/tests/test_services.py b/openedx/core/djangoapps/credit/tests/test_services.py
index 8a6fe0746da..aeaf047c679 100644
--- a/openedx/core/djangoapps/credit/tests/test_services.py
+++ b/openedx/core/djangoapps/credit/tests/test_services.py
@@ -2,6 +2,8 @@
 Tests for the Credit xBlock service
 """
 
+from nose.plugins.attrib import attr
+
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
@@ -12,6 +14,7 @@ from openedx.core.djangoapps.credit.api.eligibility import set_credit_requiremen
 from student.models import CourseEnrollment, UserProfile
 
 
+@attr('shard_2')
 class CreditServiceTests(ModuleStoreTestCase):
     """
     Tests for the Credit xBlock service
diff --git a/openedx/core/djangoapps/credit/tests/test_signals.py b/openedx/core/djangoapps/credit/tests/test_signals.py
index f6013f6e25a..f2480e9d5b5 100644
--- a/openedx/core/djangoapps/credit/tests/test_signals.py
+++ b/openedx/core/djangoapps/credit/tests/test_signals.py
@@ -5,6 +5,7 @@ Tests for minimum grade requirement status
 import pytz
 import ddt
 from datetime import timedelta, datetime
+from nose.plugins.attrib import attr
 
 from django.conf import settings
 from django.test.client import RequestFactory
@@ -21,6 +22,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
 
 
+@attr('shard_2')
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS')
 @ddt.ddt
 class TestMinGradedRequirementStatus(ModuleStoreTestCase):
diff --git a/openedx/core/djangoapps/credit/tests/test_signature.py b/openedx/core/djangoapps/credit/tests/test_signature.py
index 854bdbde68f..88099aaaea5 100644
--- a/openedx/core/djangoapps/credit/tests/test_signature.py
+++ b/openedx/core/djangoapps/credit/tests/test_signature.py
@@ -3,6 +3,8 @@
 Tests for digital signatures used to validate messages to/from credit providers.
 """
 
+from nose.plugins.attrib import attr
+
 from django.test import TestCase
 from django.test.utils import override_settings
 
@@ -10,6 +12,7 @@ from django.test.utils import override_settings
 from openedx.core.djangoapps.credit import signature
 
 
+@attr('shard_2')
 @override_settings(CREDIT_PROVIDER_SECRET_KEYS={
     "asu": u'abcd1234'
 })
diff --git a/openedx/core/djangoapps/credit/tests/test_tasks.py b/openedx/core/djangoapps/credit/tests/test_tasks.py
index ca41700c0f1..531b7a294a9 100644
--- a/openedx/core/djangoapps/credit/tests/test_tasks.py
+++ b/openedx/core/djangoapps/credit/tests/test_tasks.py
@@ -3,6 +3,7 @@ Tests for credit course tasks.
 """
 
 import mock
+from nose.plugins.attrib import attr
 from datetime import datetime, timedelta
 
 from pytz import UTC
@@ -17,6 +18,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, chec
 from edx_proctoring.api import create_exam
 
 
+@attr('shard_2')
 class TestTaskExecution(ModuleStoreTestCase):
     """Set of tests to ensure that the task code will do the right thing when
     executed directly.
diff --git a/openedx/core/djangoapps/credit/tests/test_verification_access.py b/openedx/core/djangoapps/credit/tests/test_verification_access.py
index e7a0db47e63..4eb33b8312f 100644
--- a/openedx/core/djangoapps/credit/tests/test_verification_access.py
+++ b/openedx/core/djangoapps/credit/tests/test_verification_access.py
@@ -12,6 +12,7 @@ into verify_student.
 """
 
 from mock import patch
+from nose.plugins.attrib import attr
 
 from django.conf import settings
 
@@ -27,6 +28,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, chec
 from xmodule.partitions.partitions import Group, UserPartition
 
 
+@attr('shard_2')
 class CreateVerificationPartitionTest(ModuleStoreTestCase):
     """
     Tests for applying verification access rules.
@@ -230,6 +232,7 @@ class CreateVerificationPartitionTest(ModuleStoreTestCase):
             return None
 
 
+@attr('shard_2')
 class WriteOnPublishTest(ModuleStoreTestCase):
     """
     Verify that updates to the course descriptor's
diff --git a/openedx/core/djangoapps/credit/tests/test_views.py b/openedx/core/djangoapps/credit/tests/test_views.py
index 0d2232dc23e..2fc3790d830 100644
--- a/openedx/core/djangoapps/credit/tests/test_views.py
+++ b/openedx/core/djangoapps/credit/tests/test_views.py
@@ -7,6 +7,7 @@ Tests for credit app views.
 from __future__ import unicode_literals
 import datetime
 import json
+from nose.plugins.attrib import attr
 import unittest
 
 import ddt
@@ -98,6 +99,7 @@ class ReadOnlyMixin(object):
         self.assertEqual(response.status_code, 405)
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class CreditCourseViewSetTests(UserMixin, TestCase):
     """ Tests for the CreditCourse endpoints.
@@ -259,6 +261,7 @@ class CreditCourseViewSetTests(UserMixin, TestCase):
         self.assertTrue(credit_course.enabled)
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class CreditProviderViewSetTests(ApiTestCaseMixin, ReadOnlyMixin, AuthMixin, UserMixin, TestCase):
@@ -303,6 +306,7 @@ class CreditProviderViewSetTests(ApiTestCaseMixin, ReadOnlyMixin, AuthMixin, Use
         self.assertEqual(response.data, CreditProviderSerializer(self.bayside).data)
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class CreditProviderRequestCreateViewTests(ApiTestCaseMixin, UserMixin, TestCase):
     """ Tests for CreditProviderRequestCreateView. """
@@ -451,6 +455,7 @@ class CreditProviderRequestCreateViewTests(ApiTestCaseMixin, UserMixin, TestCase
         self.assertEqual(response.status_code, 400)
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class CreditProviderCallbackViewTests(UserMixin, TestCase):
@@ -604,6 +609,7 @@ class CreditProviderCallbackViewTests(UserMixin, TestCase):
             self.assertEqual(response.status_code, 403)
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class CreditEligibilityViewTests(AuthMixin, UserMixin, ReadOnlyMixin, TestCase):
diff --git a/openedx/core/djangoapps/models/tests/test_course_details.py b/openedx/core/djangoapps/models/tests/test_course_details.py
index 0c2850a539d..46f39982cf7 100644
--- a/openedx/core/djangoapps/models/tests/test_course_details.py
+++ b/openedx/core/djangoapps/models/tests/test_course_details.py
@@ -5,6 +5,7 @@ Tests for CourseDetails
 import datetime
 import ddt
 from django.utils.timezone import UTC
+from nose.plugins.attrib import attr
 
 from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -14,6 +15,7 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
 from openedx.core.djangoapps.models.course_details import CourseDetails, ABOUT_ATTRIBUTES
 
 
+@attr('shard_2')
 @ddt.ddt
 class CourseDetailsTestCase(ModuleStoreTestCase):
     """
diff --git a/openedx/core/djangoapps/profile_images/tests/test_images.py b/openedx/core/djangoapps/profile_images/tests/test_images.py
index 253394b40b2..8291a7b2dd7 100644
--- a/openedx/core/djangoapps/profile_images/tests/test_images.py
+++ b/openedx/core/djangoapps/profile_images/tests/test_images.py
@@ -13,6 +13,7 @@ from django.test import TestCase
 from django.test.utils import override_settings
 import ddt
 import mock
+from nose.plugins.attrib import attr
 import piexif
 from PIL import Image
 
@@ -27,6 +28,7 @@ from ..images import (
 from .helpers import make_image_file, make_uploaded_file
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 class TestValidateUploadedImage(TestCase):
@@ -122,6 +124,7 @@ class TestValidateUploadedImage(TestCase):
             self.assertEqual(ctx.exception.message, file_upload_bad_mimetype)
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 class TestGenerateProfileImages(TestCase):
@@ -205,6 +208,7 @@ class TestGenerateProfileImages(TestCase):
                 yield name, image
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 class TestRemoveProfileImages(TestCase):
     """
diff --git a/openedx/core/djangoapps/profile_images/tests/test_views.py b/openedx/core/djangoapps/profile_images/tests/test_views.py
index ea82ad21331..812f2d74aba 100644
--- a/openedx/core/djangoapps/profile_images/tests/test_views.py
+++ b/openedx/core/djangoapps/profile_images/tests/test_views.py
@@ -3,6 +3,7 @@ Test cases for the HTTP endpoints of the profile image api.
 """
 from contextlib import closing
 import datetime
+from nose.plugins.attrib import attr
 from pytz import UTC
 import unittest
 
@@ -151,6 +152,7 @@ class ProfileImageEndpointMixin(UserSettingsEventTestMixin):
         self.assert_no_events_were_emitted()
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 @mock.patch('openedx.core.djangoapps.profile_images.views.log')
 class ProfileImageViewGeneralTestCase(ProfileImageEndpointMixin, APITestCase):
@@ -170,6 +172,7 @@ class ProfileImageViewGeneralTestCase(ProfileImageEndpointMixin, APITestCase):
         self.assert_no_events_were_emitted()
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 @mock.patch('openedx.core.djangoapps.profile_images.views.log')
@@ -380,6 +383,7 @@ class ProfileImageViewPostTestCase(ProfileImageEndpointMixin, APITestCase):
         self.assert_no_events_were_emitted()
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 @mock.patch('openedx.core.djangoapps.profile_images.views.log')
 class ProfileImageViewDeleteTestCase(ProfileImageEndpointMixin, APITestCase):
@@ -510,6 +514,7 @@ class DeprecatedProfileImageTestMixin(ProfileImageEndpointMixin):
         self.assert_no_events_were_emitted()
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 @mock.patch('openedx.core.djangoapps.profile_images.views.log')
 class DeprecatedProfileImageUploadTestCase(DeprecatedProfileImageTestMixin, APITestCase):
@@ -522,6 +527,7 @@ class DeprecatedProfileImageUploadTestCase(DeprecatedProfileImageTestMixin, APIT
     _replacement_method = 'openedx.core.djangoapps.profile_images.views.ProfileImageView.post'
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Profile Image API is only supported in LMS')
 @mock.patch('openedx.core.djangoapps.profile_images.views.log')
 class DeprecatedProfileImageRemoveTestCase(DeprecatedProfileImageTestMixin, APITestCase):
diff --git a/openedx/core/djangoapps/programs/tests/test_models.py b/openedx/core/djangoapps/programs/tests/test_models.py
index 5f0c3f7cfe9..6ea3fcba715 100644
--- a/openedx/core/djangoapps/programs/tests/test_models.py
+++ b/openedx/core/djangoapps/programs/tests/test_models.py
@@ -2,11 +2,13 @@
 import ddt
 from django.test import TestCase
 import mock
+from nose.plugins.attrib import attr
 
 from openedx.core.djangoapps.programs.models import ProgramsApiConfig
 from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
 
 
+@attr('shard_2')
 @ddt.ddt
 # ConfigurationModels use the cache. Make every cache get a miss.
 @mock.patch('config_models.models.cache.get', return_value=None)
diff --git a/openedx/core/djangoapps/programs/tests/test_signals.py b/openedx/core/djangoapps/programs/tests/test_signals.py
index e105f1defaf..5560c3e671a 100644
--- a/openedx/core/djangoapps/programs/tests/test_signals.py
+++ b/openedx/core/djangoapps/programs/tests/test_signals.py
@@ -3,6 +3,7 @@ This module contains tests for programs-related signals and signal handlers.
 """
 
 from django.test import TestCase
+from nose.plugins.attrib import attr
 import mock
 
 from student.tests.factories import UserFactory
@@ -14,6 +15,7 @@ from openedx.core.djangoapps.programs.signals import handle_course_cert_awarded
 TEST_USERNAME = 'test-user'
 
 
+@attr('shard_2')
 @mock.patch('openedx.core.djangoapps.programs.tasks.v1.tasks.award_program_certificates.delay')
 @mock.patch(
     'openedx.core.djangoapps.programs.models.ProgramsApiConfig.is_certification_enabled',
diff --git a/openedx/core/djangoapps/programs/tests/test_utils.py b/openedx/core/djangoapps/programs/tests/test_utils.py
index 97034204486..f0f382dc982 100644
--- a/openedx/core/djangoapps/programs/tests/test_utils.py
+++ b/openedx/core/djangoapps/programs/tests/test_utils.py
@@ -6,6 +6,7 @@ from django.core.cache import cache
 from django.test import TestCase
 import httpretty
 import mock
+from nose.plugins.attrib import attr
 from oauth2_provider.tests.factories import ClientFactory
 from provider.constants import CONFIDENTIAL
 
@@ -19,6 +20,7 @@ from student.tests.factories import UserFactory
 
 
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+@attr('shard_2')
 class TestProgramRetrieval(ProgramsApiConfigMixin, ProgramsDataMixin,
                            CredentialsApiConfigMixin, TestCase):
     """Tests covering the retrieval of programs from the Programs service."""
diff --git a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py
index 0aee5ab904f..50e5859ba46 100644
--- a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py
+++ b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py
@@ -12,6 +12,7 @@ from django.test import TestCase
 from django.test.client import RequestFactory
 from django.test.utils import override_settings
 from mock import patch
+from nose.plugins.attrib import attr
 
 from student.tests.factories import UserFactory
 
@@ -30,6 +31,7 @@ def create_mock_request():
     return request
 
 
+@attr('shard_2')
 class TestSafeSessionProcessRequest(TestSafeSessionsLogMixin, TestCase):
     """
     Test class for SafeSessionMiddleware.process_request
@@ -130,6 +132,7 @@ class TestSafeSessionProcessRequest(TestSafeSessionsLogMixin, TestCase):
         self.assert_user_in_session()
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestSafeSessionProcessResponse(TestSafeSessionsLogMixin, TestCase):
     """
@@ -232,6 +235,7 @@ class TestSafeSessionProcessResponse(TestSafeSessionsLogMixin, TestCase):
         self.assert_response_with_delete_cookie()
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestSafeSessionMiddleware(TestSafeSessionsLogMixin, TestCase):
     """
diff --git a/openedx/core/djangoapps/safe_sessions/tests/test_safe_cookie_data.py b/openedx/core/djangoapps/safe_sessions/tests/test_safe_cookie_data.py
index 468d9c09b0e..d12dfc43d08 100644
--- a/openedx/core/djangoapps/safe_sessions/tests/test_safe_cookie_data.py
+++ b/openedx/core/djangoapps/safe_sessions/tests/test_safe_cookie_data.py
@@ -7,12 +7,14 @@ import ddt
 from django.test import TestCase
 import itertools
 from mock import patch
+from nose.plugins.attrib import attr
 from time import time
 
 from ..middleware import SafeCookieData, SafeCookieError
 from .test_utils import TestSafeSessionsLogMixin
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestSafeCookieData(TestSafeSessionsLogMixin, TestCase):
     """
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py
index db086759e80..ec2ac28f7a0 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py
@@ -9,6 +9,7 @@ from dateutil.parser import parse as parse_datetime
 
 from mock import Mock, patch
 from django.test import TestCase
+from nose.plugins.attrib import attr
 from nose.tools import raises
 import unittest
 from student.tests.factories import UserFactory
@@ -33,6 +34,7 @@ def mock_render_to_string(template_name, context):
     return str((template_name, sorted(context.iteritems())))
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
 class TestAccountApi(UserSettingsEventTestMixin, TestCase):
     """
@@ -230,6 +232,7 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
         verify_event_emitted([], [{"code": "en"}, {"code": "fr"}])
 
 
+@attr('shard_2')
 @patch('openedx.core.djangoapps.user_api.accounts.image_helpers._PROFILE_IMAGE_SIZES', [50, 10])
 @patch.dict(
     'openedx.core.djangoapps.user_api.accounts.image_helpers.PROFILE_IMAGE_SIZES_MAP',
@@ -282,6 +285,7 @@ class AccountSettingsOnCreationTest(TestCase):
         })
 
 
+@attr('shard_2')
 @ddt.ddt
 class AccountCreationActivationAndPasswordChangeTest(TestCase):
     """
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py
index 2a404762793..5c8510b341b 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py
@@ -4,6 +4,7 @@ Tests for helpers.py
 import datetime
 import hashlib
 from mock import patch
+from nose.plugins.attrib import attr
 from pytz import UTC
 from unittest import skipUnless
 
@@ -17,6 +18,7 @@ TEST_SIZES = {'full': 50, 'small': 10}
 TEST_PROFILE_IMAGE_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 01, tzinfo=UTC)
 
 
+@attr('shard_2')
 @patch.dict('openedx.core.djangoapps.user_api.accounts.image_helpers.PROFILE_IMAGE_SIZES_MAP', TEST_SIZES, clear=True)
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class ProfileImageUrlTestCase(TestCase):
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
index e525c14743c..db6df2fcd97 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py
@@ -9,6 +9,7 @@ import ddt
 import hashlib
 import json
 from mock import patch
+from nose.plugins.attrib import attr
 from pytz import UTC
 import unittest
 
@@ -118,6 +119,7 @@ class UserAPITestCase(APITestCase):
     {'full': 50, 'small': 10},
     clear=True
 )
+@attr('shard_2')
 class TestAccountAPI(UserAPITestCase):
     """
     Unit tests for the Account API.
@@ -717,6 +719,7 @@ class TestAccountAPI(UserAPITestCase):
         )
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class TestAccountAPITransactions(TransactionTestCase):
     """
diff --git a/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py b/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py
index ca4422658b3..83a75469e83 100644
--- a/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py
+++ b/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py
@@ -4,10 +4,12 @@ Test the user course tag API.
 from django.test import TestCase
 
 from student.tests.factories import UserFactory
+from nose.plugins.attrib import attr
 from openedx.core.djangoapps.user_api.course_tag import api as course_tag_api
 from opaque_keys.edx.locations import SlashSeparatedCourseKey
 
 
+@attr('shard_2')
 class TestCourseTagAPI(TestCase):
     """
     Test the user service
diff --git a/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py b/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py
index 9ea502a166d..ca8f54ea428 100644
--- a/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py
+++ b/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py
@@ -5,6 +5,7 @@ import tempfile
 import shutil
 import csv
 from collections import defaultdict
+from nose.plugins.attrib import attr
 from unittest import skipUnless
 
 import ddt
@@ -21,6 +22,7 @@ from openedx.core.djangoapps.user_api.models import UserOrgTag
 from openedx.core.djangoapps.user_api.management.commands import email_opt_in_list
 
 
+@attr('shard_2')
 @ddt.ddt
 @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class EmailOptInListTest(ModuleStoreTestCase):
diff --git a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py
index 2bdc1680bd2..682d9b08280 100644
--- a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py
+++ b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py
@@ -6,6 +6,7 @@ import datetime
 import ddt
 import unittest
 from mock import patch
+from nose.plugins.attrib import attr
 from pytz import UTC
 
 from django.conf import settings
@@ -28,6 +29,7 @@ from ...preferences.api import (
 )
 
 
+@attr('shard_2')
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
 class TestPreferenceAPI(TestCase):
     """
@@ -319,6 +321,7 @@ class TestPreferenceAPI(TestCase):
         )
 
 
+@attr('shard_2')
 @ddt.ddt
 class UpdateEmailOptInTests(ModuleStoreTestCase):
     """
diff --git a/openedx/core/djangolib/tests/test_js_utils.py b/openedx/core/djangolib/tests/test_js_utils.py
index 6bfb51476c8..566db3cc433 100644
--- a/openedx/core/djangolib/tests/test_js_utils.py
+++ b/openedx/core/djangolib/tests/test_js_utils.py
@@ -3,6 +3,7 @@
 Tests for js_utils.py
 """
 import json
+from nose.plugins.attrib import attr
 from unittest import TestCase
 import HTMLParser
 
@@ -13,6 +14,7 @@ from openedx.core.djangolib.js_utils import (
 )
 
 
+@attr('shard_2')
 class TestJSUtils(TestCase):
     """
     Test JS utils
diff --git a/openedx/core/djangolib/tests/test_markup.py b/openedx/core/djangolib/tests/test_markup.py
index e0a7b603b7a..30c7403b7b7 100644
--- a/openedx/core/djangolib/tests/test_markup.py
+++ b/openedx/core/djangolib/tests/test_markup.py
@@ -3,6 +3,7 @@
 Tests for openedx.core.djangolib.markup
 """
 
+from nose.plugins.attrib import attr
 import unittest
 
 import ddt
@@ -12,6 +13,7 @@ from mako.template import Template
 from openedx.core.djangolib.markup import Text, HTML
 
 
+@attr('shard_2')
 @ddt.ddt
 class FormatHtmlTest(unittest.TestCase):
     """Test that we can format plain strings and HTML into them properly."""
diff --git a/openedx/core/lib/api/tests/test_authentication.py b/openedx/core/lib/api/tests/test_authentication.py
index 1e1091b3eb3..ea572b841bd 100644
--- a/openedx/core/lib/api/tests/test_authentication.py
+++ b/openedx/core/lib/api/tests/test_authentication.py
@@ -18,6 +18,7 @@ from django.test import TestCase
 from django.utils import unittest
 from django.utils.http import urlencode
 from mock import patch
+from nose.plugins.attrib import attr
 from rest_framework import exceptions
 from rest_framework import status
 from rest_framework.permissions import IsAuthenticated
@@ -75,6 +76,7 @@ urlpatterns = patterns(
 )
 
 
+@attr('shard_2')
 @ddt.ddt
 class OAuth2Tests(TestCase):
     """OAuth 2.0 authentication"""
@@ -292,6 +294,7 @@ class OAuth2Tests(TestCase):
         self.assertEqual(response.status_code, scope_statuses.write_status)
 
 
+@attr('shard_2')
 @ddt.ddt
 @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
 class TestJWTAuthToggle(JwtMixin, TestCase):
diff --git a/openedx/core/lib/api/tests/test_exceptions.py b/openedx/core/lib/api/tests/test_exceptions.py
index 9ce559b797a..e354d06400f 100644
--- a/openedx/core/lib/api/tests/test_exceptions.py
+++ b/openedx/core/lib/api/tests/test_exceptions.py
@@ -3,11 +3,13 @@ Test Custom Exceptions
 """
 import ddt
 from django.test import TestCase
+from nose.plugins.attrib import attr
 from rest_framework import exceptions as drf_exceptions
 
 from .. import exceptions
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestDictExceptionsAllowDictDetails(TestCase):
     """
@@ -44,6 +46,7 @@ class TestDictExceptionsAllowDictDetails(TestCase):
         self.assertEqual(exc.available_renderers, ['application/json'])
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestDictExceptionSubclassing(TestCase):
     """
diff --git a/openedx/core/lib/api/tests/test_paginators.py b/openedx/core/lib/api/tests/test_paginators.py
index bc6d2340309..dfdc3aaba9e 100644
--- a/openedx/core/lib/api/tests/test_paginators.py
+++ b/openedx/core/lib/api/tests/test_paginators.py
@@ -4,6 +4,7 @@ from collections import namedtuple
 
 import ddt
 from mock import Mock, MagicMock
+from nose.plugins.attrib import attr
 from unittest import TestCase
 from django.http import Http404
 from django.test import RequestFactory
@@ -12,6 +13,7 @@ from rest_framework import serializers
 from openedx.core.lib.api.paginators import NamespacedPageNumberPagination, paginate_search_results
 
 
+@attr('shard_2')
 @ddt.ddt
 class PaginateSearchResultsTestCase(TestCase):
     """Test cases for paginate_search_results method"""
@@ -122,6 +124,7 @@ class PaginateSearchResultsTestCase(TestCase):
             paginate_search_results(self.mock_model, self.search_results, self.default_size, page_num)
 
 
+@attr('shard_2')
 class NamespacedPaginationTestCase(TestCase):
     """
     Test behavior of `NamespacedPageNumberPagination`
diff --git a/openedx/core/lib/api/tests/test_parsers.py b/openedx/core/lib/api/tests/test_parsers.py
index 6245b23dc4f..fb6722bc462 100644
--- a/openedx/core/lib/api/tests/test_parsers.py
+++ b/openedx/core/lib/api/tests/test_parsers.py
@@ -3,6 +3,7 @@ TestCases verifying proper behavior of custom DRF request parsers.
 """
 
 from collections import namedtuple
+from nose.plugins.attrib import attr
 from io import BytesIO
 
 from rest_framework import exceptions
@@ -11,6 +12,7 @@ from rest_framework.test import APITestCase, APIRequestFactory
 from openedx.core.lib.api import parsers
 
 
+@attr('shard_2')
 class TestTypedFileUploadParser(APITestCase):
     """
     Tests that verify the behavior of TypedFileUploadParser
diff --git a/openedx/core/lib/api/tests/test_permissions.py b/openedx/core/lib/api/tests/test_permissions.py
index f44b60f1f92..bc121d32f5e 100644
--- a/openedx/core/lib/api/tests/test_permissions.py
+++ b/openedx/core/lib/api/tests/test_permissions.py
@@ -4,6 +4,7 @@ import ddt
 from django.contrib.auth.models import AnonymousUser
 from django.http import Http404
 from django.test import TestCase, RequestFactory
+from nose.plugins.attrib import attr
 
 from student.roles import CourseStaffRole, CourseInstructorRole
 from openedx.core.lib.api.permissions import (
@@ -29,6 +30,7 @@ class TestCcxObject(TestObject):
         self.coach = user
 
 
+@attr('shard_2')
 class IsCourseStaffInstructorTests(TestCase):
     """ Test for IsCourseStaffInstructor permission class. """
 
@@ -62,6 +64,7 @@ class IsCourseStaffInstructorTests(TestCase):
         self.assertFalse(self.permission.has_object_permission(self.request, None, self.obj))
 
 
+@attr('shard_2')
 class IsMasterCourseStaffInstructorTests(TestCase):
     """ Test for IsMasterCourseStaffInstructorTests permission class. """
 
@@ -106,6 +109,7 @@ class IsMasterCourseStaffInstructorTests(TestCase):
             self.permission.has_permission(post_request, None)
 
 
+@attr('shard_2')
 @ddt.ddt
 class IsStaffOrOwnerTests(TestCase):
     """ Tests for IsStaffOrOwner permission class. """
diff --git a/openedx/core/lib/block_structure/tests/test_block_structure.py b/openedx/core/lib/block_structure/tests/test_block_structure.py
index d1362dabbfd..ebca1030022 100644
--- a/openedx/core/lib/block_structure/tests/test_block_structure.py
+++ b/openedx/core/lib/block_structure/tests/test_block_structure.py
@@ -6,6 +6,7 @@ from collections import namedtuple
 from copy import deepcopy
 import ddt
 import itertools
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from openedx.core.lib.graph_traversals import traverse_post_order
@@ -15,6 +16,7 @@ from ..exceptions import TransformerException
 from .helpers import MockXBlock, MockTransformer, ChildrenMapTestMixin
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestBlockStructure(TestCase, ChildrenMapTestMixin):
     """
@@ -43,6 +45,7 @@ class TestBlockStructure(TestCase, ChildrenMapTestMixin):
         self.assertNotIn(len(children_map) + 1, block_structure)
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestBlockStructureData(TestCase, ChildrenMapTestMixin):
     """
diff --git a/openedx/core/lib/block_structure/tests/test_cache.py b/openedx/core/lib/block_structure/tests/test_cache.py
index 6654cbab9e9..c6dd917e01e 100644
--- a/openedx/core/lib/block_structure/tests/test_cache.py
+++ b/openedx/core/lib/block_structure/tests/test_cache.py
@@ -1,12 +1,14 @@
 """
 Tests for block_structure/cache.py
 """
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from ..cache import BlockStructureCache
 from .helpers import ChildrenMapTestMixin, MockCache, MockTransformer
 
 
+@attr('shard_2')
 class TestBlockStructureCache(ChildrenMapTestMixin, TestCase):
     """
     Tests for BlockStructureFactory
diff --git a/openedx/core/lib/block_structure/tests/test_factory.py b/openedx/core/lib/block_structure/tests/test_factory.py
index c7c6e69c3d7..1530f658814 100644
--- a/openedx/core/lib/block_structure/tests/test_factory.py
+++ b/openedx/core/lib/block_structure/tests/test_factory.py
@@ -1,6 +1,7 @@
 """
 Tests for block_structure_factory.py
 """
+from nose.plugins.attrib import attr
 from unittest import TestCase
 from xmodule.modulestore.exceptions import ItemNotFoundError
 
@@ -11,6 +12,7 @@ from .helpers import (
 )
 
 
+@attr('shard_2')
 class TestBlockStructureFactory(TestCase, ChildrenMapTestMixin):
     """
     Tests for BlockStructureFactory
diff --git a/openedx/core/lib/block_structure/tests/test_manager.py b/openedx/core/lib/block_structure/tests/test_manager.py
index 19ea53e5de3..5c32d6b08b9 100644
--- a/openedx/core/lib/block_structure/tests/test_manager.py
+++ b/openedx/core/lib/block_structure/tests/test_manager.py
@@ -1,6 +1,7 @@
 """
 Tests for manager.py
 """
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from ..exceptions import UsageKeyNotInBlockStructure
@@ -83,6 +84,7 @@ class TestTransformer1(MockTransformer):
         return data_key + 't1.val1.' + unicode(block_key)
 
 
+@attr('shard_2')
 class TestBlockStructureManager(TestCase, ChildrenMapTestMixin):
     """
     Test class for BlockStructureManager.
diff --git a/openedx/core/lib/block_structure/tests/test_transformer_registry.py b/openedx/core/lib/block_structure/tests/test_transformer_registry.py
index 008ed8f3bc6..5e6db93cff4 100644
--- a/openedx/core/lib/block_structure/tests/test_transformer_registry.py
+++ b/openedx/core/lib/block_structure/tests/test_transformer_registry.py
@@ -3,6 +3,7 @@ Tests for transformer_registry.py
 """
 
 import ddt
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from ..transformer_registry import TransformerRegistry
@@ -30,6 +31,7 @@ class UnregisteredTestTransformer3(MockTransformer):
     pass
 
 
+@attr('shard_2')
 @ddt.ddt
 class TransformerRegistryTestCase(TestCase):
     """
diff --git a/openedx/core/lib/block_structure/tests/test_transformers.py b/openedx/core/lib/block_structure/tests/test_transformers.py
index 2b77ab16e30..06498ba17f7 100644
--- a/openedx/core/lib/block_structure/tests/test_transformers.py
+++ b/openedx/core/lib/block_structure/tests/test_transformers.py
@@ -2,6 +2,7 @@
 Tests for transformers.py
 """
 from mock import MagicMock, patch
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from ..block_structure import BlockStructureModulestoreData
@@ -12,6 +13,7 @@ from .helpers import (
 )
 
 
+@attr('shard_2')
 class TestBlockStructureTransformers(ChildrenMapTestMixin, TestCase):
     """
     Test class for testing BlockStructureTransformers
diff --git a/openedx/core/lib/gating/tests/test_api.py b/openedx/core/lib/gating/tests/test_api.py
index 4854ea71a38..172555fdd05 100644
--- a/openedx/core/lib/gating/tests/test_api.py
+++ b/openedx/core/lib/gating/tests/test_api.py
@@ -2,6 +2,7 @@
 Tests for the gating API
 """
 from mock import patch, MagicMock
+from nose.plugins.attrib import attr
 from ddt import ddt, data
 from milestones.tests.utils import MilestonesTestCaseMixin
 from milestones import api as milestones_api
@@ -11,6 +12,7 @@ from openedx.core.lib.gating import api as gating_api
 from openedx.core.lib.gating.exceptions import GatingValidationError
 
 
+@attr('shard_2')
 @ddt
 @patch.dict('django.conf.settings.FEATURES', {'MILESTONES_APP': True})
 class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
diff --git a/openedx/core/lib/tests/test_course_tab_api.py b/openedx/core/lib/tests/test_course_tab_api.py
index e6cc09f1f09..ffd42328a41 100644
--- a/openedx/core/lib/tests/test_course_tab_api.py
+++ b/openedx/core/lib/tests/test_course_tab_api.py
@@ -3,11 +3,13 @@ Tests for the plugin API
 """
 
 from django.test import TestCase
+from nose.plugins.attrib import attr
 
 from openedx.core.lib.api.plugins import PluginError
 from openedx.core.lib.course_tabs import CourseTabPluginManager
 
 
+@attr('shard_2')
 class TestPluginApi(TestCase):
     """
     Unit tests for the plugin API
diff --git a/openedx/core/lib/tests/test_course_tabs.py b/openedx/core/lib/tests/test_course_tabs.py
index 6bb56e1c89e..c11619369b5 100644
--- a/openedx/core/lib/tests/test_course_tabs.py
+++ b/openedx/core/lib/tests/test_course_tabs.py
@@ -1,6 +1,7 @@
 """ Tests of specific tabs. """
 
 from mock import patch, Mock
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 import xmodule.tabs as xmodule_tabs
@@ -8,6 +9,7 @@ import xmodule.tabs as xmodule_tabs
 from openedx.core.lib.course_tabs import CourseTabPluginManager
 
 
+@attr('shard_2')
 class CourseTabPluginManagerTestCase(TestCase):
     """Test cases for CourseTabPluginManager class"""
 
@@ -37,6 +39,7 @@ class CourseTabPluginManagerTestCase(TestCase):
         )
 
 
+@attr('shard_2')
 class KeyCheckerTestCase(TestCase):
     """Test cases for KeyChecker class"""
 
@@ -55,6 +58,7 @@ class KeyCheckerTestCase(TestCase):
             xmodule_tabs.key_checker(self.invalid_keys)(self.dict_value)
 
 
+@attr('shard_2')
 class NeedNameTestCase(TestCase):
     """Test cases for NeedName validator"""
 
diff --git a/openedx/core/lib/tests/test_courses.py b/openedx/core/lib/tests/test_courses.py
index 690d4f1d492..696f9e503b5 100644
--- a/openedx/core/lib/tests/test_courses.py
+++ b/openedx/core/lib/tests/test_courses.py
@@ -4,6 +4,7 @@ Tests for functionality in openedx/core/lib/courses.py.
 
 import ddt
 from django.test.utils import override_settings
+from nose.plugins.attrib import attr
 
 from xmodule.modulestore import ModuleStoreEnum
 from xmodule.modulestore.tests.factories import CourseFactory
@@ -12,6 +13,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from ..courses import course_image_url
 
 
+@attr('shard_2')
 @ddt.ddt
 class CourseImageTestCase(ModuleStoreTestCase):
     """Tests for course image URLs."""
diff --git a/openedx/core/lib/tests/test_edx_api_utils.py b/openedx/core/lib/tests/test_edx_api_utils.py
index 11eab1db1d0..ca62f850e58 100644
--- a/openedx/core/lib/tests/test_edx_api_utils.py
+++ b/openedx/core/lib/tests/test_edx_api_utils.py
@@ -6,6 +6,7 @@ from django.core.cache import cache
 from django.test import TestCase
 import httpretty
 import mock
+from nose.plugins.attrib import attr
 from oauth2_provider.tests.factories import ClientFactory
 from provider.constants import CONFIDENTIAL
 from testfixtures import LogCapture
@@ -21,6 +22,7 @@ from student.tests.factories import UserFactory
 LOGGER_NAME = 'openedx.core.lib.edx_api_utils'
 
 
+@attr('shard_2')
 class TestApiDataRetrieval(CredentialsApiConfigMixin, CredentialsDataMixin, ProgramsApiConfigMixin, ProgramsDataMixin,
                            TestCase):
     """Test utility for API data retrieval."""
diff --git a/openedx/core/lib/tests/test_graph_traversals.py b/openedx/core/lib/tests/test_graph_traversals.py
index 840b05be88b..3e9ac96ee2d 100644
--- a/openedx/core/lib/tests/test_graph_traversals.py
+++ b/openedx/core/lib/tests/test_graph_traversals.py
@@ -3,6 +3,7 @@ Tests for graph traversal generator functions.
 """
 
 from collections import defaultdict
+from nose.plugins.attrib import attr
 from unittest import TestCase
 
 from ..graph_traversals import (
@@ -10,6 +11,7 @@ from ..graph_traversals import (
 )
 
 
+@attr('shard_2')
 class TestGraphTraversals(TestCase):
     """
     Test Class for graph traversal generator functions.
diff --git a/openedx/core/lib/tests/test_token_utils.py b/openedx/core/lib/tests/test_token_utils.py
index 662d5fc5dcb..495e4eebc44 100644
--- a/openedx/core/lib/tests/test_token_utils.py
+++ b/openedx/core/lib/tests/test_token_utils.py
@@ -9,6 +9,7 @@ from django.test import TestCase
 from django.test.utils import override_settings
 import freezegun
 import jwt
+from nose.plugins.attrib import attr
 from oauth2_provider.tests.factories import ClientFactory
 from provider.constants import CONFIDENTIAL
 
@@ -17,6 +18,7 @@ from student.models import anonymous_id_for_user
 from student.tests.factories import UserFactory, UserProfileFactory
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestIdTokenGeneration(TestCase):
     """Tests covering ID token generation."""
diff --git a/openedx/core/lib/tests/test_xblock_utils.py b/openedx/core/lib/tests/test_xblock_utils.py
index a9b23f66a33..b2d206d7f45 100644
--- a/openedx/core/lib/tests/test_xblock_utils.py
+++ b/openedx/core/lib/tests/test_xblock_utils.py
@@ -4,6 +4,7 @@ Tests for xblock_utils.py
 from __future__ import unicode_literals, absolute_import
 
 import ddt
+from nose.plugins.attrib import attr
 import uuid
 
 from django.test.client import RequestFactory
@@ -27,6 +28,7 @@ from openedx.core.lib.xblock_utils import (
 )
 
 
+@attr('shard_2')
 @ddt.ddt
 class TestXblockUtils(SharedModuleStoreTestCase):
     """
diff --git a/scripts/generic-ci-tests.sh b/scripts/generic-ci-tests.sh
index 136c40f4720..e9519a88368 100755
--- a/scripts/generic-ci-tests.sh
+++ b/scripts/generic-ci-tests.sh
@@ -94,7 +94,10 @@ END
                 paver test_system -s lms --extra_args="--attr='shard_1' --with-flaky" --cov_args="-p"
                 ;;
             "2")
-                paver test_system -s lms --extra_args="--attr='shard_1=False' --with-flaky" --cov_args="-p"
+                paver test_system -s lms --extra_args="--attr='shard_2' --with-flaky" --cov_args="-p"
+                ;;
+            "3")
+                paver test_system -s lms --extra_args="--attr='shard_1=False,shard_2=False' --with-flaky" --cov_args="-p"
                 ;;
             *)
                 paver test_system -s lms --extra_args="--with-flaky" --cov_args="-p"
-- 
GitLab