コード例 #1
0
 def setUp(self):
     with session.begin():
         self.owner = data_setup.create_user(password=u"owner")
         self.privileged = data_setup.create_user(password=u"privileged")
         self.system = data_setup.create_system(
             owner=self.owner, shared=True, lab_controller=data_setup.create_labcontroller()
         )
         data_setup.configure_system_power(self.system)
         self.system.custom_access_policy.add_rule(permission=SystemPermission.control_system, user=self.privileged)
         self.unprivileged = data_setup.create_user(password=u"unprivileged")
         data_setup.create_running_job(system=self.system)
     self.browser = self.get_browser()
コード例 #2
0
 def setUp(self):
     with session.begin():
         self.owner = data_setup.create_user(password=u'owner')
         self.privileged = data_setup.create_user(password=u'privileged')
         self.system = data_setup.create_system(
             owner=self.owner,
             shared=True,
             lab_controller=data_setup.create_labcontroller())
         data_setup.configure_system_power(self.system)
         self.system.custom_access_policy.add_rule(
             permission=SystemPermission.control_system,
             user=self.privileged)
         self.unprivileged = data_setup.create_user(
             password=u'unprivileged')
         data_setup.create_running_job(system=self.system)
     self.browser = self.get_browser()
コード例 #3
0
    def test_reserved_openstack_instance(self):
        with session.begin():
            owner = data_setup.create_user(
                email_address=u'*****@*****.**')
            distro_tree = data_setup.create_distro_tree(
                distro_name=u'MicrowaveOS',
                variant=u'ThreeHeats',
                arch=u'x86_64')
            job = data_setup.create_running_job(
                owner=owner,
                virt=True,
                instance_id=uuid.UUID('00000000-1111-2222-3333-444444444444'),
                distro_tree=distro_tree,
                whiteboard=u'Operation Righteous Cowboy Lightning',
                recipe_whiteboard=u'Everything Sunny All the Time Always')
            recipe = job.recipesets[0].recipes[0]
            data_setup.mark_recipe_installation_finished(
                recipe, fqdn=u'bitenuker.ge.invalid')

        with session.begin():
            bkr.server.mail.reservesys_notify(recipe)
        self.assertEqual(len(self.mail_capture.captured_mails), 1)
        sender, rcpts, raw_msg = self.mail_capture.captured_mails[0]
        self.assertEqual(rcpts, [owner.email_address])
        msg = email.message_from_string(raw_msg)
        self.assertEqual(msg['To'], owner.email_address)
        self.assertEqual(msg['Subject'],
                         '[Beaker System Reserved] bitenuker.ge.invalid')
        self.assertEqual(msg['X-Beaker-Notification'], 'system-reservation')

        expected_mail_body = u"""\
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **
                 This System is reserved by [email protected]

 To return this system early, you can click on 'Release System' against this recipe
 from the Web UI. Ensure you have your logs off the system before returning to
 Beaker.
  %(base)srecipes/%(recipeid)s

 For system details, see:
  http://openstack.example.invalid/dashboard/project/instances/00000000-1111-2222-3333-444444444444/

 For the default root password, see:
  %(base)sprefs

      Beaker Test information:
                         HOSTNAME=bitenuker.ge.invalid
                            JOBID=%(jobid)s
                         RECIPEID=%(recipeid)s
                           DISTRO=MicrowaveOS ThreeHeats x86_64
                     ARCHITECTURE=x86_64

      Job Whiteboard: Operation Righteous Cowboy Lightning

      Recipe Whiteboard: Everything Sunny All the Time Always
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **""" \
            % dict(base=get_server_base(), recipeid=recipe.id, jobid=job.id)
        actual_mail_body = msg.get_payload(decode=True)
        self.assertMultiLineEqual(actual_mail_body, expected_mail_body)
