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)
def test_ack_jobxml(self): _response_type = u'ack' with session.begin(): rs = self.job.recipesets[0] rs.nacked = RecipeSetResponse(type=_response_type) jobxml = XmlJob(xmltramp.parse(self.job.to_xml(clone=False).toprettyxml())) for xmlrecipeSet in jobxml.iter_recipeSets(): response = xmlrecipeSet.get_xml_attr('response',unicode,None) self.assertEqual(response,_response_type)
def test_ack_jobxml(self): _response_type = u'ack' with session.begin(): rs = self.job.recipesets[0] rs.nacked = RecipeSetResponse(type=_response_type) jobxml = XmlJob( xmltramp.parse(self.job.to_xml(clone=False).toprettyxml())) for xmlrecipeSet in jobxml.iter_recipeSets(): response = xmlrecipeSet.get_xml_attr('response', unicode, None) self.assertEqual(response, _response_type)
def test_ack_jobxml_clone(self): """ Unline test_ack_jobxml, we do _not_ want to see our response in here """ _response_type = u'ack' with session.begin(): rs = self.job.recipesets[0] rs.nacked = RecipeSetResponse(type=_response_type) jobxml = XmlJob(xmltramp.parse(self.job.to_xml(clone=True).toprettyxml())) for xmlrecipeSet in jobxml.iter_recipeSets(): response = xmlrecipeSet.get_xml_attr('response',unicode,None) self.assertEqual(response,None)
def test_ack_jobxml_clone(self): """ Unline test_ack_jobxml, we do _not_ want to see our response in here """ _response_type = u'ack' with session.begin(): rs = self.job.recipesets[0] rs.nacked = RecipeSetResponse(type=_response_type) jobxml = XmlJob( xmltramp.parse(self.job.to_xml(clone=True).toprettyxml())) for xmlrecipeSet in jobxml.iter_recipeSets(): response = xmlrecipeSet.get_xml_attr('response', unicode, None) self.assertEqual(response, None)
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_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)
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_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)
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)
def upload(self, jobxml, ignore_missing_tasks=False): """ Queues a new job. :param jobxml: XML description of job to be queued :type jobxml: string :param ignore_missing_tasks: pass True for this parameter to cause unknown tasks to be silently discarded (default is False) :type ignore_missing_tasks: bool """ # xml.sax (and thus, xmltramp) expect raw bytes, not unicode if isinstance(jobxml, unicode): jobxml = jobxml.encode('utf8') xml = xmltramp_parse_untrusted(jobxml) xmljob = XmlJob(xml) job = self.process_xmljob(xmljob, identity.current.user, ignore_missing_tasks=ignore_missing_tasks) session.flush() # so that we get an id return "J:%s" % job.id
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()
try: # xml.sax (and thus, xmltramp) expect raw bytes, not unicode textxml = textxml.encode('utf8') if not confirmed: job_schema = lxml.etree.RelaxNG(self.job_schema_doc) if not job_schema.validate(lxml.etree.fromstring(textxml)): log.debug('Job failed validation, with errors: %r', job_schema.error_log) return dict( title=title, form=self.job_form, action='clone', options={'xsd_errors': job_schema.error_log}, value=dict(textxml=textxml, confirmed=True), ) xmljob = XmlJob(xmltramp_parse_untrusted(textxml)) job = self.process_xmljob(xmljob, identity.current.user) session.flush() except Exception, err: session.rollback() flash(_(u'Failed to import job because of: %s' % err)) return dict( title=title, form=self.job_form, action='./clone', options={}, value=dict(textxml="%s" % textxml, confirmed=confirmed), ) else: self.success_redirect(job.id) return dict(
try: # xml.sax (and thus, xmltramp) expect raw bytes, not unicode textxml = textxml.encode('utf8') if not confirmed: job_schema = lxml.etree.RelaxNG(self.job_schema_doc) if not job_schema.validate(lxml.etree.fromstring(textxml)): log.debug('Job failed validation, with errors: %r', job_schema.error_log) return dict( title = title, form = self.job_form, action = 'clone', options = {'xsd_errors': job_schema.error_log}, value = dict(textxml=textxml, confirmed=True), ) xmljob = XmlJob(xmltramp.parse(textxml)) job = self.process_xmljob(xmljob,identity.current.user) session.flush() except Exception,err: session.rollback() flash(_(u'Failed to import job because of: %s' % err)) return dict( title = title, form = self.job_form, action = './clone', options = {}, value = dict(textxml = "%s" % textxml, confirmed=confirmed), ) else: self.success_redirect(job.id) return dict(
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)