Simply & unify console handling for libvirt drivers
Currently the libvirt.xml.template file contains the following console definitions <!-- The order is significant here. File must be defined first --> <serial type="file"> <source path='${basepath}/console.log'/> <target port='1'/> </serial> <console type='pty' tty='/dev/pts/2'> <source path='/dev/pts/2'/> <target port='0'/> </console> <serial type='pty'> <source path='/dev/pts/2'/> <target port='0'/> </serial> There are multiple things wrong with this - LXC and Xen guests don't honour the <serial> elements - The <console> element shouldn't have any <source> element of 'tty' attribute set when type=pty, since they are dynamically allocated - The <console> element will ignored if the <serial> element is set and the hypervisor supports this - It doesn't say why multiple serial elements are used or why the order is important. The reason is that the QEMU pty driver throws away data when no client is connected. This means we can't use it as a basis for the persistent log file. Instead we need two separate serial ports, the first of which is used for the logfile In addition in the nova/virt/libvirt/connect.py class the 'get_console_output' method has separate special-case handling for Xen and LXC despite the fact that both work in the same way. All this can be significantly simplified, to unify console handling across all libvirt drivers. First replacing all the existing XML with just #if $type == 'qemu' <serial type='file'> <source path='${basepath}/console.log'/> </serial> <serial type='pty'/> #else <console type='pty'/> #end if This lets Xen/UML/LXC just use a regular PTY based console, while special casing QEMU. It is minimal XML, letting libvirt automatically fill in other attributes In the code, the get_console_output method can remove the explicit checks for Xen/LXC and instead be conditionalized based on what the XML shows. Finally calling out to 'virsh ttyiname' is pointless since nova already has a connection to libvirt which can be used to fetch the XML & extract the TTY path. Change-Id: I6a966df4ea72e07dbc227683c4225670984fc507 Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
17396f6a08
commit
e3f77127fe
@ -357,12 +357,17 @@ class Domain(object):
|
||||
function='0x1'/>
|
||||
</controller>
|
||||
%(nics)s
|
||||
<serial type='pty'>
|
||||
<source pty='/dev/pts/27'/>
|
||||
<serial type='file'>
|
||||
<source path='dummy.log'/>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
<console type='pty'>
|
||||
<target type='serial' port='0'/>
|
||||
<serial type='pty'>
|
||||
<source pty='/dev/pts/27'/>
|
||||
<target port='1'/>
|
||||
</serial>
|
||||
<console type='file'>
|
||||
<source path='dummy.log'/>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
<input type='tablet' bus='usb'/>
|
||||
<input type='mouse' bus='ps2'/>
|
||||
|
@ -860,6 +860,21 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
check = (lambda t: t.find('./os/initrd'), None)
|
||||
check_list.append(check)
|
||||
|
||||
if hypervisor_type in ['qemu', 'kvm']:
|
||||
check = (lambda t: t.findall('./devices/serial')[0].get(
|
||||
'type'), 'file')
|
||||
check_list.append(check)
|
||||
check = (lambda t: t.findall('./devices/serial')[1].get(
|
||||
'type'), 'pty')
|
||||
check_list.append(check)
|
||||
check = (lambda t: t.findall('./devices/serial/source')[0].get(
|
||||
'path').split('/')[1], 'console.log')
|
||||
check_list.append(check)
|
||||
else:
|
||||
check = (lambda t: t.find('./devices/console').get(
|
||||
'type'), 'pty')
|
||||
check_list.append(check)
|
||||
|
||||
parameter = './devices/interface/filterref/parameter'
|
||||
common_checks = [
|
||||
(lambda t: t.find('.').tag, 'domain'),
|
||||
@ -869,8 +884,6 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
(lambda t: t.findall(parameter)[1].get('name'), 'DHCPSERVER'),
|
||||
(lambda t: _ipv4_like(t.findall(parameter)[1].get('value'),
|
||||
'192.168.*.1'), True),
|
||||
(lambda t: t.find('./devices/serial/source').get(
|
||||
'path').split('/')[1], 'console.log'),
|
||||
(lambda t: t.find('./memory').text, '2097152')]
|
||||
if rescue:
|
||||
common_checks += [
|
||||
|
@ -162,21 +162,20 @@
|
||||
#end if
|
||||
|
||||
#end for
|
||||
<!-- The order is significant here. File must be defined first -->
|
||||
<serial type="file">
|
||||
|
||||
#if $type == 'qemu' or $type == 'kvm'
|
||||
<!-- The QEMU 'pty' driver throws away any data if no
|
||||
client app is connected. Thus we can't get away
|
||||
with a single type=pty console. Instead we have
|
||||
to configure two separate consoles. -->
|
||||
<serial type='file'>
|
||||
<source path='${basepath}/console.log'/>
|
||||
<target port='1'/>
|
||||
</serial>
|
||||
<serial type='pty'/>
|
||||
#else
|
||||
<console type='pty'/>
|
||||
#end if
|
||||
|
||||
<console type='pty' tty='/dev/pts/2'>
|
||||
<source path='/dev/pts/2'/>
|
||||
<target port='0'/>
|
||||
</console>
|
||||
|
||||
<serial type='pty'>
|
||||
<source path='/dev/pts/2'/>
|
||||
<target port='0'/>
|
||||
</serial>
|
||||
#if $getVar('use_usb_tablet', True) and $type != 'lxc' and $type != 'xen'
|
||||
<input type='tablet' bus='usb'/>
|
||||
#end if
|
||||
|
@ -875,20 +875,13 @@ class LibvirtConnection(driver.ComputeDriver):
|
||||
timer = utils.LoopingCall(_wait_for_boot)
|
||||
return timer.start(interval=0.5, now=True)
|
||||
|
||||
def _flush_libvirt_console(self, virsh_output):
|
||||
LOG.info(_('virsh said: %r'), virsh_output)
|
||||
virsh_output = virsh_output[0].strip()
|
||||
|
||||
if virsh_output.startswith('/dev/'):
|
||||
LOG.info(_("cool, it's a device"))
|
||||
out, err = utils.execute('dd',
|
||||
'if=%s' % virsh_output,
|
||||
'iflag=nonblock',
|
||||
run_as_root=True,
|
||||
check_exit_code=False)
|
||||
return out
|
||||
else:
|
||||
return ''
|
||||
def _flush_libvirt_console(self, pty):
|
||||
out, err = utils.execute('dd',
|
||||
'if=%s' % pty,
|
||||
'iflag=nonblock',
|
||||
run_as_root=True,
|
||||
check_exit_code=False)
|
||||
return out
|
||||
|
||||
def _append_to_file(self, data, fpath):
|
||||
LOG.info(_('data: %(data)r, fpath: %(fpath)r') % locals())
|
||||
@ -904,29 +897,28 @@ class LibvirtConnection(driver.ComputeDriver):
|
||||
|
||||
@exception.wrap_exception()
|
||||
def get_console_output(self, instance):
|
||||
virt_dom = self._lookup_by_name(instance['name'])
|
||||
xml = virt_dom.XMLDesc(0)
|
||||
tree = ElementTree.fromstring(xml)
|
||||
|
||||
# If the guest has a console logging to a file prefer to use that
|
||||
node = tree.find("./devices/console[@type='file']/source")
|
||||
if node is not None:
|
||||
fpath = node.get("path")
|
||||
return libvirt_utils.load_file(fpath)
|
||||
|
||||
# else if there is a PTY, then try to read latest data from that
|
||||
node = tree.find("./devices/console[@type='pty']/source")
|
||||
if node is None:
|
||||
raise exception.Error(_("Guest does not have a console available"))
|
||||
|
||||
pty = node.get("path")
|
||||
|
||||
console_log = os.path.join(FLAGS.instances_path, instance['name'],
|
||||
'console.log')
|
||||
|
||||
libvirt_utils.chown(console_log, os.getuid())
|
||||
|
||||
if FLAGS.libvirt_type == 'xen':
|
||||
# Xen is special
|
||||
virsh_output = utils.execute('virsh',
|
||||
'ttyconsole',
|
||||
instance['name'])
|
||||
data = self._flush_libvirt_console(virsh_output)
|
||||
fpath = self._append_to_file(data, console_log)
|
||||
elif FLAGS.libvirt_type == 'lxc':
|
||||
# LXC is also special
|
||||
virsh_output = utils.execute('virsh',
|
||||
'-c',
|
||||
'lxc:///',
|
||||
'ttyconsole',
|
||||
instance['name'])
|
||||
data = self._flush_libvirt_console(virsh_output)
|
||||
fpath = self._append_to_file(data, console_log)
|
||||
else:
|
||||
fpath = console_log
|
||||
data = self._flush_libvirt_console(pty)
|
||||
fpath = self._append_to_file(data, console_log)
|
||||
|
||||
return libvirt_utils.load_file(fpath)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user