コード例 #4
0
ファイル: test_mail.py プロジェクト: qhsong/beaker
    def test_system_reserved_notification(self):
        with session.begin():
            owner = data_setup.create_user(
                email_address=u'*****@*****.**')
            system = data_setup.create_system(
                fqdn=u'funcooker.ge.invalid',
                lab_controller=data_setup.create_labcontroller())
            distro_tree = data_setup.create_distro_tree(
                distro_name=u'MicrowaveOS',
                variant=u'ThreeHeats',
                arch=u'x86_64')
            job = data_setup.create_running_job(
                owner=owner,
                system=system,
                distro_tree=distro_tree,
                whiteboard=u'Chain Reaction of Mental Anguish',
                recipe_whiteboard=u'Christmas Attack Zone')
            recipe = job.recipesets[0].recipes[0]

        with session.begin():
            bkr.server.mail.reservesys_notify(job.recipesets[0].recipes[0])
        self.assertEqual(len(self.mail_capture.captured_mails), 1)
        sender, rcpts, raw_msg = self.mail_capture.captured_mails[0]
        self.assertEqual(rcpts, [owner.email_address])
        msg = email.message_from_string(raw_msg)
        self.assertEqual(msg['To'], owner.email_address)
        self.assertEqual(msg['Subject'],
                         '[Beaker System Reserved] funcooker.ge.invalid')
        self.assertEqual(msg['X-Beaker-Notification'], 'system-reservation')

        expected_mail_body = u"""\
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **
                 This System is reserved by [email protected]

 To return this system early, you can click on 'Release System' against this recipe
 from the Web UI. Ensure you have your logs off the system before returning to
 Beaker.
  %(base)srecipes/%(recipeid)s

 For ssh, kvm, serial and power control operations please look here:
  %(base)sview/funcooker.ge.invalid

 For the default root password, see:
  %(base)sprefs

      Beaker Test information:
                         HOSTNAME=funcooker.ge.invalid
                            JOBID=%(jobid)s
                         RECIPEID=%(recipeid)s
                           DISTRO=MicrowaveOS ThreeHeats x86_64
                     ARCHITECTURE=x86_64

      Job Whiteboard: Chain Reaction of Mental Anguish

      Recipe Whiteboard: Christmas Attack Zone
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **""" \
            % dict(base=get_server_base(), recipeid=recipe.id, jobid=job.id)
        actual_mail_body = msg.get_payload(decode=True)
        self.assertEqual(actual_mail_body, expected_mail_body)
コード例 #5
0
ファイル: test_mail.py プロジェクト: beaker-project/beaker
    def test_system_reserved_notification_on(self):
        with session.begin():
            owner = data_setup.create_user(
                    email_address=u'*****@*****.**')
            system = data_setup.create_system(fqdn=u'funcooker.ge.invalid',
                    lab_controller=data_setup.create_labcontroller())
            distro_tree = data_setup.create_distro_tree(distro_name=u'MicrowaveOS-20141016.0',
                    variant=u'ThreeHeats', arch=u'x86_64')
            job = data_setup.create_running_job(owner=owner, system=system,
                    distro_tree=distro_tree,
                    whiteboard=u'Chain Reaction of Mental Anguish',
                    recipe_whiteboard=u'Christmas Attack Zone')
            recipe = job.recipesets[0].recipes[0]

        mail_capture_thread.start_capturing()
        with session.begin():
            bkr.server.mail.reservesys_notify(job.recipesets[0].recipes[0])
        captured_mails = mail_capture_thread.stop_capturing()
        self.assertEqual(len(captured_mails), 1)
        sender, rcpts, raw_msg = captured_mails[0]
        self.assertEqual(rcpts, [owner.email_address])
        msg = email.message_from_string(raw_msg)
        self.assertEqual(msg['To'], owner.email_address)
        self.assertEqual(msg['Subject'],
                '[Beaker System Reserved] funcooker.ge.invalid')
        self.assertEqual(msg['X-Beaker-Notification'], 'system-reservation')

        expected_mail_body = u"""\
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **
                 This System is reserved by [email protected]

 To return this system early, you can click on 'Release System' against this recipe
 from the Web UI. Ensure you have your logs off the system before returning to
 Beaker.
  %(base)srecipes/%(recipeid)s

 For ssh, kvm, serial and power control operations please look here:
  %(base)sview/funcooker.ge.invalid

 For the default root password, see:
  %(base)sprefs

      Beaker Test information:
                         HOSTNAME=funcooker.ge.invalid
                            JOBID=%(jobid)s
                         RECIPEID=%(recipeid)s
                           DISTRO=MicrowaveOS-20141016.0 ThreeHeats x86_64
                     ARCHITECTURE=x86_64

      Job Whiteboard: Chain Reaction of Mental Anguish

      Recipe Whiteboard: Christmas Attack Zone
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **""" \
            % dict(base=get_server_base(), recipeid=recipe.id, jobid=job.id)
        actual_mail_body = msg.get_payload(decode=True)
        self.assertEqual(actual_mail_body, expected_mail_body)
