コード例 #1
0
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
     session.flush()
コード例 #2
0
ファイル: test_deadrecipes.py プロジェクト: ShaolongHu/beaker
    def setUp(cls):
        # Create two unique labs
        lab1 = data_setup.create_labcontroller(fqdn=u'lab_%d' %
                                               int(time.time() * 1000))
        lab2 = data_setup.create_labcontroller(fqdn=u'lab_%d' %
                                               int(time.time() * 1000))

        # Create two distros and only put one in each lab.
        cls.distro_tree1 = data_setup.create_distro_tree()
        cls.distro_tree2 = data_setup.create_distro_tree()
        session.flush()
        cls.distro_tree1.lab_controller_assocs = [
            LabControllerDistroTree(lab_controller=lab2,
                                    url=u'http://notimportant')
        ]
        cls.distro_tree2.lab_controller_assocs = [
            LabControllerDistroTree(lab_controller=lab1,
                                    url=u'http://notimportant')
        ]

        # Create a user
        user = data_setup.create_user()

        # Create two systems but only put them in lab1.
        system1 = data_setup.create_system(owner=user)
        system2 = data_setup.create_system(owner=user)
        system1.lab_controller = lab1
        system2.lab_controller = lab1

        session.flush()

        # Create two jobs, one requiring distro_tree1 and one requiring distro_tree2
        job = '''
            <job>
                <whiteboard>%s</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="%s" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
                 '''
        xmljob1 = XmlJob(
            xmltramp.parse(
                job %
                (cls.distro_tree1.distro.name, cls.distro_tree1.distro.name)))
        xmljob2 = XmlJob(
            xmltramp.parse(
                job %
                (cls.distro_tree2.distro.name, cls.distro_tree2.distro.name)))

        cls.job1 = Jobs().process_xmljob(xmljob1, user)
        cls.job2 = Jobs().process_xmljob(xmljob2, user)
コード例 #3
0
ファイル: test_jobs.py プロジェクト: pombredanne/beaker-1
 def setUp(self):
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     group = data_setup.create_group(group_name='somegroup')
     self.user.groups.append(group)
     testutil.set_identity_user(self.user)
     data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
     data_setup.create_product(product_name=u'the_product')
コード例 #4
0
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user(
         user_name=u'test-job-owner',
         email_address=u'*****@*****.**')
     group = data_setup.create_group(group_name='somegroup')
     group.add_member(self.user)
     testutil.set_identity_user(self.user)
     data_setup.create_product(product_name=u'the_product')
     session.flush()
コード例 #5
0
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
     session.flush()
コード例 #6
0
ファイル: test_jobs.py プロジェクト: ShaolongHu/beaker
 def setUp(self):
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     group = data_setup.create_group(group_name='somegroup')
     self.user.groups.append(group)
     testutil.set_identity_user(self.user)
     data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
     data_setup.create_product(product_name=u'the_product')
コード例 #7
0
ファイル: test_jobs.py プロジェクト: beaker-project/beaker
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user(user_name=u'test-job-owner',
             email_address=u'*****@*****.**')
     group = data_setup.create_group(group_name='somegroup')
     group.add_member(self.user)
     testutil.set_identity_user(self.user)
     data_setup.create_product(product_name=u'the_product')
     session.flush()
コード例 #8
0
ファイル: test_jobs.py プロジェクト: sibiaoluo/beaker
    def setUp(self):
        from bkr.server.jobs import Jobs

        self.controller = Jobs()
        self.user = data_setup.create_user()
        group = data_setup.create_group(group_name="somegroup")
        self.user.groups.append(group)
        testutil.set_identity_user(self.user)
        if not Distro.by_name(u"BlueShoeLinux5-5"):
            data_setup.create_distro_tree(distro_name=u"BlueShoeLinux5-5")
        data_setup.create_product(product_name=u"the_product")
コード例 #9
0
ファイル: test_tasks.py プロジェクト: ustbgaofan/beaker
class TestTasks(unittest.TestCase):

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.task = data_setup.create_task(name=u'/fake/task/here')
        distro_tree = data_setup.create_distro_tree()
        self.user = data_setup.create_user()
        self.xmljob = XmlJob(xmltramp.parse('''
            <job>
                <whiteboard>job with fake task</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="%s" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="%s" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''' % (distro_tree.distro.name, self.task.name)))
        session.flush()

    def tearDown(self):
        session.rollback()
        session.close()

    def test_enable_task(self):
        self.task.valid=True
        session.flush()
        self.controller.process_xmljob(self.xmljob, self.user)
        
    def test_disable_task(self):
        self.task.valid=False
        session.flush()
        self.assertRaises(BX, lambda: self.controller.process_xmljob(self.xmljob, self.user))
