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 <zhshzhou(a)linux.vnet.ibm.com>
---
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 <zhshzhou(a)linux.vnet.ibm.com>