コード例 #6
0
 def test_shows_currently_running_recipe(self):
     b = self.browser
     login(b)
     with session.begin():
         job = data_setup.create_running_job()
         recipe = job.recipesets[0].recipes[0]
     self.go_to_system_view(recipe.resource.system)
     usage = b.find_element_by_class_name('system-quick-usage')
     usage.find_element_by_xpath('.//span[@class="label" and text()="Reserved"]')
     usage.find_element_by_xpath('.//a[text()="%s"]' % recipe.t_id)
コード例 #7
0
 def test_opening_running_recipe_does_not_mark_it_reviewed(self):
     with session.begin():
         job = data_setup.create_running_job(owner=self.user)
         recipe = job.recipesets[0].recipes[0]
         self.assertEqual(recipe.get_reviewed_state(self.user), False)
     b = self.browser
     self.go_to_recipe_view(recipe)
     b.find_element_by_xpath('//h1[normalize-space(string(.))="Job: %s"]' % job.t_id)
     with session.begin():
         self.assertEqual(recipe.get_reviewed_state(self.user), False)
コード例 #8
0
 def test_shows_currently_running_recipe(self):
     b = self.browser
     login(b)
     with session.begin():
         job = data_setup.create_running_job()
         recipe = job.recipesets[0].recipes[0]
     self.go_to_system_view(recipe.resource.system)
     usage = b.find_element_by_class_name('system-quick-usage')
     usage.find_element_by_xpath('.//span[@class="label" and text()="Reserved"]')
     usage.find_element_by_xpath('.//a[text()="%s"]' % recipe.t_id)
コード例 #9
0
ファイル: test_mail.py プロジェクト: sujithshankar/beaker
    def test_reserved_openstack_instance(self):
        with session.begin():
            owner = data_setup.create_user(
                    email_address=u'*****@*****.**')
            distro_tree = data_setup.create_distro_tree(distro_name=u'MicrowaveOS',
                    variant=u'ThreeHeats', arch=u'x86_64')
            job = data_setup.create_running_job(owner=owner,
                    virt=True, instance_id=uuid.UUID('00000000-1111-2222-3333-444444444444'),
                    distro_tree=distro_tree,
                    whiteboard=u'Operation Righteous Cowboy Lightning',
                    recipe_whiteboard=u'Everything Sunny All the Time Always')
            recipe = job.recipesets[0].recipes[0]
            data_setup.mark_recipe_installation_finished(recipe,
                    fqdn=u'bitenuker.ge.invalid')

        with session.begin():
            bkr.server.mail.reservesys_notify(recipe)
        self.assertEqual(len(self.mail_capture.captured_mails), 1)
        sender, rcpts, raw_msg = self.mail_capture.captured_mails[0]
        self.assertEqual(rcpts, [owner.email_address])
        msg = email.message_from_string(raw_msg)
        self.assertEqual(msg['To'], owner.email_address)
        self.assertEqual(msg['Subject'],
                '[Beaker System Reserved] bitenuker.ge.invalid')
        self.assertEqual(msg['X-Beaker-Notification'], 'system-reservation')

        expected_mail_body = u"""\
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **
                 This System is reserved by [email protected]

 To return this system early, you can click on 'Release System' against this recipe
 from the Web UI. Ensure you have your logs off the system before returning to
 Beaker.
  %(base)srecipes/%(recipeid)s

 For system details, see:
  http://openstack.example.invalid/dashboard/project/instances/00000000-1111-2222-3333-444444444444/

 For the default root password, see:
  %(base)sprefs

      Beaker Test information:
                         HOSTNAME=bitenuker.ge.invalid
                            JOBID=%(jobid)s
                         RECIPEID=%(recipeid)s
                           DISTRO=MicrowaveOS ThreeHeats x86_64
                     ARCHITECTURE=x86_64

      Job Whiteboard: Operation Righteous Cowboy Lightning

      Recipe Whiteboard: Everything Sunny All the Time Always
**  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **  **""" \
            % dict(base=get_server_base(), recipeid=recipe.id, jobid=job.id)
        actual_mail_body = msg.get_payload(decode=True)
        self.assertMultiLineEqual(actual_mail_body, expected_mail_body)
