Zhou Zheng Sheng has uploaded a new change for review. Change subject: [WIP] remote serial console via HTTP virtual server ......................................................................
[WIP] remote serial console via HTTP virtual server How to test: We test the remote serial console using netcat, however netcat do not support SSL, so we perform this test without SSL. Firstly, in /etc/vdsm/vdsm.conf, set "ssl = False", then in the last part of /etc/libvirt/libvirtd.conf set "listen_tls = 0" "listen_tcp = 1" "auth_tcp = "none"" and in the last part of /etc/libvirt/qemu.conf, set "spice_tls=0". Restart libvirtd and vdsmd, create a virtual machine with VDSM. Fedora is the preferable guest OS because it scans serial console and automatically starts a tty programme on it. You can just use a Fedora XFCE live CD to boot your VM if you do not have an existing Fedora VM. Run net cat as follow. nc 127.0.0.1 54321 Paste the following request in net cat. POST /VM/paste-your-VM-uuid-here/dev/console HTTP/1.0 Hit enter twice, you will see the response as follow. HTTP/1.0 200 OK Server: BaseHTTP/0.3 Python/2.7.3 Date: Wed, 26 Dec 2012 09:38:27 GMT Content-type: application/octet-stream After your VM is up, hit enter again, you will get the guest console as follow. It works in full duplex mode. Fedora release 17 (Beefy Miracle) Kernel 3.3.4-5.fc17.x86_64 on an x86_64 (hvc0) localhost login: If you can edit the kernel arguments while booting guest with grub, append "console=ttyS0" and you will see the boot up messages in net cat. Change-Id: I9b2fd0979dac68a7efed4d5bbcb1866cd48a0ccb Signed-off-by: Zhou Zheng Sheng <[email protected]> --- M vdsm.spec.in M vdsm/BindingXMLRPC.py M vdsm/Makefile.am A vdsm/consoleHandler.py M vdsm/libvirtvm.py M vdsm/supervdsmServer.py M vdsm/vm.py 7 files changed, 140 insertions(+), 6 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/81/10381/1 diff --git a/vdsm.spec.in b/vdsm.spec.in index 465d548..834cfa9 100644 --- a/vdsm.spec.in +++ b/vdsm.spec.in @@ -610,6 +610,7 @@ %{_datadir}/%{vdsm_name}/blkid.py* %{_datadir}/%{vdsm_name}/caps.py* %{_datadir}/%{vdsm_name}/clientIF.py* +%{_datadir}/%{vdsm_name}/consoleHandler.py* %{_datadir}/%{vdsm_name}/API.py* %{_datadir}/%{vdsm_name}/hooking.py* %{_datadir}/%{vdsm_name}/hooks.py* diff --git a/vdsm/BindingXMLRPC.py b/vdsm/BindingXMLRPC.py index 23ef422..742f709 100644 --- a/vdsm/BindingXMLRPC.py +++ b/vdsm/BindingXMLRPC.py @@ -33,6 +33,7 @@ from vdsm.define import doneCode, errCode import API from vdsm.exception import VdsmException +import consoleHandler try: from gluster.api import getGlusterMethods _glusterEnabled = True @@ -142,12 +143,8 @@ urlRouter = SecureXMLRPCServer.UrlRouter() urlRouter.registerHandler('/$', LoggingHandler) urlRouter.registerHandler('/RPC2$', LoggingHandler) - # all files under /etc/vdsm/ urlRouter.registerHandler( - '/etc/vdsm/', SimpleHTTPServer.SimpleHTTPRequestHandler) - # TODO: subclass SimpleHTTPServer.SimpleHTTPRequestHandler to provide - # a HTTP server forwarding the guest serial console stream and guest - # crash core dump download + consoleHandler.CONSOLE_REGEX, consoleHandler.consoleHandler) if self.enableSSL: KEYFILE, CERTFILE, CACERT = self._getKeyCertFilenames() diff --git a/vdsm/Makefile.am b/vdsm/Makefile.am index a687c56..38d85ad 100644 --- a/vdsm/Makefile.am +++ b/vdsm/Makefile.am @@ -30,6 +30,7 @@ caps.py \ clientIF.py \ configNetwork.py \ + consoleHandler.py \ debugPluginClient.py \ guestIF.py \ hooking.py \ diff --git a/vdsm/consoleHandler.py b/vdsm/consoleHandler.py new file mode 100644 index 0000000..1260941 --- /dev/null +++ b/vdsm/consoleHandler.py @@ -0,0 +1,118 @@ +# +# Copyright IBM Corp. 2012 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# +import os +import errno +import asyncore +import BaseHTTPServer +import urlparse + +import supervdsm +import storage.misc +import API + +CONSOLE_REGEX = (r'/VM/' + storage.misc.UUID_REGEX.pattern[1:-1] + + r'/dev/console$') + + +class ForwardSession(asyncore.dispatcher_with_send): + def __init__(self, sock, sockmap): + asyncore.dispatcher_with_send.__init__(self, sock, sockmap) + self._peer = None + + def linkPeer(self, peer): + self._peer = peer + + def unlinkPeer(self): + self._peer = None + + def sessionExit(self): + try: + self.close() + except OSError as e: + if e.errno != errno.EBADF: + raise + myPeer = self._peer + self._peer = None + if myPeer: + myPeer.unlinkPeer() + myPeer.sessionExit() + + def handle_read(self): + if not self._peer: + self.sessionExit() + return + data = self.recv(4096) + if data: + self._peer.send(data) + + def handle_close(self): + self.sessionExit() + + def readable(self): + if not (self._peer and self.connected): + return False + return len(self._peer.out_buffer) < 512 + + +class PTYSession(ForwardSession, asyncore.file_dispatcher): + def __init__(self, pty, sockmap): + ForwardSession.__init__(self, None, sockmap) + asyncore.file_dispatcher.__init__(self, pty, sockmap) + + +class consoleHandler(BaseHTTPServer.BaseHTTPRequestHandler): + wbufsize = 0 + rbufsize = 0 + disable_nagle_algorithm = True + + def do_POST(self): + self.close_connection = 1 + + pty = None + try: + vmid = urlparse.urlparse(self.path).path.split('/')[2] + vm = API.VM(vmid) + stats = vm.getStats() + if stats['status']['code'] != 0: + raise RuntimeError(stats['status']['message']) + ptyDev = stats['statsList'][0]['consolePath'] + supervdsm.getProxy().takeOwnership(ptyDev) + pty = os.open(ptyDev, os.O_RDWR | os.O_NONBLOCK | os.O_NOCTTY) + except Exception as e: + self.send_response(404, "Error open PTY for %s: %r\n" % + (self.path, e)) + self.end_headers() + self.wfile.flush() + if pty: + os.close(pty) + return + + self.send_response(200) + self.send_header("Content-type", "application/octet-stream") + self.end_headers() + self.wfile.flush() + + sockMap = {} + sockSession = ForwardSession(self.request, sockMap) + ptySession = PTYSession(pty, sockMap) + os.close(pty) + sockSession.linkPeer(ptySession) + ptySession.linkPeer(sockSession) + asyncore.loop(use_poll=True, map=sockMap) diff --git a/vdsm/libvirtvm.py b/vdsm/libvirtvm.py index d5b5e4f..a6e9361 100644 --- a/vdsm/libvirtvm.py +++ b/vdsm/libvirtvm.py @@ -1378,6 +1378,7 @@ self._getUnderlyingControllerDeviceInfo() self._getUnderlyingBalloonDeviceInfo() self._getUnderlyingWatchdogDeviceInfo() + self._getUnderlyingConsoleDeviceInfo() # Obtain info of all unknown devices. Must be last! self._getUnderlyingUnknownDeviceInfo() @@ -2881,6 +2882,16 @@ nicDev['network'] = network self.conf['devices'].append(nicDev) + def _getUnderlyingConsoleDeviceInfo(self): + ''' + Obtain console device info from libvirt + ''' + consoleElem = _domParseStr(self._lastXMLDesc).childNodes[0]. \ + getElementsByTagName('devices')[0]. \ + getElementsByTagName('console')[0] + self.conf["consolePath"] = str( + consoleElem.getElementsByTagName('source')[0].getAttribute('path')) + def _setWriteWatermarks(self): """ Define when to receive an event about high write to guest image diff --git a/vdsm/supervdsmServer.py b/vdsm/supervdsmServer.py index 1601ba5..43cc775 100755 --- a/vdsm/supervdsmServer.py +++ b/vdsm/supervdsmServer.py @@ -47,7 +47,8 @@ from storage.fileUtils import chown, resolveGid, resolveUid from storage.fileUtils import validateAccess as _validateAccess from vdsm.constants import METADATA_GROUP, EXT_UDEVADM, \ - DISKIMAGE_USER, DISKIMAGE_GROUP, P_LIBVIRT_VMCHANNELS + DISKIMAGE_USER, DISKIMAGE_GROUP, P_LIBVIRT_VMCHANNELS, \ + VDSM_USER, VDSM_GROUP from storage.devicemapper import _removeMapping, _getPathsStatus import configNetwork from vdsm.config import config @@ -292,6 +293,10 @@ def removeFs(self, path): return mkimage.removeFs(path) + @logDecorator + def takeOwnership(self, path): + chown(path, VDSM_USER, VDSM_GROUP) + def __pokeParent(parentPid, address): try: diff --git a/vdsm/vm.py b/vdsm/vm.py index e73ac6d..5a73558 100644 --- a/vdsm/vm.py +++ b/vdsm/vm.py @@ -1090,6 +1090,7 @@ 'displaySecurePort': self.conf['displaySecurePort'], 'displayType': self.conf['display'], 'displayIp': self.conf['displayIp'], + 'consolePath': self.conf['consolePath'], 'pid': self.conf['pid'], 'vmType': self.conf['vmType'], 'kvmEnable': self._kvmEnable, -- To view, visit http://gerrit.ovirt.org/10381 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I9b2fd0979dac68a7efed4d5bbcb1866cd48a0ccb Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Zhou Zheng Sheng <[email protected]> _______________________________________________ vdsm-patches mailing list [email protected] https://lists.fedorahosted.org/mailman/listinfo/vdsm-patches