コード例 #10
0
ファイル: test_tasks.py プロジェクト: pombredanne/beaker-2
class TestTasks(DatabaseTestCase):
    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.task = data_setup.create_task(name=u'/fake/task/here')
        distro_tree = data_setup.create_distro_tree()
        self.user = data_setup.create_user()
        self.xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with fake task</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="%s" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="%s" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''' % (distro_tree.distro.name, self.task.name))
        session.flush()

    def tearDown(self):
        session.rollback()

    def test_enable_task(self):
        self.task.valid = True
        session.flush()
        self.controller.process_xmljob(self.xmljob, self.user)

    def test_disable_task(self):
        self.task.valid = False
        session.flush()
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(self.xmljob, self.user))
コード例 #11
0
ファイル: test_tasks.py プロジェクト: pombredanne/beaker-2
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.task = data_setup.create_task(name=u'/fake/task/here')
     distro_tree = data_setup.create_distro_tree()
     self.user = data_setup.create_user()
     self.xmljob = lxml.etree.fromstring('''
         <job>
             <whiteboard>job with fake task</whiteboard>
             <recipeSet>
                 <recipe>
                     <distroRequires>
                         <distro_name op="=" value="%s" />
                     </distroRequires>
                     <hostRequires/>
                     <task name="%s" role="STANDALONE">
                         <params/>
                     </task>
                 </recipe>
             </recipeSet>
         </job>
         ''' % (distro_tree.distro.name, self.task.name))
     session.flush()
コード例 #12
0
ファイル: test_tasks.py プロジェクト: ustbgaofan/beaker
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.task = data_setup.create_task(name=u'/fake/task/here')
     distro_tree = data_setup.create_distro_tree()
     self.user = data_setup.create_user()
     self.xmljob = XmlJob(xmltramp.parse('''
         <job>
             <whiteboard>job with fake task</whiteboard>
             <recipeSet>
                 <recipe>
                     <distroRequires>
                         <distro_name op="=" value="%s" />
                     </distroRequires>
                     <hostRequires/>
                     <task name="%s" role="STANDALONE">
                         <params/>
                     </task>
                 </recipe>
             </recipeSet>
         </job>
         ''' % (distro_tree.distro.name, self.task.name)))
     session.flush()
コード例 #13
0
ファイル: test_jobs.py プロジェクト: qhsong/beaker
class TestJobsController(DatabaseTestCase):

    maxDiff = None

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user(
            user_name=u'test-job-owner',
            email_address=u'*****@*****.**')
        group = data_setup.create_group(group_name='somegroup')
        group.add_member(self.user)
        testutil.set_identity_user(self.user)
        data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        data_setup.create_product(product_name=u'the_product')
        session.flush()

    def tearDown(self):
        testutil.set_identity_user(None)
        session.rollback()

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with invalid hostRequires</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires>
                            <memory op=">=" value="500MB" />
                        </hostRequires>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True),
                                               pretty_print=True,
                                               encoding='utf8')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)

    def test_complete_job_results(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        session.flush()

        # Complete the job, filling in values to match what's hardcoded in
        # complete-job-results.xml...
        recipe = job.recipesets[0].recipes[0]
        guestrecipe = recipe.guests[0]
        data_setup.mark_recipe_running(
            recipe,
            fqdn=u'system.test-complete-job-results',
            start_time=datetime.datetime(2016, 1, 31, 23, 0, 0),
            install_started=datetime.datetime(2016, 1, 31, 23, 0, 1),
            install_finished=datetime.datetime(2016, 1, 31, 23, 0, 2),
            postinstall_finished=datetime.datetime(2016, 1, 31, 23, 0, 3),
            task_start_time=datetime.datetime(2016, 1, 31, 23, 0, 4))
        data_setup.mark_recipe_complete(
            guestrecipe,
            fqdn=u'guest.test-complete-job-results',
            mac_address='ff:ff:ff:00:00:00',
            start_time=datetime.datetime(2016, 1, 31, 23, 30, 0),
            install_started=datetime.datetime(2016, 1, 31, 23, 30, 1),
            install_finished=datetime.datetime(2016, 1, 31, 23, 30, 2),
            postinstall_finished=datetime.datetime(2016, 1, 31, 23, 30, 3),
            finish_time=datetime.datetime(2016, 1, 31, 23, 30, 4))
        data_setup.mark_recipe_complete(
            recipe,
            only=True,
            start_time=datetime.datetime(2016, 1, 31, 23, 0, 4),
            finish_time=datetime.datetime(2016, 1, 31, 23, 59, 0))
        recipe.installation.rendered_kickstart.url = u'http://example.com/recipe.ks'
        guestrecipe.installation.rendered_kickstart.url = u'http://example.com/guest.ks'
        session.flush()
        # Hack up the database ids... This will fail if it's flushed, but it's
        # the easiest way to make them match the expected values.
        job.id = 1
        job.recipesets[0].id = 1
        recipe.id = 1
        guestrecipe.id = 2
        recipe.tasks[0].id = 1
        recipe.tasks[1].id = 2
        guestrecipe.tasks[0].id = 3
        guestrecipe.tasks[0].results[0].id = 1
        recipe.tasks[0].results[0].id = 2
        recipe.tasks[1].results[0].id = 3

        expected_results_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job-results.xml')
        actual_results_xml = lxml.etree.tostring(job.to_xml(clone=False),
                                                 pretty_print=True,
                                                 encoding='utf8')
        self.assertMultiLineEqual(expected_results_xml, actual_results_xml)

    def test_does_not_fail_when_whiteboard_empty(self):
        xml = """
            <job>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5"/>
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </recipe>
                </recipeSet>
            </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'')

    def test_creates_taskpackages_successfully(self):
        # Note: installPackage is deprecated but we still provide backwards compatibility
        xml = """
        <job>
            <recipeSet>
                <recipe>
                    <distroRequires>
                        <distro_name op="=" value="BlueShoeLinux5-5"/>
                    </distroRequires>
                    <hostRequires/>
                    <installPackage>libbeer</installPackage>
                    <task name="/distribution/install" role="STANDALONE"/>
                </recipe>
            </recipeSet>
        </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertListEqual(
            ['libbeer'],
            [x.package for x in job.recipesets[0].recipes[0].custom_packages])

    def test_upload_xml_catches_invalid_xml(self):
        """We want that invalid Job XML is caught in the validation step."""
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with arbitrary XML in namespaces</whriteboard>
                <recipeSet>
                    <rawcipe>
                        <distroRequires>
                            <distro_name />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </rawcipe>
                </recipeSet>
            </job>
        ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1112131
    def test_preserves_arbitrary_XML_elements_in_namespace(self):
        complete_job_xml = pkg_resources.resource_filename(
            'bkr.inttest', 'complete-job.xml')
        with open(complete_job_xml, 'r') as f:
            contents = f.read()
            xmljob = lxml.etree.fromstring(contents)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        tree = job.to_xml(clone=True)
        self.assertEqual(2, len(tree.xpath('*[namespace-uri()]')))
        self.assertEqual(
            '<b:option xmlns:b="http://example.com/bar">--foobar arbitrary</b:option>',
            lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[0],
                                encoding='utf8'))
        self.assertEqual(
            u'<f:test xmlns:f="http://example.com/foo">unicode text: heißer Шис</f:test>'
            .encode('utf8'),
            lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[1],
                                encoding='utf8'))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(
            job.recipesets[0].recipes[0].reservation_request.duration, 86400)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys_with_duration(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys with duration</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys duration="600"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(
            job.recipesets[0].recipes[0].reservation_request.duration, 600)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1302857
    def test_strips_whitespace_from_whiteboard(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'so pretty')
コード例 #14
0
class TestUpdateStatus(DatabaseTestCase):

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        session.flush()

    def tearDown(self):
        session.commit()

    def test_abort_recipe_bubbles_status_to_job(self):
        xmljob = XmlJob(xmltramp.parse('''
            <job>
                <whiteboard>job </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            '''))
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()
        for recipeset in job.recipesets:
            for recipe in recipeset.recipes:
                recipe.process()
                recipe.queue()
                recipe.schedule()
                recipe.waiting()

        # Abort the first recipe.
        job.recipesets[0].recipes[0].abort()
        job.update_status()

        # Verify that it and its children are aborted.
        self.assertEquals(job.recipesets[0].recipes[0].status, TaskStatus.aborted)
        for task in job.recipesets[0].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.aborted)

        # Verify that the second recipe and its children are still waiting.
        self.assertEquals(job.recipesets[1].recipes[0].status, TaskStatus.waiting)
        for task in job.recipesets[1].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.waiting)

        # Verify that the job still shows waiting.
        self.assertEquals(job.status, TaskStatus.waiting)

        # Abort the second recipe now.
        job.recipesets[1].recipes[0].abort()
        job.update_status()

        # Verify that the whole job shows aborted now.
        self.assertEquals(job.status, TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=903935
    def test_finished_recipe_with_unstarted_guests(self):
        # host completes, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        data_setup.mark_recipe_running(job.recipesets[0].recipes[0], only=True)
        job.recipesets[0].recipes[0].tasks[-1].stop()
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                TaskStatus.completed)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                TaskStatus.aborted)

        # host aborts, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        job.recipesets[0].recipes[0].abort(msg='blorf')
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                TaskStatus.aborted)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                TaskStatus.aborted)

    def test_update_status_can_be_roundtripped_35508(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'job_35508.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))

        data_setup.create_tasks(xmljob)
        session.flush()
        
        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist 
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()
        
        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.wrappedEl('status')), job.status)
        self.assertEquals(TaskResult.from_string(xmljob.wrappedEl('result')), job.result)
        for i, recipeset in enumerate(xmljob.iter_recipeSets()):
            for j, recipe in enumerate(recipeset.iter_recipes()):
                self.assertEquals(TaskStatus.from_string(recipe.wrappedEl('status')),
                        job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.wrappedEl('result')),
                        job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.iter_tasks()):
                    self.assertEquals(TaskStatus.from_string(task.status),
                            job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(TaskResult.from_string(task.result),
                            job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)

    def test_update_status_can_be_roundtripped_40214(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'job_40214.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))

        data_setup.create_tasks(xmljob)
        session.flush()
        
        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist 
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()
        
        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.wrappedEl('status')), job.status)
        self.assertEquals(TaskResult.from_string(xmljob.wrappedEl('result')), job.result)
        for i, recipeset in enumerate(xmljob.iter_recipeSets()):
            for j, recipe in enumerate(recipeset.iter_recipes()):
                self.assertEquals(TaskStatus.from_string(recipe.wrappedEl('status')),
                        job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.wrappedEl('result')),
                        job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.iter_tasks()):
                    self.assertEquals(TaskStatus.from_string(task.status),
                            job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(TaskResult.from_string(task.result),
                            job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)
コード例 #15
0
class TestJobsController(DatabaseTestCase):

    maxDiff = None

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user(
            user_name=u'test-job-owner',
            email_address=u'*****@*****.**')
        group = data_setup.create_group(group_name='somegroup')
        group.add_member(self.user)
        testutil.set_identity_user(self.user)
        data_setup.create_product(product_name=u'the_product')
        session.flush()

    def tearDown(self):
        testutil.set_identity_user(None)
        session.rollback()

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with invalid hostRequires</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires>
                            <memory op=">=" value="500MB" />
                        </hostRequires>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True),
                                               pretty_print=True,
                                               encoding='utf8')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)

    # https://bugzilla.redhat.com/show_bug.cgi?id=911515
    def test_job_with_custom_distro_without_optional_attributes_can_be_roundtripped(
            self):
        complete_job_xml = '''
                    <job>
                        <whiteboard>
                            so pretty
                        </whiteboard>
                        <recipeSet>
                            <recipe>
                                <distro>
                                    <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                                    <initrd url="pxeboot/initrd"/>
                                    <kernel url="pxeboot/vmlinuz"/>
                                    <arch value="i386"/>
                                    <osversion major="RedHatEnterpriseLinux7"/>
                                </distro>
                                <hostRequires/>
                                <task name="/distribution/install"/>
                            </recipe>
                        </recipeSet>
                    </job>
                '''
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True),
                                               pretty_print=True,
                                               encoding='utf8')
        self.assertIn(
            '<tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>',
            roundtripped_xml)
        self.assertIn('<initrd url="pxeboot/initrd"/>', roundtripped_xml)
        self.assertIn('<kernel url="pxeboot/vmlinuz"/>', roundtripped_xml)
        self.assertIn('<arch value="i386"/>', roundtripped_xml)
        self.assertIn('<osversion major="RedHatEnterpriseLinux7" minor="0"/>',
                      roundtripped_xml)

    def test_complete_job_results(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        session.flush()

        # Complete the job, filling in values to match what's hardcoded in
        # complete-job-results.xml...
        recipe = job.recipesets[0].recipes[0]
        guestrecipe = recipe.guests[0]
        data_setup.mark_recipe_running(
            recipe,
            fqdn=u'system.test-complete-job-results',
            start_time=datetime.datetime(2016, 1, 31, 23, 0, 0),
            install_started=datetime.datetime(2016, 1, 31, 23, 0, 1),
            install_finished=datetime.datetime(2016, 1, 31, 23, 0, 2),
            postinstall_finished=datetime.datetime(2016, 1, 31, 23, 0, 3),
            task_start_time=datetime.datetime(2016, 1, 31, 23, 0, 4))
        data_setup.mark_recipe_complete(
            guestrecipe,
            fqdn=u'guest.test-complete-job-results',
            mac_address='ff:ff:ff:00:00:00',
            start_time=datetime.datetime(2016, 1, 31, 23, 30, 0),
            install_started=datetime.datetime(2016, 1, 31, 23, 30, 1),
            install_finished=datetime.datetime(2016, 1, 31, 23, 30, 2),
            postinstall_finished=datetime.datetime(2016, 1, 31, 23, 30, 3),
            finish_time=datetime.datetime(2016, 1, 31, 23, 30, 4))
        data_setup.mark_recipe_complete(
            recipe,
            only=True,
            start_time=datetime.datetime(2016, 1, 31, 23, 0, 4),
            finish_time=datetime.datetime(2016, 1, 31, 23, 59, 0))
        recipe.installation.rendered_kickstart.url = u'http://example.com/recipe.ks'
        guestrecipe.installation.rendered_kickstart.url = u'http://example.com/guest.ks'
        session.flush()
        # Hack up the database ids... This will fail if it's flushed, but it's
        # the easiest way to make them match the expected values.
        job.id = 1
        job.recipesets[0].id = 1
        recipe.id = 1
        guestrecipe.id = 2
        recipe.tasks[0].id = 1
        recipe.tasks[1].id = 2
        guestrecipe.tasks[0].id = 3
        guestrecipe.tasks[0].results[0].id = 1
        recipe.tasks[0].results[0].id = 2
        recipe.tasks[1].results[0].id = 3

        expected_results_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job-results.xml')
        expected_results_xml = expected_results_xml.replace(
            '${BEAKER_SERVER_BASE_URL}', get_server_base())
        actual_results_xml = lxml.etree.tostring(job.to_xml(clone=False),
                                                 pretty_print=True,
                                                 encoding='utf8')
        self.assertMultiLineEqual(expected_results_xml, actual_results_xml)

    def test_does_not_fail_when_whiteboard_empty(self):
        xml = """
            <job>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5"/>
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </recipe>
                </recipeSet>
            </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'')

    def test_creates_taskpackages_successfully(self):
        # Note: installPackage is deprecated but we still provide backwards compatibility
        xml = """
        <job>
            <recipeSet>
                <recipe>
                    <distroRequires>
                        <distro_name op="=" value="BlueShoeLinux5-5"/>
                    </distroRequires>
                    <hostRequires/>
                    <installPackage>libbeer</installPackage>
                    <task name="/distribution/install" role="STANDALONE"/>
                </recipe>
            </recipeSet>
        </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertListEqual(
            ['libbeer'],
            [x.package for x in job.recipesets[0].recipes[0].custom_packages])

    def test_upload_xml_catches_invalid_xml(self):
        """We want that invalid Job XML is caught in the validation step."""
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with arbitrary XML in namespaces</whriteboard>
                <recipeSet>
                    <rawcipe>
                        <distroRequires>
                            <distro_name />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </rawcipe>
                </recipeSet>
            </job>
        ''')
        self.assertRaises(
            BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1112131
    def test_preserves_arbitrary_XML_elements_in_namespace(self):
        complete_job_xml = pkg_resources.resource_filename(
            'bkr.inttest', 'complete-job.xml')
        with open(complete_job_xml, 'r') as f:
            contents = f.read()
            xmljob = lxml.etree.fromstring(contents)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        tree = job.to_xml(clone=True)
        self.assertEqual(2, len(tree.xpath('*[namespace-uri()]')))
        self.assertEqual(
            '<b:option xmlns:b="http://example.com/bar">--foobar arbitrary</b:option>',
            lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[0],
                                encoding='utf8'))
        self.assertEqual(
            u'<f:test xmlns:f="http://example.com/foo">unicode text: heißer Шис</f:test>'
            .encode('utf8'),
            lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[1],
                                encoding='utf8'))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(
            job.recipesets[0].recipes[0].reservation_request.duration, 86400)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys_with_duration(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys with duration</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys duration="600"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(
            job.recipesets[0].recipes[0].reservation_request.duration, 600)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1302857
    def test_strips_whitespace_from_whiteboard(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'so pretty')

    # https://bugzilla.redhat.com/show_bug.cgi?id=911515
    def test_distro_metadata_stored_at_job_submission_time_for_user_defined_distro(
            self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="i386"/>
                            <osversion major="RedHatEnterpriseLinux7" minor="4"/>
                            <name value="MyCustomLinux1.0"/>
                            <variant value="Server"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertEqual(
            recipe.installation.tree_url,
            "ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"
        )
        self.assertEqual(recipe.installation.initrd_path, "pxeboot/initrd")
        self.assertEqual(recipe.installation.kernel_path, "pxeboot/vmlinuz")
        self.assertEqual(recipe.installation.arch.arch, "i386")
        self.assertEqual(recipe.installation.distro_name, "MyCustomLinux1.0")
        self.assertEqual(recipe.installation.osmajor, "RedHatEnterpriseLinux7")
        self.assertEqual(recipe.installation.osminor, "4")
        self.assertEqual(recipe.installation.variant, "Server")
        self.assertEqual(recipe.distro_requires, None)

    def test_distro_metadata_stored_at_job_submission_time_for_traditional_distro(
            self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertIsNone(recipe.installation.tree_url)
        self.assertIsNone(recipe.installation.initrd_path)
        self.assertIsNone(recipe.installation.kernel_path)
        self.assertEqual(recipe.installation.arch.arch, u'i386')
        self.assertEqual(recipe.installation.distro_name, u'BlueShoeLinux5-5')
        self.assertEqual(recipe.installation.osmajor, u'BlueShoeLinux5')
        self.assertEqual(recipe.installation.osminor, u'9')
        self.assertEqual(recipe.installation.variant, u'Server')
        self.assertNotEqual(recipe.distro_requires, None)

    def test_unknown_arch_in_user_defined_distro_throws_exception(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="idontexist"/>
                            <osversion major="RedHatEnterpriseLinux7" minor="4"/>
                            <name value="MyCustomLinux1.0"/>
                            <variant value="Server"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, 'No arch matches'):
            self.controller.process_xmljob(jobxml, self.user)

    def test_osminor_defaults_to_zero_when_not_provided_in_distro_metadata(
            self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="i386"/>
                            <osversion major="RedHatEnterpriseLinux7"/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertEqual(recipe.installation.osminor, "0")

    def test_required_attributes_throw_exception_when_empty(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="some/random/tree"/>
                            <kernel url="some/kernel"/>
                            <arch value="i386"/>
                            <osversion major=""/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<initrd/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="some/random/tree"/>
                            <initrd url="some/random/initrd"/>
                            <arch value="i386"/>
                            <osversion major=""/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<kernel/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="/some/random/tree"/>
                            <initrd url="/some/random/initrd"/>
                            <kernel url="/some/random/kernel"/>
                            <arch value="i386"/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<osmajor/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
コード例 #16
0
class TestUpdateStatus(DatabaseTestCase):

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        session.flush()

    def tearDown(self):
        session.commit()

    def test_abort_recipe_bubbles_status_to_job(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/check-install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/check-install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()
        for recipeset in job.recipesets:
            for recipe in recipeset.recipes:
                recipe.process()
                recipe.queue()
                recipe.schedule()
                recipe.waiting()

        # Abort the first recipe.
        job.recipesets[0].recipes[0].abort()
        job.update_status()

        # Verify that it and its children are aborted.
        self.assertEquals(job.recipesets[0].recipes[0].status, TaskStatus.aborted)
        for task in job.recipesets[0].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.aborted)

        # Verify that the second recipe and its children are still waiting.
        self.assertEquals(job.recipesets[1].recipes[0].status, TaskStatus.waiting)
        for task in job.recipesets[1].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.waiting)

        # Verify that the job still shows waiting.
        self.assertEquals(job.status, TaskStatus.waiting)

        # Abort the second recipe now.
        job.recipesets[1].recipes[0].abort()
        job.update_status()

        # Verify that the whole job shows aborted now.
        self.assertEquals(job.status, TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=903935
    def test_finished_recipe_with_unstarted_guests(self):
        # host completes, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        data_setup.mark_recipe_running(job.recipesets[0].recipes[0], only=True)
        job.recipesets[0].recipes[0].tasks[-1].stop()
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                TaskStatus.completed)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                TaskStatus.aborted)

        # host aborts, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        job.recipesets[0].recipes[0].abort(msg='blorf')
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                TaskStatus.aborted)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1309530
    def test_recipe_start_time_is_set_to_rebooted_timestamp(self):
        # For a normal recipe running on a system, update_status should set 
        # recipe.start_time to the rebooted timestamp.
        job = data_setup.create_job()
        data_setup.mark_job_scheduled(job)
        recipe = job.recipesets[0].recipes[0]
        self.assertIsNone(recipe.start_time)
        recipe.provision()
        recipe.installation.rebooted = datetime.datetime(2016, 2, 18, 13, 0, 0)
        job.update_status()
        self.assertEqual(recipe.start_time, datetime.datetime(2016, 2, 18, 13, 0, 0))
        self.assertEqual(recipe.status, TaskStatus.installing)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1309530
    def test_recipe_start_time_is_set_to_first_task_start_time(self):
        # For guest recipes, and systems without power control, there is no 
        # rebooted timestamp. Instead the first task just gets started.
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_scheduled(job)
        guestrecipe = job.recipesets[0].recipes[0].guests[0]
        self.assertIsNone(guestrecipe.start_time)
        # /distribution/virt/install starts the first task before it starts 
        # running the guest installation.
        guestrecipe.provision()
        guestrecipe.first_task.start()
        guestrecipe.first_task.start_time = datetime.datetime(2016, 2, 18, 14, 0, 0)
        self.assertIsNone(guestrecipe.installation.rebooted)
        job.update_status()
        self.assertEqual(guestrecipe.start_time, datetime.datetime(2016, 2, 18, 14, 0, 0))

    # https://bugzilla.redhat.com/show_bug.cgi?id=991245#c12
    def test_status_is_Waiting_when_installation_is_finished_but_tasks_have_not_started(self):
        # Beah <= 0.7.9 will consider 'Installing' to mean that the recipe is 
        # finished, so we want the status to go back to 'Waiting' once the 
        # installation is finished.
        job = data_setup.create_job()
        recipe = job.recipesets[0].recipes[0]
        data_setup.mark_recipe_installing(recipe)
        data_setup.mark_recipe_installation_finished(recipe)
        self.assertEqual(recipe.tasks[0].status, TaskStatus.waiting)
        self.assertIsNone(recipe.tasks[0].start_time)
        self.assertEqual(recipe.status, TaskStatus.waiting)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1558776
    def test_scheduler_status_is_not_reset_on_already_released_systems(self):
        first_recipe = data_setup.create_recipe()
        second_recipe = data_setup.create_recipe()
        job = data_setup.create_job_for_recipesets([
                data_setup.create_recipeset_for_recipes([first_recipe]),
                data_setup.create_recipeset_for_recipes([second_recipe])])
        data_setup.mark_recipe_complete(first_recipe)
        first_system = first_recipe.resource.system
        self.assertEquals(first_system.scheduler_status, SystemSchedulerStatus.pending)
        # Pretend the scheduler has set the system back to idle
        first_system.scheduler_status = SystemSchedulerStatus.idle

        data_setup.mark_recipe_scheduled(second_recipe)
        job.update_status()
        # The bug was that job.update_status() would reset the *first* recipe's 
        # system back to pending, even though it had already been released and 
        # could potentially be reserved for another recipe already.
        self.assertEquals(first_system.scheduler_status, SystemSchedulerStatus.idle)

    def test_update_status_can_be_roundtripped_35508(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'job_35508.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist 
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.get('status')), job.status)
        self.assertEquals(TaskResult.from_string(xmljob.get('result')), job.result)
        for i, recipeset in enumerate(xmljob.xpath('recipeSet')):
            for j, recipe in enumerate(recipeset.xpath('recipe')):
                self.assertEquals(TaskStatus.from_string(recipe.get('status')),
                                  job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.get('result')),
                                  job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.xpath('task')):
                    self.assertEquals(TaskStatus.from_string(task.get('status')),
                                      job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(TaskResult.from_string(task.get('result')),
                                      job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)

    def test_update_status_can_be_roundtripped_40214(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'job_40214.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.get('status')), job.status)
        self.assertEquals(TaskResult.from_string(xmljob.get('result')), job.result)
        for i, recipeset in enumerate(xmljob.xpath('recipeSet')):
            for j, recipe in enumerate(recipeset.xpath('recipes')):
                self.assertEquals(TaskStatus.from_string(recipe.get('status')),
                                  job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.get('result')),
                                  job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.xpath('task')):
                    self.assertEquals(TaskStatus.from_string(task.get('status')),
                                      job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(TaskResult.from_string(task.get('result')),
                                      job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)
コード例 #17
0
ファイル: test_jobs.py プロジェクト: pombredanne/beaker-1
class TestJobsController(DatabaseTestCase):

    maxDiff = None

    @with_transaction
    def setUp(self):
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        group = data_setup.create_group(group_name='somegroup')
        self.user.groups.append(group)
        testutil.set_identity_user(self.user)
        data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        data_setup.create_product(product_name=u'the_product')

    def tearDown(self):
        testutil.set_identity_user(None)

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = XmlJob(
            xmltramp.parse('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            '''))
        with session.begin():
            self.assertRaises(
                BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        session.begin()
        try:
            xmljob = XmlJob(
                xmltramp.parse('''
                <job>
                    <whiteboard>job with invalid hostRequires</whiteboard>
                    <recipeSet>
                        <recipe>
                            <distroRequires>
                                <distro_name op="=" value="BlueShoeLinux5-5" />
                            </distroRequires>
                            <hostRequires>
                                <memory op=">=" value="500MB" />
                            </hostRequires>
                            <task name="/distribution/install" role="STANDALONE">
                                <params/>
                            </task>
                        </recipe>
                    </recipeSet>
                </job>
                '''))
            self.assertRaises(
                BX, lambda: self.controller.process_xmljob(xmljob, self.user))
        finally:
            session.rollback()

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'complete-job.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))
        with session.begin():
            job = testutil.call(self.controller.process_xmljob, xmljob,
                                self.user)
        roundtripped_xml = job.to_xml(clone=True).toprettyxml(indent='    ')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)
コード例 #18
0
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     session.flush()
コード例 #19
0
class TestUpdateStatus(DatabaseTestCase):
    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        session.flush()

    def tearDown(self):
        session.commit()

    def test_abort_recipe_bubbles_status_to_job(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()
        for recipeset in job.recipesets:
            for recipe in recipeset.recipes:
                recipe.process()
                recipe.queue()
                recipe.schedule()
                recipe.waiting()

        # Abort the first recipe.
        job.recipesets[0].recipes[0].abort()
        job.update_status()

        # Verify that it and its children are aborted.
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.aborted)
        for task in job.recipesets[0].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.aborted)

        # Verify that the second recipe and its children are still waiting.
        self.assertEquals(job.recipesets[1].recipes[0].status,
                          TaskStatus.waiting)
        for task in job.recipesets[1].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.waiting)

        # Verify that the job still shows waiting.
        self.assertEquals(job.status, TaskStatus.waiting)

        # Abort the second recipe now.
        job.recipesets[1].recipes[0].abort()
        job.update_status()

        # Verify that the whole job shows aborted now.
        self.assertEquals(job.status, TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=903935
    def test_finished_recipe_with_unstarted_guests(self):
        # host completes, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        data_setup.mark_recipe_running(job.recipesets[0].recipes[0], only=True)
        job.recipesets[0].recipes[0].tasks[-1].stop()
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.completed)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                          TaskStatus.aborted)

        # host aborts, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        job.recipesets[0].recipes[0].abort(msg='blorf')
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.aborted)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                          TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1309530
    def test_recipe_start_time_is_set_to_rebooted_timestamp(self):
        # For a normal recipe running on a system, update_status should set
        # recipe.start_time to the rebooted timestamp.
        job = data_setup.create_job()
        data_setup.mark_job_scheduled(job)
        recipe = job.recipesets[0].recipes[0]
        self.assertIsNone(recipe.start_time)
        recipe.provision()
        recipe.installation.rebooted = datetime.datetime(2016, 2, 18, 13, 0, 0)
        job.update_status()
        self.assertEqual(recipe.start_time,
                         datetime.datetime(2016, 2, 18, 13, 0, 0))
        self.assertEqual(recipe.status, TaskStatus.installing)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1309530
    def test_recipe_start_time_is_set_to_first_task_start_time(self):
        # For guest recipes, and systems without power control, there is no
        # rebooted timestamp. Instead the first task just gets started.
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_scheduled(job)
        guestrecipe = job.recipesets[0].recipes[0].guests[0]
        self.assertIsNone(guestrecipe.start_time)
        # /distribution/virt/install starts the first task before it starts
        # running the guest installation.
        guestrecipe.provision()
        guestrecipe.first_task.start()
        guestrecipe.first_task.start_time = datetime.datetime(
            2016, 2, 18, 14, 0, 0)
        self.assertIsNone(guestrecipe.installation.rebooted)
        job.update_status()
        self.assertEqual(guestrecipe.start_time,
                         datetime.datetime(2016, 2, 18, 14, 0, 0))

    # https://bugzilla.redhat.com/show_bug.cgi?id=991245#c12
    def test_status_is_Waiting_when_installation_is_finished_but_tasks_have_not_started(
            self):
        # Beah <= 0.7.9 will consider 'Installing' to mean that the recipe is
        # finished, so we want the status to go back to 'Waiting' once the
        # installation is finished.
        job = data_setup.create_job()
        recipe = job.recipesets[0].recipes[0]
        data_setup.mark_recipe_installing(recipe)
        data_setup.mark_recipe_installation_finished(recipe)
        self.assertEqual(recipe.tasks[0].status, TaskStatus.waiting)
        self.assertIsNone(recipe.tasks[0].start_time)
        self.assertEqual(recipe.status, TaskStatus.waiting)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1558776
    def test_scheduler_status_is_not_reset_on_already_released_systems(self):
        first_recipe = data_setup.create_recipe()
        second_recipe = data_setup.create_recipe()
        job = data_setup.create_job_for_recipesets([
            data_setup.create_recipeset_for_recipes([first_recipe]),
            data_setup.create_recipeset_for_recipes([second_recipe])
        ])
        data_setup.mark_recipe_complete(first_recipe)
        first_system = first_recipe.resource.system
        self.assertEquals(first_system.scheduler_status,
                          SystemSchedulerStatus.pending)
        # Pretend the scheduler has set the system back to idle
        first_system.scheduler_status = SystemSchedulerStatus.idle

        data_setup.mark_recipe_scheduled(second_recipe)
        job.update_status()
        # The bug was that job.update_status() would reset the *first* recipe's
        # system back to pending, even though it had already been released and
        # could potentially be reserved for another recipe already.
        self.assertEquals(first_system.scheduler_status,
                          SystemSchedulerStatus.idle)

    def test_update_status_can_be_roundtripped_35508(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'job_35508.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.get('status')),
                          job.status)
        self.assertEquals(TaskResult.from_string(xmljob.get('result')),
                          job.result)
        for i, recipeset in enumerate(xmljob.xpath('recipeSet')):
            for j, recipe in enumerate(recipeset.xpath('recipe')):
                self.assertEquals(TaskStatus.from_string(recipe.get('status')),
                                  job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.get('result')),
                                  job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.xpath('task')):
                    self.assertEquals(
                        TaskStatus.from_string(task.get('status')),
                        job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(
                        TaskResult.from_string(task.get('result')),
                        job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)

    def test_update_status_can_be_roundtripped_40214(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'job_40214.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.get('status')),
                          job.status)
        self.assertEquals(TaskResult.from_string(xmljob.get('result')),
                          job.result)
        for i, recipeset in enumerate(xmljob.xpath('recipeSet')):
            for j, recipe in enumerate(recipeset.xpath('recipes')):
                self.assertEquals(TaskStatus.from_string(recipe.get('status')),
                                  job.recipesets[i].recipes[j].status)
                self.assertEquals(TaskResult.from_string(recipe.get('result')),
                                  job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.xpath('task')):
                    self.assertEquals(
                        TaskStatus.from_string(task.get('status')),
                        job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(
                        TaskResult.from_string(task.get('result')),
                        job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)
コード例 #20
0
 def setUp(self):
     session.begin()
     from bkr.server.jobs import Jobs
     self.controller = Jobs()
     self.user = data_setup.create_user()
     session.flush()
コード例 #21
0
class TestUpdateStatus(DatabaseTestCase):
    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        session.flush()

    def tearDown(self):
        session.commit()

    def test_abort_recipe_bubbles_status_to_job(self):
        xmljob = XmlJob(
            xmltramp.parse('''
            <job>
                <whiteboard>job </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            '''))
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()
        for recipeset in job.recipesets:
            for recipe in recipeset.recipes:
                recipe.process()
                recipe.queue()
                recipe.schedule()
                recipe.waiting()

        # Abort the first recipe.
        job.recipesets[0].recipes[0].abort()
        job.update_status()

        # Verify that it and its children are aborted.
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.aborted)
        for task in job.recipesets[0].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.aborted)

        # Verify that the second recipe and its children are still waiting.
        self.assertEquals(job.recipesets[1].recipes[0].status,
                          TaskStatus.waiting)
        for task in job.recipesets[1].recipes[0].tasks:
            self.assertEquals(task.status, TaskStatus.waiting)

        # Verify that the job still shows waiting.
        self.assertEquals(job.status, TaskStatus.waiting)

        # Abort the second recipe now.
        job.recipesets[1].recipes[0].abort()
        job.update_status()

        # Verify that the whole job shows aborted now.
        self.assertEquals(job.status, TaskStatus.aborted)

    # https://bugzilla.redhat.com/show_bug.cgi?id=903935
    def test_finished_recipe_with_unstarted_guests(self):
        # host completes, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        data_setup.mark_recipe_running(job.recipesets[0].recipes[0], only=True)
        job.recipesets[0].recipes[0].tasks[-1].stop()
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.completed)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                          TaskStatus.aborted)

        # host aborts, but guest never started
        job = data_setup.create_job(num_recipes=1, num_guestrecipes=1)
        data_setup.mark_job_waiting(job)
        job.recipesets[0].recipes[0].abort(msg='blorf')
        job.update_status()
        self.assertEquals(job.recipesets[0].recipes[0].status,
                          TaskStatus.aborted)
        self.assertEquals(job.recipesets[0].recipes[0].guests[0].status,
                          TaskStatus.aborted)

    def test_update_status_can_be_roundtripped_35508(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'job_35508.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.wrappedEl('status')),
                          job.status)
        self.assertEquals(TaskResult.from_string(xmljob.wrappedEl('result')),
                          job.result)
        for i, recipeset in enumerate(xmljob.iter_recipeSets()):
            for j, recipe in enumerate(recipeset.iter_recipes()):
                self.assertEquals(
                    TaskStatus.from_string(recipe.wrappedEl('status')),
                    job.recipesets[i].recipes[j].status)
                self.assertEquals(
                    TaskResult.from_string(recipe.wrappedEl('result')),
                    job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.iter_tasks()):
                    self.assertEquals(
                        TaskStatus.from_string(task.status),
                        job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(
                        TaskResult.from_string(task.result),
                        job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)

    def test_update_status_can_be_roundtripped_40214(self):
        complete_job_xml = pkg_resources.resource_string(
            'bkr.inttest', 'job_40214.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))

        data_setup.create_tasks(xmljob)
        session.flush()

        # Import the job xml
        job = self.controller.process_xmljob(xmljob, self.user)
        session.flush()

        # Mark job waiting
        data_setup.mark_job_waiting(job)
        session.flush()

        # watchdog's should exist
        self.assertNotEqual(len(watchdogs_for_job(job)), 0)

        # Play back the original jobs results and status
        data_setup.playback_job_results(job, xmljob)
        session.flush()

        # Verify that the original status and results match
        self.assertEquals(TaskStatus.from_string(xmljob.wrappedEl('status')),
                          job.status)
        self.assertEquals(TaskResult.from_string(xmljob.wrappedEl('result')),
                          job.result)
        for i, recipeset in enumerate(xmljob.iter_recipeSets()):
            for j, recipe in enumerate(recipeset.iter_recipes()):
                self.assertEquals(
                    TaskStatus.from_string(recipe.wrappedEl('status')),
                    job.recipesets[i].recipes[j].status)
                self.assertEquals(
                    TaskResult.from_string(recipe.wrappedEl('result')),
                    job.recipesets[i].recipes[j].result)
                for k, task in enumerate(recipe.iter_tasks()):
                    self.assertEquals(
                        TaskStatus.from_string(task.status),
                        job.recipesets[i].recipes[j].tasks[k].status)
                    self.assertEquals(
                        TaskResult.from_string(task.result),
                        job.recipesets[i].recipes[j].tasks[k].result)

        # No watchdog's should exist when the job is complete
        self.assertEquals(len(watchdogs_for_job(job)), 0)
コード例 #22
0
ファイル: test_jobs.py プロジェクト: beaker-project/beaker
class TestJobsController(DatabaseTestCase):

    maxDiff = None

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user(user_name=u'test-job-owner',
                email_address=u'*****@*****.**')
        group = data_setup.create_group(group_name='somegroup')
        group.add_member(self.user)
        testutil.set_identity_user(self.user)
        data_setup.create_product(product_name=u'the_product')
        session.flush()

    def tearDown(self):
        testutil.set_identity_user(None)
        session.rollback()

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with invalid hostRequires</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires>
                            <memory op=">=" value="500MB" />
                        </hostRequires>
                        <task name="/distribution/check-install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True), pretty_print=True, encoding='utf8')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)

    # https://bugzilla.redhat.com/show_bug.cgi?id=911515
    def test_job_with_custom_distro_without_optional_attributes_can_be_roundtripped(self):
        complete_job_xml = '''
                    <job>
                        <whiteboard>
                            so pretty
                        </whiteboard>
                        <recipeSet>
                            <recipe>
                                <distro>
                                    <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                                    <initrd url="pxeboot/initrd"/>
                                    <kernel url="pxeboot/vmlinuz"/>
                                    <arch value="i386"/>
                                    <osversion major="RedHatEnterpriseLinux7"/>
                                </distro>
                                <hostRequires/>
                                <task name="/distribution/check-install"/>
                            </recipe>
                        </recipeSet>
                    </job>
                '''
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True), pretty_print=True, encoding='utf8')
        self.assertIn('<tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>', roundtripped_xml)
        self.assertIn('<initrd url="pxeboot/initrd"/>', roundtripped_xml)
        self.assertIn('<kernel url="pxeboot/vmlinuz"/>', roundtripped_xml)
        self.assertIn('<arch value="i386"/>', roundtripped_xml)
        self.assertIn('<osversion major="RedHatEnterpriseLinux7" minor="0"/>', roundtripped_xml)

    def test_complete_job_results(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        session.flush()

        # Complete the job, filling in values to match what's hardcoded in 
        # complete-job-results.xml...
        recipe = job.recipesets[0].recipes[0]
        guestrecipe = recipe.guests[0]
        data_setup.mark_recipe_running(recipe, fqdn=u'system.test-complete-job-results',
                start_time=datetime.datetime(2016, 1, 31, 23, 0, 0),
                install_started=datetime.datetime(2016, 1, 31, 23, 0, 1),
                install_finished=datetime.datetime(2016, 1, 31, 23, 0, 2),
                postinstall_finished=datetime.datetime(2016, 1, 31, 23, 0, 3),
                task_start_time=datetime.datetime(2016, 1, 31, 23, 0, 4))
        data_setup.mark_recipe_complete(guestrecipe, fqdn=u'guest.test-complete-job-results',
                mac_address='ff:ff:ff:00:00:00',
                start_time=datetime.datetime(2016, 1, 31, 23, 30, 0),
                install_started=datetime.datetime(2016, 1, 31, 23, 30, 1),
                install_finished=datetime.datetime(2016, 1, 31, 23, 30, 2),
                postinstall_finished=datetime.datetime(2016, 1, 31, 23, 30, 3),
                finish_time=datetime.datetime(2016, 1, 31, 23, 30, 4))
        data_setup.mark_recipe_complete(recipe, only=True,
                start_time=datetime.datetime(2016, 1, 31, 23, 0, 4),
                finish_time=datetime.datetime(2016, 1, 31, 23, 59, 0))
        recipe.installation.rendered_kickstart.url = u'http://example.com/recipe.ks'
        guestrecipe.installation.rendered_kickstart.url = u'http://example.com/guest.ks'
        session.flush()
        # Hack up the database ids... This will fail if it's flushed, but it's 
        # the easiest way to make them match the expected values.
        job.id = 1
        job.recipesets[0].id = 1
        recipe.id = 1
        guestrecipe.id = 2
        recipe.tasks[0].id = 1
        recipe.tasks[1].id = 2
        guestrecipe.tasks[0].id = 3
        guestrecipe.tasks[0].results[0].id = 1
        recipe.tasks[0].results[0].id = 2
        recipe.tasks[1].results[0].id = 3

        expected_results_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job-results.xml')
        expected_results_xml = expected_results_xml.replace(
                '${BEAKER_SERVER_BASE_URL}', get_server_base())
        actual_results_xml = lxml.etree.tostring(job.to_xml(clone=False),
                pretty_print=True, encoding='utf8')
        self.assertMultiLineEqual(expected_results_xml, actual_results_xml)

    def test_does_not_fail_when_whiteboard_empty(self):
        xml = """
            <job>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5"/>
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/check-install" role="STANDALONE"/>
                    </recipe>
                </recipeSet>
            </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'')

    def test_creates_taskpackages_successfully(self):
        # Note: installPackage is deprecated but we still provide backwards compatibility
        xml = """
        <job>
            <recipeSet>
                <recipe>
                    <distroRequires>
                        <distro_name op="=" value="BlueShoeLinux5-5"/>
                    </distroRequires>
                    <hostRequires/>
                    <installPackage>libbeer</installPackage>
                    <task name="/distribution/check-install" role="STANDALONE"/>
                </recipe>
            </recipeSet>
        </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertListEqual(['libbeer'], [x.package for x in job.recipesets[0].recipes[0].custom_packages])

    def test_upload_xml_catches_invalid_xml(self):
        """We want that invalid Job XML is caught in the validation step."""
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with arbitrary XML in namespaces</whriteboard>
                <recipeSet>
                    <rawcipe>
                        <distroRequires>
                            <distro_name />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/check-install" role="STANDALONE"/>
                    </rawcipe>
                </recipeSet>
            </job>
        ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1112131
    def test_preserves_arbitrary_XML_elements_in_namespace(self):
        complete_job_xml = pkg_resources.resource_filename('bkr.inttest', 'complete-job.xml')
        with open(complete_job_xml, 'r') as f:
            contents = f.read()
            xmljob = lxml.etree.fromstring(contents)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        tree = job.to_xml(clone=True)
        self.assertEqual(2, len(tree.xpath('*[namespace-uri()]')))
        self.assertEqual('<b:option xmlns:b="http://example.com/bar">--foobar arbitrary</b:option>',
                         lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[0], encoding='utf8'))
        self.assertEqual(u'<f:test xmlns:f="http://example.com/foo">unicode text: heißer Шис</f:test>'.encode('utf8'),
                         lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[1], encoding='utf8'))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/check-install"/>
                        <reservesys/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.recipesets[0].recipes[0].reservation_request.duration, 86400)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys_with_duration(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys with duration</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/check-install"/>
                        <reservesys duration="600"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.recipesets[0].recipes[0].reservation_request.duration, 600)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1302857
    def test_strips_whitespace_from_whiteboard(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'so pretty')

    # https://bugzilla.redhat.com/show_bug.cgi?id=911515
    def test_distro_metadata_stored_at_job_submission_time_for_user_defined_distro(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="i386"/>
                            <osversion major="RedHatEnterpriseLinux7" minor="4"/>
                            <name value="MyCustomLinux1.0"/>
                            <variant value="Server"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertEqual(recipe.installation.tree_url,
                         "ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/")
        self.assertEqual(recipe.installation.initrd_path, "pxeboot/initrd")
        self.assertEqual(recipe.installation.kernel_path, "pxeboot/vmlinuz")
        self.assertEqual(recipe.installation.arch.arch, "i386")
        self.assertEqual(recipe.installation.distro_name, "MyCustomLinux1.0")
        self.assertEqual(recipe.installation.osmajor, "RedHatEnterpriseLinux7")
        self.assertEqual(recipe.installation.osminor, "4")
        self.assertEqual(recipe.installation.variant, "Server")
        self.assertEqual(recipe.distro_requires, None)

    def test_distro_metadata_stored_at_job_submission_time_for_traditional_distro(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertIsNone(recipe.installation.tree_url)
        self.assertIsNone(recipe.installation.initrd_path)
        self.assertIsNone(recipe.installation.kernel_path)
        self.assertEqual(recipe.installation.arch.arch, u'i386')
        self.assertEqual(recipe.installation.distro_name, u'BlueShoeLinux5-5')
        self.assertEqual(recipe.installation.osmajor, u'BlueShoeLinux5')
        self.assertEqual(recipe.installation.osminor, u'9')
        self.assertEqual(recipe.installation.variant, u'Server')
        self.assertNotEqual(recipe.distro_requires, None)

    def test_unknown_arch_in_user_defined_distro_throws_exception(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="idontexist"/>
                            <osversion major="RedHatEnterpriseLinux7" minor="4"/>
                            <name value="MyCustomLinux1.0"/>
                            <variant value="Server"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, 'No arch matches'):
            self.controller.process_xmljob(jobxml, self.user)

    def test_osminor_defaults_to_zero_when_not_provided_in_distro_metadata(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="ftp://dummylab.example.com/distros/MyCustomLinux1.0/Server/i386/os/"/>
                            <initrd url="pxeboot/initrd"/>
                            <kernel url="pxeboot/vmlinuz"/>
                            <arch value="i386"/>
                            <osversion major="RedHatEnterpriseLinux7"/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(jobxml, self.user)
        recipe = job.recipesets[0].recipes[0]
        self.assertEqual(recipe.installation.osminor, "0")

    def test_required_attributes_throw_exception_when_empty(self):
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="some/random/tree"/>
                            <kernel url="some/kernel"/>
                            <arch value="i386"/>
                            <osversion major=""/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<initrd/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="some/random/tree"/>
                            <initrd url="some/random/initrd"/>
                            <arch value="i386"/>
                            <osversion major=""/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<kernel/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
        jobxml = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distro>
                            <tree url="/some/random/tree"/>
                            <initrd url="/some/random/initrd"/>
                            <kernel url="/some/random/kernel"/>
                            <arch value="i386"/>
                            <name value="MyCustomLinux1.0"/>
                        </distro>
                        <hostRequires/>
                        <task name="/distribution/check-install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        with self.assertRaisesRegexp(BX, '<osmajor/> element is required'):
            self.controller.process_xmljob(jobxml, self.user)
コード例 #23
0
ファイル: test_jobs.py プロジェクト: ustbgaofan/beaker
class TestJobsController(unittest.TestCase):

    maxDiff = None

    @with_transaction
    def setUp(self):
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user()
        group = data_setup.create_group(group_name='somegroup')
        self.user.groups.append(group)
        testutil.set_identity_user(self.user)
        if not Distro.by_name(u'BlueShoeLinux5-5'):
            data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        data_setup.create_product(product_name=u'the_product')

    def tearDown(self):
        testutil.set_identity_user(None)

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = XmlJob(xmltramp.parse('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            '''))
        with session.begin():
            self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        session.begin()
        try:
            xmljob = XmlJob(xmltramp.parse('''
                <job>
                    <whiteboard>job with invalid hostRequires</whiteboard>
                    <recipeSet>
                        <recipe>
                            <distroRequires>
                                <distro_name op="=" value="BlueShoeLinux5-5" />
                            </distroRequires>
                            <hostRequires>
                                <memory op=">=" value="500MB" />
                            </hostRequires>
                            <task name="/distribution/install" role="STANDALONE">
                                <params/>
                            </task>
                        </recipe>
                    </recipeSet>
                </job>
                '''))
            self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))
        finally:
            session.rollback()
            session.close()

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job.xml')
        xmljob = XmlJob(xmltramp.parse(complete_job_xml))
        with session.begin():
            job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = job.to_xml(clone=True).toprettyxml(indent='    ')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)