コード例 #10
0
 def test_opening_running_recipe_does_not_mark_it_reviewed(self):
     with session.begin():
         job = data_setup.create_running_job()
         recipe = job.recipesets[0].recipes[0]
         self.assertEqual(recipe.get_reviewed_state(self.user), False)
     b = self.browser
     login(b, user=self.user.user_name, password='******')
     go_to_recipe_view(b, recipe)
     b.find_element_by_xpath('//h1[contains(string(.), "%s")]' % recipe.t_id)
     with session.begin():
         self.assertEqual(recipe.get_reviewed_state(self.user), False)
コード例 #11
0
 def test_recipe_is_extended(self):
     # The external watchdog script is supposed to print the number of seconds
     # by which the watchdog timer should be extended.
     with session.begin():
         system = data_setup.create_system(lab_controller=self.get_lc(),
                 fqdn='watchdog.script.please.extend.600')
         job = data_setup.create_running_job(system=system)
         self.addCleanup(self.cleanup_job, job)
         recipe = job.recipesets[0].recipes[0]
         recipe.extend(-1)
     wait_for_condition(lambda: self.check_watchdog_extended(recipe, 600))
コード例 #12
0
 def test_filter_finished_jobs(self):
     with session.begin():
         completed_job = data_setup.create_completed_job(task_status=TaskStatus.completed)
         cancelled_job = data_setup.create_completed_job(task_status=TaskStatus.cancelled)
         aborted_job = data_setup.create_completed_job(task_status=TaskStatus.aborted)
         running_job = data_setup.create_running_job()
     out = run_client(['bkr', 'job-list', '--finished'])
     self.assertIn(completed_job.t_id, out)
     self.assertIn(cancelled_job.t_id, out)
     self.assertIn(aborted_job.t_id, out)
     self.assertNotIn(running_job.t_id, out)
コード例 #13
0
 def test_recipe_is_aborted_on_script_failure(self):
     # If the external watchdog script exits non-zero,
     # beaker-watchdog ignores its output and aborts the recipe.
     with session.begin():
         system = data_setup.create_system(lab_controller=self.get_lc(),
                 fqdn='watchdog.script.please.crash')
         job = data_setup.create_running_job(system=system)
         self.addCleanup(self.cleanup_job, job)
         recipe = job.recipesets[0].recipes[0]
         recipe.extend(-1)
     wait_for_condition(lambda: self.check_watchdog_expiry(recipe))
コード例 #14
0
ファイル: test_job_cancel.py プロジェクト: xhernandez/beaker
    def test_add_msg_when_cancelling_running_job_successful(self):
        with session.begin():
            job_owner = data_setup.create_user(password=u'owner')
            job = data_setup.create_running_job(owner=job_owner)

        run_client(['bkr', 'job-cancel', job.t_id,
                    '--username', job_owner.user_name,
                    '--password', 'owner',
                    '--msg=test adding cancel message'])
        with session.begin():
            session.refresh(job)
            self.assertEquals(job.activity[0].action, u'Cancelled')
            self.assertEquals(job.recipesets[0].recipes[0].tasks[0].results[0].log,
                    'test adding cancel message')
コード例 #15
0
 def test_duplicate_logs_are_filtered_out(self):
     # Even if the db contains multiple rows referencing the same filename 
     # (which it shouldn't) we want recipe.files() to filter those out 
     # before returning them, to avoid breaking beaker-transfer.
     with session.begin():
         job = data_setup.create_running_job()
         recipe = job.recipesets[0].recipes[0]
         recipe.logs.extend([
             LogRecipe(path=u'/', filename=u'imadupe.log'),
             LogRecipe(path=u'/', filename=u'imadupe.log'),
         ])
     logs = self.server.recipes.files(recipe.id)
     self.assertEqual(len(logs), 1)
     self.assertEqual(logs[0]['filename'], u'imadupe.log')
コード例 #16
0
 def test_filter_finished_jobs(self):
     with session.begin():
         completed_job = data_setup.create_completed_job(
             task_status=TaskStatus.completed)
         cancelled_job = data_setup.create_completed_job(
             task_status=TaskStatus.cancelled)
         aborted_job = data_setup.create_completed_job(
             task_status=TaskStatus.aborted)
         running_job = data_setup.create_running_job()
     out = run_client(['bkr', 'job-list', '--finished'])
     self.assertIn(completed_job.t_id, out)
     self.assertIn(cancelled_job.t_id, out)
     self.assertIn(aborted_job.t_id, out)
     self.assertNotIn(running_job.t_id, out)
