def test_mixed_multinode(self): user = self.factory.make_user() device_type = self.factory.make_device_type() self.factory.make_device(device_type, 'fakeqemu1') self.factory.make_device(device_type, 'fakeqemu2') self.factory.make_device(device_type, 'fakeqemu3') self.factory.make_device(device_type, 'fakeqemu4') submission = yaml.load(open( os.path.join(os.path.dirname(__file__), 'kvm-multinode.yaml'), 'r')) role_list = submission['protocols'][MultinodeProtocol.name]['roles'] for role in role_list: if 'tags' in role_list[role]: del role_list[role]['tags'] job_list = TestJob.from_yaml_and_user(yaml.dump(submission), user) self.assertEqual(len(job_list), 2) # make the list mixed fakeqemu1 = Device.objects.get(hostname='fakeqemu1') fakeqemu1.is_pipeline = False fakeqemu1.save(update_fields=['is_pipeline']) fakeqemu3 = Device.objects.get(hostname='fakeqemu3') fakeqemu3.is_pipeline = False fakeqemu3.save(update_fields=['is_pipeline']) device_list = Device.objects.filter(device_type=device_type, is_pipeline=True) self.assertEqual(len(device_list), 2) self.assertIsInstance(device_list, RestrictedResourceQuerySet) self.assertIsInstance(list(device_list), list) job_list = TestJob.from_yaml_and_user(yaml.dump(submission), user) self.assertEqual(len(job_list), 2) for job in job_list: self.assertEqual(job.requested_device_type, device_type)
def test_from_yaml_unsupported_tags(self): self.factory.make_device(self.device_type, 'fakeqemu1') self.factory.ensure_tag('usb') self.factory.ensure_tag('sata') try: TestJob.from_yaml_and_user( self.factory.make_job_json(tags=['usb', 'sata']), self.factory.make_user()) except DevicesUnavailableException: pass else: self.fail("Device tags failure: job submitted without any devices supporting the requested tags")
def test_from_yaml_and_user_reuses_tag_objects(self): self.factory.ensure_tag('tag') tags = list(Tag.objects.filter(name='tag')) self.factory.make_device(device_type=self.device_type, hostname="fakeqemu1", tags=tags) job1 = TestJob.from_yaml_and_user( self.factory.make_job_json(tags=['tag']), self.factory.make_user()) job2 = TestJob.from_yaml_and_user( self.factory.make_job_json(tags=['tag']), self.factory.make_user()) self.assertEqual( set(tag.pk for tag in job1.tags.all()), set(tag.pk for tag in job2.tags.all()))
def test_set(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) result_samples = [ {"case": "linux-INLINE-lscpu", "definition": "smoke-tests-basic", "result": "fail", "set": "listing"}, {"case": "linux-INLINE-lspci", "definition": "smoke-tests-basic", "result": "fail", "set": "listing"} ] suite = TestSuite.objects.create( job=job, name='test-suite' ) suite.save() self.assertEqual('/results/%s/test-suite' % job.id, suite.get_absolute_url()) for sample in result_samples: ret = map_scanned_results(results=sample, job=job) self.assertTrue(ret) self.assertEqual(2, TestCase.objects.count()) val = URLValidator() for testcase in TestCase.objects.filter(suite=suite): self.assertEqual(testcase.suite, suite) self.assertIsNotNone(testcase.name) self.assertIsNotNone(testcase.result) self.assertIsNone(testcase.metadata) self.assertEqual(testcase.result, TestCase.RESULT_PASS) self.assertEqual(testcase.test_set.name, 'listing') self.assertTrue(testcase.name.startswith('linux-INLINE-')) val('http://localhost/%s' % testcase.get_absolute_url()) self.factory.cleanup()
def test_job(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), user) job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) device = Device.objects.get(hostname='fakeqemu1') device_config = device.load_device_configuration(job_ctx) # raw dict parser = JobParser() obj = PipelineDevice(device_config, device.hostname) pipeline_job = parser.parse(job.definition, obj, job.id, None, output_dir='/tmp') pipeline_job.pipeline.validate_actions() pipeline = pipeline_job.describe() map_metadata(yaml.dump(pipeline), job) self.assertEqual(MetaType.objects.filter(metatype=MetaType.DEPLOY_TYPE).count(), 1) self.assertEqual(MetaType.objects.filter(metatype=MetaType.BOOT_TYPE).count(), 1) count = ActionData.objects.all().count() self.assertEqual(TestData.objects.all().count(), 1) testdata = TestData.objects.all()[0] self.assertEqual(testdata.testjob, job) for actionlevel in ActionData.objects.all(): self.assertEqual(actionlevel.testdata, testdata) action_levels = [] for testdata in job.test_data.all(): action_levels.extend(testdata.actionlevels.all()) self.assertEqual(count, len(action_levels)) count = ActionData.objects.filter(meta_type__metatype=MetaType.DEPLOY_TYPE).count() self.assertNotEqual(ActionData.objects.filter(meta_type__metatype=MetaType.BOOT_TYPE).count(), 0) self.assertEqual(ActionData.objects.filter(meta_type__metatype=MetaType.UNKNOWN_TYPE).count(), 0) for actionlevel in ActionData.objects.filter(meta_type__metatype=MetaType.BOOT_TYPE): self.assertEqual(actionlevel.testdata.testjob.id, job.id) self.assertEqual(ActionData.objects.filter( meta_type__metatype=MetaType.DEPLOY_TYPE, testdata__testjob=job ).count(), count)
def test_level_input(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), user) suite = TestSuite.objects.create( job=job, name='test-suite' ) suite.save() result_sample = """ results: lava-test-shell: !!python/object/apply:collections.OrderedDict - - [ping-test, fail] power_off: !!python/object/apply:collections.OrderedDict - - [status, Complete] - [level, 5.1] """ scanned = yaml.load(result_sample) ret = map_scanned_results(scanned_dict=scanned, job=job) self.assertTrue(ret) for testcase in TestCase.objects.filter(suite=suite): if testcase.name == 'power_off': self.assertTrue(type(testcase.metadata) in [str, unicode]) self.assertTrue(type(testcase.action_data) == OrderedDict) self.assertEqual(testcase.action_data['status'], 'Complete') self.assertEqual(testcase.action_data['level'], 5.1) self.assertEqual(testcase.action_level, '5.1') self.assertEqual(testcase.result, TestCase.RESULT_UNKNOWN) self.factory.cleanup()
def test_case_as_url(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) test_dict = { 'definition': 'unit-test', 'case': 'unit-test', 'level': '1.3.4.1', # list of numbers, generates a much longer YAML string than just the count 'result': 'pass' } pattern = '[-_a-zA-Z0-9.\\(\\)]+' matches = re.search(pattern, test_dict['case']) self.assertIsNotNone(matches) # passes self.assertEqual(matches.group(0), test_dict['case']) suite, _ = TestSuite.objects.get_or_create(name=test_dict["definition"], job=job) case, _ = TestCase.objects.get_or_create(suite=suite, name=test_dict['case'], result=TestCase.RESULT_PASS) self.assertIsNotNone(reverse('lava.results.testcase', args=[case.id])) self.assertIsNotNone(reverse('lava.results.testcase', args=[job.id, suite.name, case.id])) self.assertIsNotNone(map_scanned_results(test_dict, job, None)) # now break the reverse pattern test_dict['case'] = 'unit test' # whitespace in the case name matches = re.search(pattern, test_dict['case']) self.assertIsNotNone(matches) self.assertRaises(NoReverseMatch, reverse, 'lava.results.testcase', args=[job.id, suite.name, test_dict['case']])
def test_duration(self): TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) metatype = MetaType(name='fake', metatype=MetaType.DEPLOY_TYPE) metatype.save() action_data = ActionData(meta_type=metatype, action_level='1.2.3', action_name='fake') action_data.save() action_data.duration = '1.2' action_data.save(update_fields=['duration']) action_data = ActionData.objects.get(id=action_data.id) # reload self.assertIsInstance(action_data.duration, decimal.Decimal) # unit tests check the instance as well as the value. self.assertEqual(float(action_data.duration), 1.2) action_data.timeout = 300 action_data.save(update_fields=['timeout']) self.assertEqual(action_data.timeout, 300)
def test_metastore(self): field = TestCase._meta.get_field('metadata') level = '1.3.5.1' # artificially inflate results to represent a set of kernel messages results = { 'definition': 'lava', 'case': 'unit-test', # list of numbers, generates a much longer YAML string than just the count 'extra': range(int(field.max_length / 2)), 'result': 'pass' } stub = "%s-%s-%s.yaml" % (results['definition'], results['case'], level) job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) meta_filename = os.path.join(job.output_dir, 'metadata', stub) filename = "%s/job-%s/pipeline/%s/%s-%s.yaml" % (job.output_dir, job.id, level.split('.')[0], level, results['definition']) mkdir(os.path.dirname(filename)) if os.path.exists(meta_filename): # isolate from other unit tests os.unlink(meta_filename) self.assertEqual(meta_filename, create_metadata_store(results, job, level)) self.assertTrue(map_scanned_results(results, job, meta_filename)) self.assertEqual(TestCase.objects.filter(name='unit-test').count(), 1) test_data = yaml.load(TestCase.objects.filter(name='unit-test')[0].metadata, Loader=yaml.CLoader) self.assertEqual(test_data['extra'], meta_filename) self.assertTrue(os.path.exists(meta_filename)) with open(test_data['extra'], 'r') as extra_file: data = yaml.load(extra_file, Loader=yaml.CLoader) self.assertIsNotNone(data) os.unlink(meta_filename) shutil.rmtree(job.output_dir)
def test_select_device(self): self.restart() hostname = 'fakeqemu3' device_dict = DeviceDictionary(hostname=hostname) device_dict.parameters = self.conf device_dict.save() device = self.factory.make_device(self.device_type, hostname) job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.factory.make_user()) # this uses the system jinja2 path - local changes to the qemu.jinja2 # will not be available. selected = select_device(job, self.dispatchers) self.assertIsNone(selected) job.actual_device = device selected = select_device(job, self.dispatchers) self.assertIsNone(selected) device.worker_host = self.worker selected = select_device(job, self.dispatchers) self.assertIsNone(selected) # device needs to be in reserved state # fake up the assignment which needs a separate test job.actual_device = device job.save() device.current_job = job device.status = Device.RESERVED device.save() selected = select_device(job, self.dispatchers) self.assertEqual(selected, device)
def test_parameter_support(self): # pylint: disable=too-many-locals data = self.factory.make_job_data() test_block = [block for block in data['actions'] if 'test' in block][0] smoke = test_block['test']['definitions'][0] smoke['parameters'] = { 'VARIABLE_NAME_1': "first variable value", 'VARIABLE_NAME_2': "second value" } job = TestJob.from_yaml_and_user(yaml.dump(data), self.user) job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) device = Device.objects.get(hostname='fakeqemu1') device_config = device.load_device_configuration(job_ctx, system=False) # raw dict parser = JobParser() obj = PipelineDevice(device_config, device.hostname) pipeline_job = parser.parse(job.definition, obj, job.id, None, "", output_dir='/tmp') allow_missing_path(pipeline_job.pipeline.validate_actions, self, 'qemu-system-x86_64') pipeline = pipeline_job.describe() device_values = _get_device_metadata(pipeline['device']) try: testdata, _ = TestData.objects.get_or_create(testjob=job) except (MultipleObjectsReturned): self.fail('multiple objects') for key, value in device_values.items(): if not key or not value: continue testdata.attributes.create(name=key, value=value) retval = _get_job_metadata(pipeline['job']['actions']) self.assertIn('test.0.common.definition.parameters.VARIABLE_NAME_2', retval) self.assertIn('test.0.common.definition.parameters.VARIABLE_NAME_1', retval) self.assertEqual(retval['test.0.common.definition.parameters.VARIABLE_NAME_1'], 'first variable value') self.assertEqual(retval['test.0.common.definition.parameters.VARIABLE_NAME_2'], 'second value')
def test_pipeline_results(self): result_sample = """ - results: !!python/object/apply:collections.OrderedDict - - [linux-linaro-ubuntu-pwd, pass] - [linux-linaro-ubuntu-uname, pass] - [linux-linaro-ubuntu-vmstat, pass] - [linux-linaro-ubuntu-ifconfig, pass] - [linux-linaro-ubuntu-lscpu, pass] - [linux-linaro-ubuntu-lsb_release, pass] - [linux-linaro-ubuntu-netstat, pass] - [linux-linaro-ubuntu-ifconfig-dump, pass] - [linux-linaro-ubuntu-route-dump-a, pass] - [linux-linaro-ubuntu-route-ifconfig-up-lo, pass] - [linux-linaro-ubuntu-route-dump-b, pass] - [linux-linaro-ubuntu-route-ifconfig-up, pass] - [ping-test, fail] - [realpath-check, fail] - [ntpdate-check, pass] - [curl-ftp, pass] - [tar-tgz, pass] - [remove-tgz, pass] """ result_store = { 'result_sample': OrderedDict([ ('linux-linaro-ubuntu-pwd', 'pass'), ('linux-linaro-ubuntu-uname', 'pass'), ('linux-linaro-ubuntu-vmstat', 'pass'), ('linux-linaro-ubuntu-ifconfig', 'pass'), ('linux-linaro-ubuntu-lscpu', 'pass'), ('linux-linaro-ubuntu-lsb_release', 'pass'), ('linux-linaro-ubuntu-netstat', 'pass'), ('linux-linaro-ubuntu-ifconfig-dump', 'pass'), ('linux-linaro-ubuntu-route-dump-a', 'pass'), ('linux-linaro-ubuntu-route-ifconfig-up-lo', 'pass'), ('linux-linaro-ubuntu-route-dump-b', 'pass'), ('linux-linaro-ubuntu-route-ifconfig-up', 'pass'), ('ping-test', 'fail'), ('realpath-check', 'fail'), ('ntpdate-check', 'pass'), ('curl-ftp', 'pass'), ('tar-tgz', 'pass'), ('remove-tgz', 'pass')])} name = "result_sample" user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_json(), user) store = JobPipeline.get(job.id) scanned = yaml.load(result_sample) if isinstance(scanned, list) and len(scanned) == 1: if 'results' in scanned[0] and isinstance(scanned[0], dict): store.pipeline.update({name: scanned[0]['results']}) # too often to save the results? store.save() self.assertIsNotNone(store.pipeline) self.assertIsNot({}, store.pipeline) self.assertIs(type(store.pipeline), dict) self.assertIn('result_sample', store.pipeline) self.assertIs(type(store.pipeline['result_sample']), OrderedDict) self.assertEqual(store.pipeline, result_store)
def test_result_store(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) store = JobPipeline.get(job.id) self.assertIsNotNone(store) self.assertIsInstance(store, JobPipeline) self.assertIs(type(store.pipeline), dict) self.factory.cleanup()
def test_make_ssh_guest_yaml(self): hostname = 'fakeqemu3' device = self.factory.make_device(self.device_type, hostname) try: jobs = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.factory.make_user()) except DevicesUnavailableException as exc: self.fail(exc) sub_id = [] group_size = 0 path = os.path.join(os.path.dirname(os.path.join(__file__)), 'devices') host_role = [] for job in jobs: data = yaml.load(job.definition) params = data['protocols']['lava-multinode'] params['target_group'] = 'replaced' if not group_size: group_size = params['group_size'] if job.device_role == 'host': self.assertFalse(job.dynamic_connection) self.assertEqual(job.requested_device_type.name, device.device_type.name) self.assertIn(params['sub_id'], [0, 1, 2]) sub_id.append(params['sub_id']) comparison = yaml.load(open(os.path.join(path, 'qemu-ssh-parent.yaml'), 'r').read()) self.assertIn('protocols', data) self.assertIn('lava-multinode', data['protocols']) self.assertIn('sub_id', data['protocols']['lava-multinode']) del(comparison['protocols']['lava-multinode']['sub_id']) del(data['protocols']['lava-multinode']['sub_id']) self.assertEqual( data, comparison ) self.assertEqual(job.device_role, 'host') host_role.append(job.device_role) else: self.assertTrue(job.dynamic_connection) self.assertNotIn(sub_id, params['sub_id']) sub_id.append(params['sub_id']) self.assertIsNone(job.requested_device_type) deploy = [action for action in data['actions'] if 'deploy' in action][0] self.assertEqual(deploy['deploy']['connection'], 'ssh') # validate each job del(data['protocols']['lava-multinode']['sub_id']) self.assertEqual( data, yaml.load(open(os.path.join(path, 'qemu-ssh-guest-1.yaml'), 'r').read()) ) self.assertIsNone(job.requested_device_type) self.assertIsNone(job.actual_device) host_role.append(data['host_role']) self.assertFalse(any(role for role in host_role if role != 'host')) self.assertEqual(len(sub_id), group_size) self.assertEqual(sub_id, list(range(group_size)))
def test_new_pipeline_store(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_json(), user) store = JobPipeline.get('foo') self.assertIsNone(store) store = JobPipeline.get(job.id) self.assertIsNotNone(store) self.assertIsInstance(store, JobPipeline) self.assertIs(type(store.pipeline), dict)
def testjob_submission(job_definition, user, check_device=None): """ Single submission frontend for JSON or YAML :param job_definition: string of the job submission :param user: user attempting the submission :param check_device: set specified device as the target **and** thereby set job as a health check job. (JSON only) :return: a job or a list of jobs :raises: SubmissionException, Device.DoesNotExist, DeviceType.DoesNotExist, DevicesUnavailableException, JSONDataError, JSONDecodeError, ValueError """ if is_deprecated_json(job_definition): allow_health = False job_json = simplejson.loads(job_definition) target_device = None if 'target' in job_json: target_device = Device.objects.get(hostname=job_json['target']) if check_device: job_json['target'] = check_device.hostname job_json['health-check'] = True job_definition = simplejson.dumps(job_json) allow_health = True try: # returns a single job or a list (not a QuerySet) of job objects. job = TestJob.from_json_and_user(job_definition, user, health_check=allow_health) if isinstance(job, list): # multinode health checks not supported return job job.health_check = allow_health if check_device: job.requested_device = check_device elif target_device: job.requested_device = target_device job.save(update_fields=['health_check', 'requested_device']) except (JSONDataError, ValueError) as exc: if check_device: check_device.put_into_maintenance_mode( user, "Job submission failed for health job for %s: %s" % (check_device, exc)) raise JSONDataError("Health check job submission failed for %s: %s" % (check_device, exc)) else: raise JSONDataError("Job submission failed: %s" % exc) else: validate_job(job_definition) # returns a single job or a list (not a QuerySet) of job objects. job = TestJob.from_yaml_and_user(job_definition, user) if check_device and isinstance(check_device, Device) and not isinstance(job, list): # the slave must neither know nor care if this is a health check, # only the master cares and that has the database connection. job.health_check = True job.requested_device = check_device job.save(update_fields=['health_check', 'requested_device']) return job
def test_from_yaml_and_user_sets_multiple_tag_from_device_tags(self): tag_list = [ self.factory.ensure_tag('tag1'), self.factory.ensure_tag('tag2') ] self.factory.make_device(self.device_type, hostname='fakeqemu1', tags=tag_list) job = TestJob.from_yaml_and_user( self.factory.make_job_json(tags=['tag1', 'tag2']), self.factory.make_user()) self.assertEqual( set(tag.name for tag in job.tags.all()), {'tag1', 'tag2'})
def test_invalid_device(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_json(), user) job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) device = Device.objects.get(hostname='fakeqemu1') device_config = device.load_device_configuration(job_ctx, system=False) # raw dict del device_config['device_type'] parser = JobParser() obj = PipelineDevice(device_config, device.hostname) # equivalent of the NewDevice in lava-dispatcher, without .yaml file. self.assertRaises(KeyError, parser.parse, job.definition, obj, job.id, None, None, None, output_dir='/tmp')
def test_set(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), user) result_sample = """ results: lava-test-shell: !!python/object/apply:collections.OrderedDict - - [ping-test, fail] - - set-name - !!python/object/apply:collections.OrderedDict - - [linux-linaro-foo, pass] - [linux-linaro-ubuntu-uname, pass] - [linux-linaro-ubuntu-vmstat, pass] - [linux-linaro-ubuntu-ifconfig, pass] - [linux-linaro-ubuntu-lscpu, pass] - [linux-linaro-ubuntu-lsb_release, pass] - [linux-linaro-ubuntu-netstat, pass] - [linux-linaro-ubuntu-ifconfig-dump, pass] - [linux-linaro-ubuntu-route-dump-a, pass] - [linux-linaro-ubuntu-route-ifconfig-up-lo, pass] - [linux-linaro-ubuntu-route-dump-b, pass] - [linux-linaro-ubuntu-route-ifconfig-up, pass] - [realpath-check, fail] - [ntpdate-check, pass] - [curl-ftp, pass] - [tar-tgz, pass] - [remove-tgz, pass] """ scanned = yaml.load(result_sample) suite = TestSuite.objects.create( job=job, name='test-suite' ) suite.save() self.assertEqual('/results/%s/test-suite' % job.id, suite.get_absolute_url()) ret = map_scanned_results(scanned_dict=scanned, job=job) self.assertTrue(ret) self.assertIsNot([], TestCase.objects.all()) val = URLValidator() for testcase in TestCase.objects.filter(suite=suite): self.assertEqual(testcase.suite, suite) self.assertIsNotNone(testcase.name) self.assertIsNotNone(testcase.result) self.assertIsNone(testcase.metadata) self.assertNotEqual(testcase.result, TestCase.RESULT_UNKNOWN) if testcase.test_set: self.assertEqual(testcase.test_set.name, 'set-name') self.assertTrue(testcase.name.startswith('linux-linaro')) val('http://localhost/%s' % testcase.get_absolute_url()) self.factory.cleanup()
def test_export(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) test_suite = TestSuite.objects.get_or_create(name='lava', job=job)[0] test_case = TestCase( name='name', suite=test_suite, result=TestCase.RESULT_FAIL ) self.assertTrue( any( map(lambda v: v in testcase_export_fields(), export_testcase(test_case).keys()) ) )
def test_bad_input(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) # missing {'results'} key result_samples = [ {"definition": "lava", "result": "pass"}, {"case": "test-runscript-overlay", "result": "pass"}, {"case": "test-runscript-overlay", "definition": "lava"}, {} ] for sample in result_samples: ret = map_scanned_results(results=sample, job=job) self.assertFalse(ret) self.factory.cleanup()
def test_same_type_devices_with_map(self): device_dict = DeviceDictionary(hostname=self.factory.bbb1.hostname) device_dict.parameters = { # client, RJ45 10M 100M 'extends': 'beaglebone-black.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1"}, 'mac_addr': {'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:c0:8b"}, 'tags': {'eth0': [], 'eth1': ['RJ45', '10M', '100M']}, 'map': {'eth0': {'192.168.0.2': 5}, 'eth1': {'192.168.0.2': 7}} } device_dict.save() bbb2 = self.factory.make_device(self.factory.bbb_type, hostname='bbb2') device_dict = DeviceDictionary(hostname=bbb2.hostname) device_dict.parameters = { # server, RJ45 10M 100M 'extends': 'beaglebone-black.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1"}, 'mac_addr': {'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c"}, 'tags': {'eth0': [], 'eth1': ['RJ45', '10M', '100M']}, 'map': {'eth0': {'192.168.0.2': 7}, 'eth1': {'192.168.0.2': 9}} } device_dict.save() devices = list(Device.objects.filter(status=Device.IDLE).order_by('is_public')) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'bbb-bbb-vland-group.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue(match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() self.factory.bbb1.refresh_from_db() bbb2.refresh_from_db() self.assertIsNotNone(self.factory.bbb1.current_job) self.assertIsNotNone(bbb2.current_job) self.assertIsNotNone(self.factory.bbb1.current_job.actual_device) self.assertIsNotNone(bbb2.current_job.actual_device) self.assertNotEqual(self.factory.bbb1.current_job, bbb2.current_job) self.assertNotEqual(self.factory.bbb1.current_job.actual_device, bbb2.current_job.actual_device)
def test_visibility(self): user = self.factory.make_user() user2 = self.factory.make_user() user3 = self.factory.make_user() # public set in the YAML yaml_str = self.factory.make_job_json() yaml_data = yaml.load(yaml_str) job = TestJob.from_yaml_and_user( yaml_str, user) self.assertTrue(job.is_public) self.assertTrue(job.can_view(user)) self.assertTrue(job.can_view(user2)) self.assertTrue(job.can_view(user3)) yaml_data['visibility'] = 'personal' self.assertEqual(yaml_data['visibility'], 'personal') job2 = TestJob.from_yaml_and_user( yaml.dump(yaml_data), user3) self.assertFalse(job2.is_public) self.assertFalse(job2.can_view(user)) self.assertFalse(job2.can_view(user2)) self.assertTrue(job2.can_view(user3)) group1, _ = Group.objects.get_or_create(name='group1') group1.user_set.add(user2) job2.viewing_groups.add(group1) job2.visibility = TestJob.VISIBLE_GROUP job2.save() self.assertFalse(job2.is_public) self.assertEqual(job2.visibility, TestJob.VISIBLE_GROUP) self.assertEqual(len(job2.viewing_groups.all()), 1) self.assertIn(group1, job2.viewing_groups.all()) self.assertTrue(job.can_view(user2)) self.assertFalse(job2.can_view(user)) self.assertTrue(job2.can_view(user3))
def testjob_submission(job_definition, user, original_job=None): """ Single submission frontend for YAML :param job_definition: string of the job submission :param user: user attempting the submission :return: a job or a list of jobs :raises: SubmissionException, Device.DoesNotExist, DeviceType.DoesNotExist, DevicesUnavailableException, ValueError """ validate_job(job_definition) # returns a single job or a list (not a QuerySet) of job objects. job = TestJob.from_yaml_and_user(job_definition, user, original_job=original_job) return job
def test_compatibility(self): # pylint: disable=too-many-locals user = self.factory.make_user() # public set in the YAML yaml_str = self.factory.make_job_json() yaml_data = yaml.load(yaml_str) job = TestJob.from_yaml_and_user( yaml_str, user) self.assertTrue(job.is_public) self.assertTrue(job.can_view(user)) # initial state prior to validation self.assertEqual(job.pipeline_compatibility, 0) self.assertNotIn('compatibility', yaml_data) # FIXME: dispatcher master needs to make this kind of test more accessible. definition = yaml.load(job.definition) self.assertNotIn('protocols', definition) job.actual_device = Device.objects.get(hostname='fakeqemu1') job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) parser = JobParser() device = job.actual_device try: device_config = device.load_device_configuration(job_ctx, system=False) # raw dict except (jinja2.TemplateError, yaml.YAMLError, IOError) as exc: # FIXME: report the exceptions as useful user messages self.fail("[%d] jinja2 error: %s" % (job.id, exc)) if not device_config or not isinstance(device_config, dict): # it is an error to have a pipeline device without a device dictionary as it will never get any jobs. msg = "Administrative error. Device '%s' has no device dictionary." % device.hostname self.fail('[%d] device-dictionary error: %s' % (job.id, msg)) device_object = PipelineDevice(device_config, device.hostname) # equivalent of the NewDevice in lava-dispatcher, without .yaml file. # FIXME: drop this nasty hack once 'target' is dropped as a parameter if 'target' not in device_object: device_object.target = device.hostname device_object['hostname'] = device.hostname parser_device = device_object try: # pass (unused) output_dir just for validation as there is no zmq socket either. pipeline_job = parser.parse( job.definition, parser_device, job.id, None, None, None, output_dir=job.output_dir) except (AttributeError, JobError, NotImplementedError, KeyError, TypeError) as exc: self.fail('[%s] parser error: %s' % (job.sub_id, exc)) description = pipeline_job.describe() self.assertIn('compatibility', description) self.assertGreaterEqual(description['compatibility'], BootQEMU.compatibility)
def test_bad_input(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), user) # missing {'results'} key result_sample = """ lava-test-shell: !!python/object/apply:collections.OrderedDict - - [ping-test, fail] - [realpath-check, fail] - [ntpdate-check, pass] - [curl-ftp, pass] - [tar-tgz, pass] - [remove-tgz, pass] """ scanned = yaml.load(result_sample) suite = TestSuite.objects.create( job=job, name='test-suite' ) suite.save() self.assertEqual('/results/%s/test-suite' % job.id, suite.get_absolute_url()) ret = map_scanned_results(scanned_dict=scanned, job=job) self.assertFalse(ret) result_sample = """ results: lava-test-shell: !!python/object/apply:collections.OrderedDict - - [ping-test, fail] power_off: !!python/object/apply:collections.OrderedDict - - [status, Complete] """ scanned = yaml.load(result_sample) self.assertEqual('/results/%s/test-suite' % job.id, suite.get_absolute_url()) ret = map_scanned_results(scanned_dict=scanned, job=job) self.assertTrue(ret) for testcase in TestCase.objects.filter(suite=suite): if testcase.name == 'power_off': self.assertTrue(type(testcase.metadata) in [str, unicode]) self.assertTrue(type(testcase.action_data) == OrderedDict) self.assertEqual(testcase.action_data['status'], 'Complete') self.assertEqual(testcase.result, TestCase.RESULT_UNKNOWN) elif testcase.name == 'ping-test': self.assertIsNone(testcase.metadata) self.assertEqual(testcase.result, TestCase.RESULT_FAIL) else: self.fail("Unrecognised testcase name") self.factory.cleanup()
def test_pipelinestore(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), user) result_sample = { 'results': { 'test-runscript-overlay': OrderedDict([ ('success', 'c66c77b2-bc32-4cec-bc6d-477712da7eb6'), ('filename', '/tmp/tmp9ICoFn/lava-device/tests/2_singlenode-advanced/run.sh')]), 'test-install-overlay': OrderedDict([ ('success', 'c66c77b2-bc32-4cec-bc6d-477712da7eb6')]), 'power_off': OrderedDict([('status', 'Complete')]), 'test-overlay': OrderedDict([('success', 'c66c77b2-bc32-4cec-bc6d-477712da7eb6')]), 'git-repo-action': OrderedDict([('success', '6dd3121dc7f2855d710e83fe39c217392e4fb2b4')]), 'lava-test-shell': OrderedDict([ ('linux-linaro-ubuntu-pwd', 'pass'), ('linux-linaro-ubuntu-uname', 'pass'), ('linux-linaro-ubuntu-vmstat', 'pass'), ('linux-linaro-ubuntu-ifconfig', 'pass'), ('linux-linaro-ubuntu-lscpu', 'pass'), ('linux-linaro-ubuntu-lsb_release', 'fail'), ('linux-linaro-ubuntu-netstat', 'pass'), ('linux-linaro-ubuntu-ifconfig-dump', 'pass'), ('linux-linaro-ubuntu-route-dump-a', 'pass'), ('linux-linaro-ubuntu-route-ifconfig-up-lo', 'pass'), ('linux-linaro-ubuntu-route-dump-b', 'pass'), ('linux-linaro-ubuntu-route-ifconfig-up', 'pass'), ('ping-test', 'fail'), ('realpath-check', 'fail'), ('ntpdate-check', 'pass'), ('curl-ftp', 'pass'), ('tar-tgz', 'pass'), ('remove-tgz', 'pass')])} } ret = map_scanned_results(scanned_dict=result_sample, job=job) self.assertTrue(ret) self.assertIsNot([], TestCase.objects.all()) self.assertIsNot([], TestCase.objects.all()) val = URLValidator() for testcase in TestCase.objects.all(): self.assertIsNotNone(testcase.name) self.assertIsNotNone(testcase.result) if testcase.test_set: val('http://localhost/%s' % testcase.get_absolute_url()) self.assertIsNotNone(TestCase.objects.filter(name='ping-test')) self.factory.cleanup()
def test_repositories(self): # pylint: disable=too-many-locals job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) device = Device.objects.get(hostname='fakeqemu1') device_config = device.load_device_configuration(job_ctx, system=False) # raw dict parser = JobParser() obj = PipelineDevice(device_config, device.hostname) pipeline_job = parser.parse(job.definition, obj, job.id, None, "", output_dir='/tmp') allow_missing_path(pipeline_job.pipeline.validate_actions, self, 'qemu-system-x86_64') pipeline = pipeline_job.describe() device_values = _get_device_metadata(pipeline['device']) self.assertEqual( device_values, {'target.device_type': 'qemu'} ) del pipeline['device']['device_type'] self.assertNotIn('device_type', pipeline['device']) device_values = _get_device_metadata(pipeline['device']) try: testdata, _ = TestData.objects.get_or_create(testjob=job) except (MultipleObjectsReturned): self.fail('multiple objects') for key, value in device_values.items(): if not key or not value: continue testdata.attributes.create(name=key, value=value) retval = _get_job_metadata(pipeline['job']['actions']) if 'lava-server-version' in retval: del retval['lava-server-version'] self.assertEqual( retval, { 'test.1.common.definition.from': 'git', 'test.0.common.definition.repository': 'git://git.linaro.org/qa/test-definitions.git', 'test.0.common.definition.name': 'smoke-tests', 'test.1.common.definition.repository': 'http://git.linaro.org/lava-team/lava-functional-tests.git', 'boot.0.common.method': 'qemu', 'test.1.common.definition.name': 'singlenode-advanced', 'test.0.common.definition.from': 'git', 'test.0.common.definition.path': 'ubuntu/smoke-tests-basic.yaml', 'test.1.common.definition.path': 'lava-test-shell/single-node/singlenode03.yaml'} )
def test_level_input(self): job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) suite = TestSuite.objects.create( job=job, name='lava' ) suite.save() ret = map_scanned_results( results={"case": "test-overlay", "definition": "lava", "duration": 0.01159811019897461, "level": "1.3.3.2", "result": "pass"}, job=job) self.assertTrue(ret) self.assertEqual(1, TestCase.objects.filter(suite=suite).count()) testcase = TestCase.objects.get(suite=suite) self.assertTrue(type(testcase.metadata) in [str, unicode]) self.assertEqual(testcase.result, TestCase.RESULT_PASS) self.factory.cleanup()
def test_job_multi(self): MetaType.objects.all().delete() multi_test_file = os.path.join(os.path.dirname(__file__), 'multi-test.yaml') self.assertTrue(os.path.exists(multi_test_file)) with open(multi_test_file, 'r') as test_support: data = test_support.read() job = TestJob.from_yaml_and_user(data, self.user) job_def = yaml.load(job.definition) job_ctx = job_def.get('context', {}) device = Device.objects.get(hostname='fakeqemu1') device_config = device.load_device_configuration(job_ctx, system=False) # raw dict parser = JobParser() obj = PipelineDevice(device_config, device.hostname) pipeline_job = parser.parse(job.definition, obj, job.id, None, "", output_dir='/tmp') allow_missing_path(pipeline_job.pipeline.validate_actions, self, 'qemu-system-x86_64') pipeline = pipeline_job.describe() map_metadata(yaml.dump(pipeline), job)
def test_name(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user(self.factory.make_job_yaml(), user) result_sample = """ - results: !!python/object/apply:collections.OrderedDict - - [linux-linaro-ubuntu-pwd, pass] - [linux-linaro-ubuntu-uname, pass] - [linux-linaro-ubuntu-vmstat, pass] - [linux-linaro-ubuntu-ifconfig, pass] - [linux-linaro-ubuntu-lscpu, pass] - [linux-linaro-ubuntu-lsb_release, pass] - [linux-linaro-ubuntu-netstat, pass] - [linux-linaro-ubuntu-ifconfig-dump, pass] - [linux-linaro-ubuntu-route-dump-a, pass] - [linux-linaro-ubuntu-route-ifconfig-up-lo, pass] - [linux-linaro-ubuntu-route-dump-b, pass] - [linux-linaro-ubuntu-route-ifconfig-up, pass] - [ping-test, fail] - [realpath-check, fail] - [ntpdate-check, pass] - [curl-ftp, pass] - [tar-tgz, pass] - [remove-tgz, pass] """ scanned = yaml.load(result_sample)[0] suite = TestSuite.objects.create(job=job, name='test-suite') suite.save() for testcase, result in scanned['results'].items(): TestCase.objects.create(name=testcase, suite=suite, result=TestCase.RESULT_MAP[result]).save() self.assertIsNot([], TestCase.objects.all()) for testcase in TestCase.objects.all(): self.assertEqual(testcase.suite, suite) self.assertIsNotNone(testcase.name) self.assertIsNotNone(testcase.result) self.factory.cleanup()
def test_metastore(self): field = TestCase._meta.get_field('metadata') level = '1.3.5.1' # artificially inflate results to represent a set of kernel messages results = { 'definition': 'lava', 'case': 'unit-test', 'level': level, # list of numbers, generates a much longer YAML string than just the count 'extra': range(int(field.max_length / 2)), 'result': 'pass' } stub = "%s-%s-%s.yaml" % (results['definition'], results['case'], level) job = TestJob.from_yaml_and_user( self.factory.make_job_yaml(), self.user) meta_filename = os.path.join(job.output_dir, 'metadata', stub) filename = "%s/job-%s/pipeline/%s/%s-%s.yaml" % (job.output_dir, job.id, level.split('.')[0], level, results['definition']) mkdir(os.path.dirname(filename)) if os.path.exists(meta_filename): # isolate from other unit tests os.unlink(meta_filename) self.assertEqual(meta_filename, create_metadata_store(results, job)) ret = map_scanned_results(results, job, meta_filename) self.assertIsNotNone(ret) ret.save() self.assertEqual(TestCase.objects.filter(name='unit-test').count(), 1) test_data = yaml.load(TestCase.objects.filter(name='unit-test')[0].metadata, Loader=yaml.CLoader) self.assertEqual(test_data['extra'], meta_filename) self.assertTrue(os.path.exists(meta_filename)) with open(test_data['extra'], 'r') as extra_file: data = yaml.load(extra_file, Loader=yaml.CLoader) self.assertIsNotNone(data) os.unlink(meta_filename) shutil.rmtree(job.output_dir)
def test_level_input(self): job = TestJob.from_yaml_and_user(self.factory.make_job_yaml(), self.user) suite = TestSuite.objects.create(job=job, name="lava") suite.save() ret = map_scanned_results( results={ "case": "test-overlay", "definition": "lava", "duration": 0.01159811019897461, "level": "1.3.3.2", "result": "pass", }, job=job, markers={}, meta_filename=None, ) self.assertTrue(ret) ret.save() self.assertEqual(1, TestCase.objects.filter(suite=suite).count()) testcase = TestCase.objects.get(suite=suite) self.assertTrue(isinstance(testcase.metadata, string_types)) self.assertEqual(testcase.result, TestCase.RESULT_PASS) self.factory.cleanup()
def test_group_visibility(self): self.factory.cleanup() dt = self.factory.make_device_type(name='name') device = self.factory.make_device(device_type=dt, hostname='name-1') device.save() definition = self.factory.make_job_data() definition['visibility'] = {'group': ['newgroup']} definition['job_name'] = 'unittest_visibility' self.assertIsNotNone(yaml.safe_dump(definition)) self.assertIsNotNone(list(Device.objects.filter(device_type=dt))) user = self.factory.make_user() user.user_permissions.add( Permission.objects.get(codename='add_testjob')) user.save() self.assertRaises(SubmissionException, TestJob.from_yaml_and_user, yaml.safe_dump(definition), user) self.factory.make_group('newgroup') known_groups = list(Group.objects.filter(name__in=['newgroup'])) job = TestJob.from_yaml_and_user(yaml.safe_dump(definition), user) job.refresh_from_db() self.assertEqual(user, job.submitter) self.assertEqual(job.visibility, TestJob.VISIBLE_GROUP) self.assertEqual(known_groups, list(job.viewing_groups.all())) self.factory.cleanup()
def test_parameter_support(self): data = self.factory.make_job_data() test_block = [block for block in data["actions"] if "test" in block][0] smoke = test_block["test"]["definitions"][0] smoke["parameters"] = { "VARIABLE_NAME_1": "first variable value", "VARIABLE_NAME_2": "second value", } job = TestJob.from_yaml_and_user(yaml_dump(data), self.user) job_def = yaml_safe_load(job.definition) job_ctx = job_def.get("context", {}) job_ctx.update( {"no_kvm": True}) # override to allow unit tests on all types of systems device = Device.objects.get(hostname="fakeqemu1") device_config = device.load_configuration(job_ctx) # raw dict parser = JobParser() obj = PipelineDevice(device_config) pipeline_job = parser.parse(job.definition, obj, job.id, None, "") allow_missing_path(pipeline_job.pipeline.validate_actions, self, "qemu-system-x86_64") pipeline = pipeline_job.describe() testdata, _ = TestData.objects.get_or_create(testjob=job) retval = _get_action_metadata(pipeline["job"]["actions"]) self.assertIn("test.0.common.definition.parameters.VARIABLE_NAME_2", retval) self.assertIn("test.0.common.definition.parameters.VARIABLE_NAME_1", retval) self.assertEqual( retval["test.0.common.definition.parameters.VARIABLE_NAME_1"], "first variable value", ) self.assertEqual( retval["test.0.common.definition.parameters.VARIABLE_NAME_2"], "second value", )
def test_preserve_comments(self): """ TestJob.original_definition must preserve comments, if supplied. """ definition = self.factory.make_job_data_from_file( 'qemu-pipeline-first-job.yaml') for line in definition: if line.startswith('#'): break self.fail('Comments have not been preserved') dt = self.factory.make_device_type(name='qemu') device = self.factory.make_device(device_type=dt, hostname='qemu-1') device.save() user = self.factory.make_user() user.user_permissions.add( Permission.objects.get(codename='add_testjob')) user.save() job = TestJob.from_yaml_and_user(definition, user) job.refresh_from_db() self.assertEqual(user, job.submitter) for line in job.original_definition: if line.startswith('#'): break self.fail('Comments have not been preserved after submission')
def submit_job(self, job_data): """ Name ---- `submit_job` (`job_data`) Description ----------- Submit the given job data which is in LAVA job JSON format as a new job to LAVA scheduler. Arguments --------- `job_data`: string Job JSON string. Return value ------------ This function returns an XML-RPC integer which is the newly created job's id, provided the user is authenticated with an username and token. """ self._authenticate() if not self.user.has_perm('lava_scheduler_app.add_testjob'): raise xmlrpclib.Fault( 403, "Permission denied. User %r does not have the " "'lava_scheduler_app.add_testjob' permission. Contact " "the administrators." % self.user.username) is_json = True is_yaml = False try: json.loads(job_data) except (AttributeError, JSONDecodeError, ValueError) as exc: is_json = False try: # only try YAML if this is not JSON # YAML can parse JSON as YAML, JSON cannot parse YAML at all yaml_data = yaml.load(job_data) except yaml.YAMLError as exc: # neither yaml nor json loaders were able to process the submission. raise xmlrpclib.Fault( 400, "Loading job submission failed: %s." % exc) # validate against the submission schema. is_yaml = validate_submission( yaml_data) # raises SubmissionException if invalid. try: if is_json: job = TestJob.from_json_and_user(job_data, self.user) elif is_yaml: job = TestJob.from_yaml_and_user(job_data, self.user) else: raise xmlrpclib.Fault( 400, "Unable to determine whether job is JSON or YAML.") except (JSONDataError, JSONDecodeError, ValueError) as exc: raise xmlrpclib.Fault(400, "Decoding job submission failed: %s." % exc) except Device.DoesNotExist: raise xmlrpclib.Fault(404, "Specified device not found.") except DeviceType.DoesNotExist: raise xmlrpclib.Fault(404, "Specified device type not found.") except DevicesUnavailableException as e: raise xmlrpclib.Fault(400, str(e)) if isinstance(job, type(list())): return [j.sub_id for j in job] else: return job.id
def test_differing_vlan_tags(self): """ More devices of the requested type than needed by the test with some devices having unsuitable vland interface tags. """ x86 = self.factory.make_device_type('x86') x86_1 = self.factory.make_device(device_type=x86, hostname='x86-1') device_dict = DeviceDictionary(hostname=x86_1.hostname) device_dict.parameters = { # client, RJ45 10M 100M with separate 10G 'extends': 'x86.jinja2', 'interfaces': ['eth0', 'eth1', 'eth2'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1", 'eth2': "/sys/devices/pci0000:00/0000:00:1c.2/0000:04:00.0/net/eth2" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:c0:8b", 'eth2': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M', '1G'], 'eth2': ['SFP+', '10G'] }, 'map': { 'eth0': { '192.168.0.2': 5 }, 'eth1': { '192.168.0.2': 7 }, 'eth2': { '192.168.0.2': 12 } } } device_dict.save() x86_2 = self.factory.make_device(device_type=x86, hostname='x86-2') device_dict = DeviceDictionary(hostname=x86_2.hostname) device_dict.parameters = { # client, RJ45 10M 100M with separate 10G 'extends': 'x86.jinja2', 'interfaces': ['eth0', 'eth1', 'eth2'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1", 'eth2': "/sys/devices/pci0000:00/0000:00:1c.2/0000:04:00.0/net/eth2" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:d0:8b", 'eth2': "00:24:d7:9b:d0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M', '1G'], 'eth2': ['SFP+', '10G'] }, 'map': { 'eth0': { '192.168.0.2': 14 }, 'eth1': { '192.168.0.2': 17 }, 'eth2': { '192.168.0.2': 22 } } } device_dict.save() x86_3 = self.factory.make_device(device_type=x86, hostname='x86-3') device_dict = DeviceDictionary(hostname=x86_3.hostname) device_dict.parameters = { # client, 40G, not 10G 'extends': 'x86.jinja2', 'interfaces': ['eth0', 'eth1', 'eth2'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1", 'eth2': "/sys/devices/pci0000:00/0000:00:1c.2/0000:04:00.0/net/eth2" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:d0:8b", 'eth2': "00:24:d7:9b:d0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M', '1G'], 'eth2': ['QSFP+', '40G'] }, 'map': { 'eth0': { '192.168.0.2': 15 }, 'eth1': { '192.168.0.2': 16 }, 'eth2': { '192.168.0.2': 20 } } } device_dict.save() x86_4 = self.factory.make_device(device_type=x86, hostname='x86-4') device_dict = DeviceDictionary(hostname=x86_4.hostname) device_dict.parameters = { # client, RJ45 10M 100M with separate 10G 'extends': 'x86.jinja2', 'interfaces': ['eth0', 'eth1', 'eth2'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1", 'eth2': "/sys/devices/pci0000:00/0000:00:1c.2/0000:04:00.0/net/eth2" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:d0:8b", 'eth2': "00:24:d7:9b:d0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M', '1G'], 'eth2': ['SFP+', '10G'] }, 'map': { 'eth0': { '192.168.0.2': 14 }, 'eth1': { '192.168.0.2': 17 }, 'eth2': { '192.168.0.2': 22 } } } device_dict.save() devices = list( Device.objects.filter(status=Device.IDLE).order_by('is_public')) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'sample_jobs', 'x86-vlan.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertIsNotNone(device) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue( match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() # reset state, pretend the assigned jobs have completed. for job in TestJob.objects.all(): job.status = TestJob.COMPLETE job.actual_device.status = Device.IDLE job.actual_device.current_job = None job.actual_device.save(update_fields=['status', 'current_job']) job.save(update_fields=['status']) # take x86_1 offline, forcing the idle list to include x86_3 for evaluation x86_1.status = Device.OFFLINE x86_1.save(update_fields=['status']) x86_1.refresh_from_db() devices = list( Device.objects.filter(status=Device.IDLE).order_by('is_public')) self.assertNotIn(x86_1, devices) self.assertIn(x86_2, devices) self.assertIn(x86_3, devices) self.assertIn(x86_4, devices) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'sample_jobs', 'x86-vlan.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertIsNotNone(device) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue( match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() x86_1.refresh_from_db() x86_2.refresh_from_db() x86_3.refresh_from_db() x86_4.refresh_from_db() self.assertEqual(Device.STATUS_CHOICES[Device.OFFLINE], Device.STATUS_CHOICES[x86_1.status]) self.assertEqual(Device.STATUS_CHOICES[Device.RESERVED], Device.STATUS_CHOICES[x86_2.status]) self.assertEqual(Device.STATUS_CHOICES[Device.IDLE], Device.STATUS_CHOICES[x86_3.status]) self.assertEqual(Device.STATUS_CHOICES[Device.RESERVED], Device.STATUS_CHOICES[x86_4.status])
def testjob_submission(job_definition, user, check_device=None): """ Single submission frontend for JSON or YAML :param job_definition: string of the job submission :param user: user attempting the submission :param check_device: set specified device as the target **and** thereby set job as a health check job. (JSON only) :return: a job or a list of jobs :raises: SubmissionException, Device.DoesNotExist, DeviceType.DoesNotExist, DevicesUnavailableException, JSONDataError, JSONDecodeError, ValueError """ if is_deprecated_json(job_definition): allow_health = False job_json = simplejson.loads(job_definition) target_device = None if 'target' in job_json: target_device = Device.objects.get(hostname=job_json['target']) if check_device: job_json['target'] = check_device.hostname job_json['health-check'] = True job_definition = simplejson.dumps(job_json) allow_health = True try: # returns a single job or a list (not a QuerySet) of job objects. job = TestJob.from_json_and_user(job_definition, user, health_check=allow_health) if isinstance(job, list): # multinode health checks not supported return job job.health_check = allow_health if check_device: job.requested_device = check_device elif target_device: job.requested_device = target_device job.save(update_fields=['health_check', 'requested_device']) except (JSONDataError, ValueError) as exc: if check_device: check_device.put_into_maintenance_mode( user, "Job submission failed for health job for %s: %s" % (check_device, exc)) raise JSONDataError( "Health check job submission failed for %s: %s" % (check_device, exc)) else: raise JSONDataError("Job submission failed: %s" % exc) else: validate_job(job_definition) # returns a single job or a list (not a QuerySet) of job objects. job = TestJob.from_yaml_and_user(job_definition, user) if check_device and isinstance(check_device, Device) and not isinstance(job, list): # the slave must neither know nor care if this is a health check, # only the master cares and that has the database connection. job.health_check = True job.requested_device = check_device job.save(update_fields=['health_check', 'requested_device']) return job
def test_no_tags(self): self.factory.make_device(self.device_type, 'fakeqemu3') TestJob.from_yaml_and_user(self.factory.make_job_json(), self.factory.make_user())
def test_same_type_devices_with_map(self): device_dict = DeviceDictionary(hostname=self.factory.bbb1.hostname) device_dict.parameters = { # client, RJ45 10M 100M 'extends': 'beaglebone-black.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:22", 'eth1': "00:24:d7:9b:c0:8b" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M'] }, 'map': { 'eth0': { '192.168.0.2': 5 }, 'eth1': { '192.168.0.2': 7 } } } device_dict.save() bbb2 = self.factory.make_device(self.factory.bbb_type, hostname='bbb2') device_dict = DeviceDictionary(hostname=bbb2.hostname) device_dict.parameters = { # server, RJ45 10M 100M 'extends': 'beaglebone-black.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M'] }, 'map': { 'eth0': { '192.168.0.2': 7 }, 'eth1': { '192.168.0.2': 9 } } } device_dict.save() devices = list( Device.objects.filter(status=Device.IDLE).order_by('is_public')) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'bbb-bbb-vland-group.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue( match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() self.factory.bbb1.refresh_from_db() bbb2.refresh_from_db() self.assertIsNotNone(self.factory.bbb1.current_job) self.assertIsNotNone(bbb2.current_job) self.assertIsNotNone(self.factory.bbb1.current_job.actual_device) self.assertIsNotNone(bbb2.current_job.actual_device) self.assertNotEqual(self.factory.bbb1.current_job, bbb2.current_job) self.assertNotEqual(self.factory.bbb1.current_job.actual_device, bbb2.current_job.actual_device)
def test_differing_vlan_tags(self): """ More devices of the requested type than needed by the test with some devices having unsuitable vland interface tags. """ x86 = self.factory.make_device_type('x86') x86_1 = self.factory.make_device(device_type=x86, hostname='x86-01') x86_2 = self.factory.make_device(device_type=x86, hostname='x86-02') x86_3 = self.factory.make_device(device_type=x86, hostname='x86-03') x86_4 = self.factory.make_device(device_type=x86, hostname='x86-04') devices = list(Device.objects.filter(status=Device.IDLE).order_by('is_public')) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'sample_jobs', 'x86-vlan.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertIsNotNone(device) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue(match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() # reset state, pretend the assigned jobs have completed. for job in TestJob.objects.all(): job.status = TestJob.COMPLETE job.actual_device.status = Device.IDLE job.actual_device.current_job = None job.actual_device.save(update_fields=['status', 'current_job']) job.save(update_fields=['status']) # take x86_1 offline, forcing the idle list to include x86_3 for evaluation x86_1.status = Device.OFFLINE x86_1.save(update_fields=['status']) x86_1.refresh_from_db() devices = list(Device.objects.filter(status=Device.IDLE).order_by('is_public')) self.assertNotIn(x86_1, devices) self.assertIn(x86_2, devices) self.assertIn(x86_3, devices) self.assertIn(x86_4, devices) user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'sample_jobs', 'x86-vlan.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertIsNotNone(device) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue(match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device if device in devices: devices.remove(device) assign_jobs() x86_1.refresh_from_db() x86_2.refresh_from_db() x86_3.refresh_from_db() x86_4.refresh_from_db() self.assertEqual(Device.STATUS_CHOICES[Device.OFFLINE], Device.STATUS_CHOICES[x86_1.status]) self.assertEqual(Device.STATUS_CHOICES[Device.RESERVED], Device.STATUS_CHOICES[x86_2.status]) self.assertEqual(Device.STATUS_CHOICES[Device.IDLE], Device.STATUS_CHOICES[x86_3.status]) self.assertEqual(Device.STATUS_CHOICES[Device.RESERVED], Device.STATUS_CHOICES[x86_4.status])
def test_match_devices_with_map(self): devices = Device.objects.filter( status=Device.IDLE).order_by('is_public') device_dict = DeviceDictionary(hostname=self.factory.bbb1.hostname) device_dict.parameters = { # client, RJ45 10M only 'extends': 'beaglebone-black.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M'] }, 'map': { 'eth0': { '192.168.0.2': 5 }, 'eth1': { '192.168.0.2': 7 } } } device_dict.save() device_dict = DeviceDictionary(hostname=self.factory.cubie1.hostname) device_dict.parameters = { # server includes 100M 'extends': 'cubietruck.jinja2', 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M', '100M'] }, 'map': { 'eth0': { '192.168.0.2': 4 }, 'eth1': { '192.168.0.2': 6 } } } device_dict.save() user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'bbb-cubie-vlan-group.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) del (data['protocols']['lava-multinode']['roles']['client']['tags']) del (data['protocols']['lava-multinode']['roles']['server']['tags']) interfaces = [] job_dict = split_multinode_yaml(data, 'abcdefg123456789') client_job = job_dict['client'][0] device_dict = DeviceDictionary.get( self.factory.bbb1.hostname).to_dict() self.assertIsNotNone(device_dict) tag_list = client_job['protocols']['lava-vland']['vlan_one']['tags'] for interface, tags in device_dict['parameters']['tags'].iteritems(): if set(tags) & set(tag_list) == set( tag_list) and interface not in interfaces: interfaces.append(interface) break self.assertEqual(['eth1'], interfaces) self.assertEqual(len(interfaces), len(client_job['protocols']['lava-vland'].keys())) interfaces = [] server_job = job_dict['server'][0] device_dict = DeviceDictionary.get( self.factory.cubie1.hostname).to_dict() self.assertIsNotNone(device_dict) tag_list = server_job['protocols']['lava-vland']['vlan_two']['tags'] for interface, tags in device_dict['parameters']['tags'].iteritems(): if set(tags) & set(tag_list) == set( tag_list) and interface not in interfaces: interfaces.append(interface) break self.assertEqual(['eth1'], interfaces) self.assertEqual(len(interfaces), len(client_job['protocols']['lava-vland'].keys())) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) # vlan_one: client role. RJ45 10M. bbb device type # vlan_two: server role. RJ45 100M. cubie device type. assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue( match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device self.assertEqual(assignments['client'].hostname, self.factory.bbb1.hostname) self.assertEqual(assignments['server'].hostname, self.factory.cubie1.hostname)
def test_make_ssh_guest_yaml(self): hostname = "fakeqemu3" device = self.factory.make_device(self.device_type, hostname) try: jobs = TestJob.from_yaml_and_user(self.factory.make_job_yaml(), self.factory.make_user()) except DevicesUnavailableException as exc: self.fail(exc) sub_id = [] group_size = 0 path = os.path.join(os.path.dirname(os.path.join(__file__)), "sample_jobs") host_role = [] for job in jobs: data = yaml.safe_load(job.definition) params = data["protocols"]["lava-multinode"] params["target_group"] = "replaced" if not group_size: group_size = params["group_size"] if job.device_role == "host": self.assertFalse(job.dynamic_connection) self.assertEqual(job.requested_device_type.name, device.device_type.name) self.assertIn(params["sub_id"], [0, 1, 2]) sub_id.append(params["sub_id"]) comparison = yaml.safe_load( open(os.path.join(path, "qemu-ssh-parent.yaml"), "r").read()) self.assertIn("protocols", data) self.assertIn("lava-multinode", data["protocols"]) self.assertIn("sub_id", data["protocols"]["lava-multinode"]) del (comparison["protocols"]["lava-multinode"]["sub_id"]) del (data["protocols"]["lava-multinode"]["sub_id"]) self.assertEqual(data, comparison) self.assertEqual(job.device_role, "host") host_role.append(job.device_role) else: self.assertTrue(job.dynamic_connection) self.assertNotIn(sub_id, params["sub_id"]) sub_id.append(params["sub_id"]) self.assertIsNone(job.requested_device_type) deploy = [ action for action in data["actions"] if "deploy" in action ][0] self.assertEqual(deploy["deploy"]["connection"], "ssh") # validate each job del (data["protocols"]["lava-multinode"]["sub_id"]) self.assertEqual( data, yaml.safe_load( open(os.path.join(path, "qemu-ssh-guest-1.yaml"), "r").read()), ) self.assertIsNone(job.requested_device_type) self.assertIsNone(job.actual_device) host_role.append(data["host_role"]) self.assertFalse(any(role for role in host_role if role != "host")) self.assertEqual(len(sub_id), group_size) self.assertEqual(sub_id, list(range(group_size)))
def setUp(self): super().setUp() self.admin_user = User.objects.create( username=self.factory.get_unique_user(), is_superuser=True ) # set up auth data. self.group1 = self.factory.make_group(name="test-group1") self.group2 = self.factory.make_group(name="test-group2") self.user1 = self.factory.make_user() self.user1.groups.add(self.group1) self.user2 = self.factory.make_user() self.user2.groups.add(self.group2) self.user3 = self.factory.make_user() # Create device types. self.qemu_device_type = self.factory.make_device_type(name="qemu") self.bbb_device_type = self.factory.make_device_type(name="bbb") self.lxc_device_type = self.factory.make_device_type(name="lxc") self.all_device_types = [ self.qemu_device_type, self.bbb_device_type, self.lxc_device_type, ] # Create devices. self.qemu_device1 = self.factory.make_device( device_type=self.qemu_device_type, hostname="qemu-1" ) self.qemu_device2 = self.factory.make_device( device_type=self.qemu_device_type, hostname="qemu-2" ) self.qemu_device3 = self.factory.make_device( device_type=self.qemu_device_type, hostname="qemu-3" ) self.all_qemu_devices = [ self.qemu_device1, self.qemu_device2, self.qemu_device3, ] self.bbb_device1 = self.factory.make_device( device_type=self.bbb_device_type, hostname="bbb-1" ) self.bbb_device2 = self.factory.make_device( device_type=self.bbb_device_type, hostname="bbb-2" ) self.all_bbb_devices = [self.bbb_device1, self.bbb_device2] self.all_devices = self.all_qemu_devices + self.all_bbb_devices self.definition = self.factory.make_job_data_from_file("qemu.yaml") # Create testjobs. self.qemu_job1 = TestJob.from_yaml_and_user(self.definition, self.admin_user) self.qemu_job2 = TestJob.from_yaml_and_user(self.definition, self.admin_user) self.all_bbb_jobs = TestJob.from_yaml_and_user( self.factory.make_job_data_from_file("bbb-bbb-vland-group.yaml"), self.admin_user, ) self.bbb_job1 = self.all_bbb_jobs[0] self.bbb_job2 = self.all_bbb_jobs[1] self.all_qemu_jobs = [self.qemu_job1, self.qemu_job2] self.all_jobs = self.all_qemu_jobs + self.all_bbb_jobs
def test_from_yaml_and_user_sets_submitter(self): user = self.factory.make_user() job = TestJob.from_yaml_and_user(self.factory.make_job_json(), user) self.assertEqual(user, job.submitter)
def test_from_yaml_and_user_sets_definition(self): definition = self.factory.make_job_json() job = TestJob.from_yaml_and_user(definition, self.factory.make_user()) self.assertEqual(definition, job.definition)
def test_make_ssh_guest_yaml(self): hostname = 'fakeqemu3' device = self.factory.make_device(self.device_type, hostname) try: jobs = TestJob.from_yaml_and_user(self.factory.make_job_json(), self.factory.make_user()) except DevicesUnavailableException as exc: self.fail(exc) sub_id = [] group_size = 0 path = os.path.join(os.path.dirname(os.path.join(__file__)), 'devices') host_role = [] for job in jobs: data = yaml.load(job.definition) params = data['protocols']['lava-multinode'] params['target_group'] = 'replaced' if not group_size: group_size = params['group_size'] if job.sub_id.endswith('.0'): self.assertFalse(job.dynamic_connection) self.assertEqual(job.requested_device_type.name, device.device_type.name) self.assertEqual(params['sub_id'], 0) sub_id.append(params['sub_id']) self.assertEqual( data, yaml.load( open(os.path.join(path, 'qemu-ssh-parent.yaml'), 'r').read())) self.assertEqual(job.device_role, 'host') host_role.append(job.device_role) else: self.assertTrue(job.dynamic_connection) self.assertNotIn(sub_id, params['sub_id']) sub_id.append(params['sub_id']) self.assertIsNone(job.requested_device_type) deploy = [ action for action in data['actions'] if 'deploy' in action ][0] self.assertEqual(deploy['deploy']['connection'], 'ssh') # validate each job if params['sub_id'] == 1: self.assertEqual( data, yaml.load( open(os.path.join(path, 'qemu-ssh-guest-1.yaml'), 'r').read())) elif params['sub_id'] == 2: self.assertEqual( data, yaml.load( open(os.path.join(path, 'qemu-ssh-guest-2.yaml'), 'r').read())) else: self.fail("Unexpected sub_id parameter") self.assertIsNone(job.requested_device_type) self.assertIsNone(job.actual_device) self.assertIsNone(job.requested_device) host_role.append(data['host_role']) self.assertFalse(any(role for role in host_role if role != 'host')) self.assertEqual(len(sub_id), group_size) self.assertEqual(sub_id, range(0, group_size))
def test_match_devices_with_map_and_tags(self): devices = Device.objects.filter( status=Device.IDLE).order_by('is_public') self.factory.ensure_tag('usb-eth') self.factory.ensure_tag('sata') self.factory.bbb1.tags = Tag.objects.filter(name='usb-eth') self.factory.bbb1.save() self.factory.cubie1.tags = Tag.objects.filter(name='sata') self.factory.cubie1.save() device_dict = DeviceDictionary(hostname=self.factory.bbb1.hostname) device_dict.parameters = { 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '10M'] }, 'map': { 'eth0': { '192.168.0.2': 5 }, 'eth1': { '192.168.0.2': 7 } } } device_dict.save() device_dict = DeviceDictionary(hostname=self.factory.cubie1.hostname) device_dict.parameters = { 'interfaces': ['eth0', 'eth1'], 'sysfs': { 'eth0': "/sys/devices/pci0000:00/0000:00:19.0/net/eth0", 'eth1': "/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/eth1" }, 'mac_addr': { 'eth0': "f0:de:f1:46:8c:21", 'eth1': "00:24:d7:9b:c0:8c" }, 'tags': { 'eth0': [], 'eth1': ['RJ45', '100M'] }, 'map': { 'eth0': { '192.168.0.2': 4 }, 'eth1': { '192.168.0.2': 6 } } } device_dict.save() user = self.factory.make_user() sample_job_file = os.path.join(os.path.dirname(__file__), 'bbb-cubie-vlan-group.yaml') with open(sample_job_file, 'r') as test_support: data = yaml.load(test_support) vlan_job = TestJob.from_yaml_and_user(yaml.dump(data), user) assignments = {} for job in vlan_job: device = find_device_for_job(job, devices) self.assertEqual(device.device_type, job.requested_device_type) # map has been defined self.assertTrue( match_vlan_interface(device, yaml.load(job.definition))) assignments[job.device_role] = device self.assertEqual(assignments['client'].hostname, self.factory.bbb1.hostname) self.assertEqual(assignments['server'].hostname, self.factory.cubie1.hostname)