コード例 #24
0
class TestJobsController(DatabaseTestCase):

    maxDiff = None

    def setUp(self):
        session.begin()
        from bkr.server.jobs import Jobs
        self.controller = Jobs()
        self.user = data_setup.create_user(user_name=u'test-job-owner',
                email_address=u'*****@*****.**')
        group = data_setup.create_group(group_name='somegroup')
        group.add_member(self.user)
        testutil.set_identity_user(self.user)
        data_setup.create_distro_tree(distro_name=u'BlueShoeLinux5-5')
        data_setup.create_product(product_name=u'the_product')
        session.flush()

    def tearDown(self):
        testutil.set_identity_user(None)
        session.rollback()

    def test_uploading_job_without_recipeset_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with norecipesets</whiteboard>
            </job>
            ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_uploading_job_with_invalid_hostRequires_raises_exception(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>job with invalid hostRequires</whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5" />
                        </distroRequires>
                        <hostRequires>
                            <memory op=">=" value="500MB" />
                        </hostRequires>
                        <task name="/distribution/install" role="STANDALONE">
                            <params/>
                        </task>
                    </recipe>
                </recipeSet>
            </job>
            ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    def test_job_xml_can_be_roundtripped(self):
        # Ideally the logic for parsing job XML into a Job instance would live in model code,
        # so that this test doesn't have to go through the web layer...
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        roundtripped_xml = lxml.etree.tostring(job.to_xml(clone=True), pretty_print=True, encoding='utf8')
        self.assertMultiLineEqual(roundtripped_xml, complete_job_xml)

    def test_complete_job_results(self):
        complete_job_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job.xml')
        xmljob = lxml.etree.fromstring(complete_job_xml)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        session.flush()

        # Complete the job, filling in values to match what's hardcoded in 
        # complete-job-results.xml...
        recipe = job.recipesets[0].recipes[0]
        guestrecipe = recipe.guests[0]
        data_setup.mark_recipe_running(recipe, fqdn=u'system.test-complete-job-results',
                start_time=datetime.datetime(2016, 1, 31, 23, 0, 0),
                install_started=datetime.datetime(2016, 1, 31, 23, 0, 1),
                install_finished=datetime.datetime(2016, 1, 31, 23, 0, 2),
                postinstall_finished=datetime.datetime(2016, 1, 31, 23, 0, 3),
                task_start_time=datetime.datetime(2016, 1, 31, 23, 0, 4))
        data_setup.mark_recipe_complete(guestrecipe, fqdn=u'guest.test-complete-job-results',
                mac_address='ff:ff:ff:00:00:00',
                start_time=datetime.datetime(2016, 1, 31, 23, 30, 0),
                install_started=datetime.datetime(2016, 1, 31, 23, 30, 1),
                install_finished=datetime.datetime(2016, 1, 31, 23, 30, 2),
                postinstall_finished=datetime.datetime(2016, 1, 31, 23, 30, 3),
                finish_time=datetime.datetime(2016, 1, 31, 23, 30, 4))
        data_setup.mark_recipe_complete(recipe, only=True,
                start_time=datetime.datetime(2016, 1, 31, 23, 0, 4),
                finish_time=datetime.datetime(2016, 1, 31, 23, 59, 0))
        recipe.installation.rendered_kickstart.url = u'http://example.com/recipe.ks'
        guestrecipe.installation.rendered_kickstart.url = u'http://example.com/guest.ks'
        session.flush()
        # Hack up the database ids... This will fail if it's flushed, but it's 
        # the easiest way to make them match the expected values.
        job.id = 1
        job.recipesets[0].id = 1
        recipe.id = 1
        guestrecipe.id = 2
        recipe.tasks[0].id = 1
        recipe.tasks[1].id = 2
        guestrecipe.tasks[0].id = 3
        guestrecipe.tasks[0].results[0].id = 1
        recipe.tasks[0].results[0].id = 2
        recipe.tasks[1].results[0].id = 3

        expected_results_xml = pkg_resources.resource_string('bkr.inttest', 'complete-job-results.xml')
        actual_results_xml = lxml.etree.tostring(job.to_xml(clone=False),
                pretty_print=True, encoding='utf8')
        self.assertMultiLineEqual(expected_results_xml, actual_results_xml)

    def test_does_not_fail_when_whiteboard_empty(self):
        xml = """
            <job>
                <recipeSet>
                    <recipe>
                        <distroRequires>
                            <distro_name op="=" value="BlueShoeLinux5-5"/>
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </recipe>
                </recipeSet>
            </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'')

    def test_creates_taskpackages_successfully(self):
        # Note: installPackage is deprecated but we still provide backwards compatibility
        xml = """
        <job>
            <recipeSet>
                <recipe>
                    <distroRequires>
                        <distro_name op="=" value="BlueShoeLinux5-5"/>
                    </distroRequires>
                    <hostRequires/>
                    <installPackage>libbeer</installPackage>
                    <task name="/distribution/install" role="STANDALONE"/>
                </recipe>
            </recipeSet>
        </job>
        """
        xmljob = lxml.etree.fromstring(xml)
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertListEqual(['libbeer'], [x.package for x in job.recipesets[0].recipes[0].custom_packages])

    def test_upload_xml_catches_invalid_xml(self):
        """We want that invalid Job XML is caught in the validation step."""
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with arbitrary XML in namespaces</whriteboard>
                <recipeSet>
                    <rawcipe>
                        <distroRequires>
                            <distro_name />
                        </distroRequires>
                        <hostRequires/>
                        <task name="/distribution/install" role="STANDALONE"/>
                    </rawcipe>
                </recipeSet>
            </job>
        ''')
        self.assertRaises(BX, lambda: self.controller.process_xmljob(xmljob, self.user))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1112131
    def test_preserves_arbitrary_XML_elements_in_namespace(self):
        complete_job_xml = pkg_resources.resource_filename('bkr.inttest', 'complete-job.xml')
        with open(complete_job_xml, 'r') as f:
            contents = f.read()
            xmljob = lxml.etree.fromstring(contents)
        job = testutil.call(self.controller.process_xmljob, xmljob, self.user)
        tree = job.to_xml(clone=True)
        self.assertEqual(2, len(tree.xpath('*[namespace-uri()]')))
        self.assertEqual('<b:option xmlns:b="http://example.com/bar">--foobar arbitrary</b:option>',
                         lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[0], encoding='utf8'))
        self.assertEqual(u'<f:test xmlns:f="http://example.com/foo">unicode text: heißer Шис</f:test>'.encode('utf8'),
                         lxml.etree.tostring(tree.xpath('*[namespace-uri()]')[1], encoding='utf8'))

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.recipesets[0].recipes[0].reservation_request.duration, 86400)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1295642
    def test_parses_reservesys_with_duration(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whriteboard>job with reservesys with duration</whriteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                        <reservesys duration="600"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.recipesets[0].recipes[0].reservation_request.duration, 600)

    # https://bugzilla.redhat.com/show_bug.cgi?id=1302857
    def test_strips_whitespace_from_whiteboard(self):
        xmljob = lxml.etree.fromstring('''
            <job>
                <whiteboard>
                    so pretty
                </whiteboard>
                <recipeSet>
                    <recipe>
                        <distroRequires/> <hostRequires/>
                        <task name="/distribution/install"/>
                    </recipe>
                </recipeSet>
            </job>
        ''')
        job = self.controller.process_xmljob(xmljob, self.user)
        self.assertEqual(job.whiteboard, u'so pretty')