1
0
mirror of https://pagure.io/fedora-qa/os-autoinst-distri-fedora.git synced 2024-09-30 17:47:23 +00:00

kickstart-tests: handle non-US keyboard layouts

wow, this was a deep dive. A few of the kickstart-tests set the
OS keymap to cz. Long story short, the way openQA does keyboard
input is via VNC; VNC keyboard events are for keysyms, which
are layout-dependent (so when we say `send_key "a"`, openQA
sends qemu the keysym for 'a', regardless of layout issues).
qemu turns the keysym into a keycode and sends *that* to the
guest OS, which turns it back into a keysym according to its
keymap config.

qemu uses a US map by default to turn the keysym it receives via
VNC into a keycode. So if you send qemu a '-', it sends the guest
OS the keycode that would be converted to the keysym for '-' *in
the US layout*. But if the guest OS has its layout set to cz,
the same keycode is converted to '=', since that's what the key
with that keycode is labelled as on a Czech keyboard.

That's why if you set the guest OS keymap to cz and tell openQA
to type '-', you get '='.

However! You can tell qemu to use a different keymap for its
keysym -> keycode conversion. If you keep the qemu keymap and
the guest OS keymap lined up, when you say `send_key "-"`, you
should actually *get* a "-". Which is what we want, for these
tests. So we tell openQA to tell qemu to do that, using the
VNCKB var, which is for precisely this: it tells os-autoinst
to run qemu with `-k (layout)`.
This commit is contained in:
Adam Williamson 2016-04-04 14:21:06 -07:00
parent dd247c7737
commit d33f624c20

View File