コード例 #17
0
ファイル: test_recipes_xmlrpc.py プロジェクト: joyxu/beaker
 def test_duplicate_logs_are_filtered_out(self):
     # Even if the db contains multiple rows referencing the same filename
     # (which it shouldn't) we want recipe.files() to filter those out
     # before returning them, to avoid breaking beaker-transfer.
     with session.begin():
         job = data_setup.create_running_job()
         recipe = job.recipesets[0].recipes[0]
         recipe.logs.extend([
             LogRecipe(path=u'/', filename=u'imadupe.log'),
             LogRecipe(path=u'/', filename=u'imadupe.log'),
         ])
     logs = self.server.recipes.files(recipe.id)
     self.assertEqual(len(logs), 1)
     self.assertEqual(logs[0]['filename'], u'imadupe.log')
コード例 #18
0
 def test_filter_unfinished_jobs(self):
     with session.begin():
         queued_job = data_setup.create_queued_job()
         running_job = data_setup.create_running_job()
         waiting_job = data_setup.create_waiting_job()
         scheduled_job = data_setup.create_scheduled_job()
         installing_job = data_setup.create_installing_job()
         completed_job = data_setup.create_completed_job()
     out = run_client(['bkr', 'job-list', '--unfinished'])
     self.assertIn(queued_job.t_id, out)
     self.assertIn(running_job.t_id, out)
     self.assertIn(waiting_job.t_id, out)
     self.assertIn(scheduled_job.t_id, out)
     self.assertIn(installing_job.t_id, out)
     self.assertNotIn(completed_job.t_id, out)
コード例 #19
0
 def test_filter_unfinished_jobs(self):
     with session.begin():
         queued_job = data_setup.create_queued_job()
         running_job = data_setup.create_running_job()
         waiting_job = data_setup.create_waiting_job()
         scheduled_job = data_setup.create_scheduled_job()
         installing_job = data_setup.create_installing_job()
         completed_job = data_setup.create_completed_job()
     out = run_client(['bkr', 'job-list', '--unfinished'])
     self.assertIn(queued_job.t_id, out)
     self.assertIn(running_job.t_id, out)
     self.assertIn(waiting_job.t_id, out)
     self.assertIn(scheduled_job.t_id, out)
     self.assertIn(installing_job.t_id, out)
     self.assertNotIn(completed_job.t_id, out)
コード例 #20
0
ファイル: test_mail.py プロジェクト: beaker-project/beaker
    def test_system_reserved_notification_off(self):
        with session.begin():
            owner = data_setup.create_user(email_address=u'*****@*****.**',
                                           notify_reservesys=False)
            system = data_setup.create_system(fqdn=u'funcooker.ge.valid',
                    lab_controller=data_setup.create_labcontroller())
            distro_tree = data_setup.create_distro_tree(arch=u'x86_64')
            job = data_setup.create_running_job(owner=owner, system=system,
                    distro_tree=distro_tree,
                    whiteboard=u'This is a whiteboard',
                    recipe_whiteboard=u'This is another whiteboard')

        mail_capture_thread.start_capturing()
        with session.begin():
            bkr.server.mail.reservesys_notify(job.recipesets[0].recipes[0])
        captured_mails = mail_capture_thread.stop_capturing(wait=False)
        self.assertEqual(len(captured_mails), 0)
コード例 #21
0
ファイル: test_mail.py プロジェクト: walbon/beaker
    def test_system_reserved_notification_off(self):
        with session.begin():
            owner = data_setup.create_user(email_address=u'*****@*****.**',
                                           notify_reservesys=False)
            system = data_setup.create_system(fqdn=u'funcooker.ge.valid',
                    lab_controller=data_setup.create_labcontroller())
            distro_tree = data_setup.create_distro_tree(arch=u'x86_64')
            job = data_setup.create_running_job(owner=owner, system=system,
                    distro_tree=distro_tree,
                    whiteboard=u'This is a whiteboard',
                    recipe_whiteboard=u'This is another whiteboard')

        mail_capture_thread.start_capturing()
        with session.begin():
            bkr.server.mail.reservesys_notify(job.recipesets[0].recipes[0])
        captured_mails = mail_capture_thread.stop_capturing(wait=False)
        self.assertEqual(len(captured_mails), 0)
コード例 #22
0
 def test_lab_controller_remove(self):
     b = self.browser
     with session.begin():
         lc = data_setup.create_labcontroller()
         # When an LC is removed, we de-associate all systems, cancel all
         # running recipes, and remove all distro tree associations. So this
         # test creates a system, job, and distro tree in the lab to cover
         # all those cases.
         sys = data_setup.create_system(lab_controller=lc)
         job = data_setup.create_running_job(lab_controller=lc)
         distro_tree = data_setup.create_distro_tree(lab_controllers=[lc])
     b.get(get_server_base() + 'labcontrollers')
     b.find_element_by_xpath(
         "//table[@id='widget']/tbody/tr/"
         "td[preceding-sibling::td/a[normalize-space(text())='%s']]"
         "/a[normalize-space(text())='Remove']" % lc.fqdn).click()
     # confirm deletion
     b.find_element_by_xpath(
         '//button[@type="button" and .//text()="Delete"]').click()
     self.assert_('%s removed' %
                  lc.fqdn in b.find_element_by_css_selector('.flash').text)
     with session.begin():
         session.expire_all()
         # check lc activity
         self.assertEquals(lc.activity[0].field_name, u'Removed')
         self.assertEquals(lc.activity[0].action, u'Changed')
         self.assertEquals(lc.activity[0].new_value, u'True')
         self.assertEquals(lc.activity[1].field_name, u'Disabled')
         self.assertEquals(lc.activity[1].action, u'Changed')
         self.assertEquals(lc.activity[1].new_value, u'True')
         # check system activity
         self.assertEquals(sys.activity[0].field_name, u'lab_controller')
         self.assertEquals(sys.activity[0].action, u'Changed')
         self.assertEquals(sys.activity[0].new_value, None)
         # check job status
         job.update_status()
         self.assertEquals(job.status, TaskStatus.cancelled)
         # check distro tree activity
         self.assertEquals(distro_tree.activity[0].field_name,
                           u'lab_controller_assocs')
         self.assertEquals(distro_tree.activity[0].action, u'Removed')
コード例 #23
0
 def test_lab_controller_remove(self):
     b = self.browser
     with session.begin():
         lc = data_setup.create_labcontroller()
         # When an LC is removed, we de-associate all systems, cancel all
         # running recipes, and remove all distro tree associations. So this
         # test creates a system, job, and distro tree in the lab to cover
         # all those cases.
         sys = data_setup.create_system(lab_controller=lc)
         job = data_setup.create_running_job(lab_controller=lc)
         distro_tree = data_setup.create_distro_tree(lab_controllers=[lc])
     b.get(get_server_base() + "labcontrollers")
     b.find_element_by_xpath(
         "//table[@id='widget']/tbody/tr/"
         "td[preceding-sibling::td/a[normalize-space(text())='%s']]"
         "/a[normalize-space(text())='Remove']" % lc.fqdn
     ).click()
     # confirm deletion
     b.find_element_by_xpath('//button[@type="button" and .//text()="Delete"]').click()
     self.assert_("%s removed" % lc.fqdn in b.find_element_by_css_selector(".flash").text)
     with session.begin():
         session.expire_all()
         # check lc activity
         self.assertEquals(lc.activity[0].field_name, u"Removed")
         self.assertEquals(lc.activity[0].action, u"Changed")
         self.assertEquals(lc.activity[0].new_value, u"True")
         self.assertEquals(lc.activity[1].field_name, u"Disabled")
         self.assertEquals(lc.activity[1].action, u"Changed")
         self.assertEquals(lc.activity[1].new_value, u"True")
         # check system activity
         self.assertEquals(sys.activity[0].field_name, u"lab_controller")
         self.assertEquals(sys.activity[0].action, u"Changed")
         self.assertEquals(sys.activity[0].new_value, None)
         # check job status
         job.update_status()
         self.assertEquals(job.status, TaskStatus.cancelled)
         # check distro tree activity
         self.assertEquals(distro_tree.activity[0].field_name, u"lab_controller_assocs")
         self.assertEquals(distro_tree.activity[0].action, u"Removed")