@ -47,11 +47,13 @@ def prep_kickstarts(indir, outdir):
if not tests: if not tests:
raise ValueError("No tests found!") raise ValueError("No tests found!")
for test in tests: for test in tests:
# read in the .ks.in file
with open('{0}/{1}.ks.in'.format(indir, test), 'r') as ksinfh: with open('{0}/{1}.ks.in'.format(indir, test), 'r') as ksinfh:
kstext = ksinfh.read() kstext = ksinfh.read()
ksout = "{0}.ks".format(test)
for (orig, repl) in SUBSTS: for (orig, repl) in SUBSTS:
kstext = kstext.replace(orig, repl) kstext = kstext.replace(orig, repl)
# write out the processed .ks
ksout = "{0}.ks".format(test)
with open('{0}/{1}'.format(outdir, ksout), 'w') as ksoutfh: with open('{0}/{1}'.format(outdir, ksout), 'w') as ksoutfh:
ksoutfh.write(kstext) ksoutfh.write(kstext)
@ -73,8 +75,8 @@ def merge_templates(indir, baseurl, tempfile, outfile):
outfh.write(json.dumps(templates, sort_keys=True, indent=4, outfh.write(json.dumps(templates, sort_keys=True, indent=4,
separators=(',', ': '))) separators=(',', ': ')))
def _get_disk_settings(test): def _get_settings_disk(sh):
"""Given text of a kickstart_tests test (.sh file) as test, return """Given text of a kickstart_tests test (.sh file) as sh, return
a list of appropriate openQA settings dicts for hard disks. a list of appropriate openQA settings dicts for hard disks.
""" """
# most prepare_disks just create empty disks of a given size in # most prepare_disks just create empty disks of a given size in
@ -85,11 +87,11 @@ def _get_disk_settings(test):
# create multiple clean disk images with different sizes. # create multiple clean disk images with different sizes.
settings = [] settings = []
simpre = re.compile(r'qemu-img create -q -f qcow2 \$\{(tmp|disk)dir\}/disk-.\.img \d+G') simpre = re.compile(r'qemu-img create -q -f qcow2 \$\{(tmp|disk)dir\}/disk-.\.img \d+G')
numdisks = len(simpre.findall(test)) numdisks = len(simpre.findall(sh))
# the one tricky case that exists so far is the driverdisk case. # the one tricky case that exists so far is the driverdisk case.
# it gets created elsewhere. here we just point to it. # it gets created elsewhere. here we just point to it.
if 'mkdud.py' in test: if 'mkdud.py' in sh:
numdisks += 1 numdisks += 1
settings.append({'key': 'HDD_{0}'.format(str(numdisks)), 'value': "driverdisk.img"}) settings.append({'key': 'HDD_{0}'.format(str(numdisks)), 'value': "driverdisk.img"})
@ -98,26 +100,65 @@ def _get_disk_settings(test):
return settings return settings
def _get_settings_postinstall(sh):
"""Given text of a kickstart_tests test (.sh file) as sh, return
a list of appropriate openQA settings dict for post-install test
settings (currently a single-item list, but we're future-proofing
here).
"""
# just one test checks for RESULT in /home instead of /root
if '/home/RESULT' in sh:
return [{'key': 'POSTINSTALL', 'value': 'kstest_home'}]
else:
return [{'key': 'POSTINSTALL', 'value': 'kstest_root'}]
def _get_settings_keyboard(ksin):
"""Given text of a kickstart_tests .ks.in file as ksin, return
a list of appropriate openQA settings dict for keyboard test
settings (currently a single-item list, but we're future-proofing
here).
"""
# if the kickstart sets a keyboard layout, we set VNCKB to the
# same layout. This tells openQA to run qemu with '-k (layout)',
# which makes it use that layout for converting keysyms received
# via VNC to keycodes which are passed on to the guest OS, which
# converts them back into keysyms. Basically if we want to set a
# non-US layout in the guest and have 'type_string qwerty' still
# type 'qwerty' and not 'qwertz' or 'azert' or something, this is
# how we do that.
match = re.search(r'--vckeymap (\S+)', ksin)
if match:
return [{'key': 'VNCKB', 'value': match.group(1)}]
else:
return []
def create_testsuite(test, path, baseurl): def create_testsuite(test, path, baseurl):
"""Create an openQA 'test suite' for a given kickstart_test. test """Create an openQA 'test suite' for a given kickstart_test. test
is the test name, path is the directory the test files are in. is the test name, path is the directory the test files are in.
""" """
with open('{0}/{1}.sh'.format(path, test), 'r') as testfh: # duh. these are all kickstart tests.
sh = testfh.read() settings = [{'key': 'KICKSTART', 'value': '1'}]
with open('{0}/{1}.ks.in'.format(path, test), 'r') as ksfh:
ks = ksfh.read() # get text of .sh and .ks.in files
settings = _get_disk_settings(sh) with open('{0}/{1}.sh'.format(path, test), 'r') as shfh:
settings.append({'key': 'KICKSTART', 'value': '1'}) sh = shfh.read()
# just one test checks for RESULT in /home instead of /root with open('{0}/{1}.ks.in'.format(path, test), 'r') as ksinfh:
if '/home/RESULT' in sh: ksin = ksinfh.read()
settings.append({'key': 'POSTINSTALL', 'value': 'kstest_home'})
else: # call all the functions for getting different settings.
settings.append({'key': 'POSTINSTALL', 'value': 'kstest_root'}) settings.extend(_get_settings_disk(sh))
# for some goddamn reason there are two different root passwords. settings.extend(_get_settings_postinstall(sh))
rootpatt = re.compile(r'rootpw (.+)') settings.extend(_get_settings_keyboard(ksin))
settings.append({'key': 'ROOT_PASSWORD', 'value': rootpatt.search(ks).group(1)})
# do some very simple ones ourselves (to avoid this function
# growing too much, the rule is that if it needs more than one
# line, split it out).
# root password. for now we assume there is one.
settings.append({'key': 'ROOT_PASSWORD', 'value': re.search(r'rootpw (.+)', ksin).group(1)})
# kickstart URL
settings.append({'key': 'GRUB', 'value': "inst.ks={0}/{1}.ks".format(baseurl.strip('/'), test)}) settings.append({'key': 'GRUB', 'value': "inst.ks={0}/{1}.ks".format(baseurl.strip('/'), test)})
# we never want to do a user login for these. # we never want to do a user login for these
settings.append({'key': 'USER_LOGIN', 'value': "false"}) settings.append({'key': 'USER_LOGIN', 'value': "false"})
return {'name': "kstest_{0}".format(test), 'settings': settings} return {'name': "kstest_{0}".format(test), 'settings': settings}