--- a/components/openstack/nova/files/zone-vnc-console Fri Mar 20 03:13:26 2015 -0700
+++ b/components/openstack/nova/files/zone-vnc-console Thu Mar 19 14:41:20 2015 -0700
@@ -1,23 +1,43 @@
#!/usr/bin/python2.6
+# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
import errno
import os
import pwd
import smf_include
import subprocess
import sys
+import time
from subprocess import CalledProcessError, check_call, Popen
from tempfile import mkstemp
+GTF = "/usr/bin/gtf"
SVCCFG = "/usr/sbin/svccfg"
SVCPROP = "/usr/bin/svcprop"
VNCSERVER = "/usr/bin/vncserver"
+XRANDR = "/usr/bin/xrandr"
XSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \
"# DO NOT EDIT THIS FILE. EDITS WILL BE LOST.\n"
XTERM = "/usr/bin/xterm"
-# Monospsce font, point size 15, white foreground on black background"
-XTERMOPTS = ' -fa Monospace -fs 15 -fg white -bg black'
+# Borderless, Monospsce font, point size 14, white foreground on black
+# background are reasonable defaults.
+XTERMOPTS = ' -b 0 -fa Monospace -fs 14 -fg white -bg black -title ' + \
+ '"Zone Console: $ZONENAME"'
+XWININFO = "/usr/bin/xwininfo"
# Enclose command in comments to prevent xterm consuming zlogin opts
ZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n'
XSTARTUP = XSTARTUPHDR + XTERM + XTERMOPTS + ZLOGINOPTS
@@ -38,8 +58,8 @@
desktop_name = zonename + ' console'
# NOTE: 'geometry' below is that which matches the size of standard
# 80 character undecorated xterm window using font style specified in
- # XTERMOPTS. Update this geometry whenever XTERMOPTS are changed.
- # Avoids exposing X root window within noVNC canvas widget.
+ # XTERMOPTS. The geometry doesn't matter too much because the display
+ # will be resized using xrandr once the xterm geometry is established.
cmd = [VNCSERVER, "-name", desktop_name, "-SecurityTypes=None",
"-geometry", "964x580", "-localhost", "-autokill"]
vnc = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
@@ -72,6 +92,8 @@
if retcode != 0:
print "Error updating 'vnc/port' property: " + err
return smf_include.SMF_EXIT_ERR_FATAL
+ resize_xserver(display, zonename)
+
return smf_include.SMF_EXIT_OK
@@ -135,6 +157,121 @@
os.rename(stemp[1], xstartup_path)
+def resize_xserver(display, zonename):
+ """ Try to determine xterm window geometry and resize the Xvnc display
+ to match using XRANDR. Treat failure as non-fatal since an
+ incorrectly sized console is arguably better than none.
+ """
+ class UnmappedWindowError(Exception):
+ pass
+
+ def _get_window_geometry(display, windowname):
+ """ Find the xterm xwindow by name/title and extract its geometry
+ Returns: tuple of window [width, height]
+ Raises: UnmappedWindowError if window is not viewable/unmapped
+ """
+ cmd = [XWININFO, '-d', display, '-name', windowname]
+ xwininfo = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = xwininfo.communicate()
+ retcode = xwininfo.wait()
+ if retcode != 0:
+ print "Error finding console xwindow info: " + err
+ return
+
+ width = None
+ height = None
+ mapped = False
+ for line in out.splitlines():
+ line = line.strip()
+ if line.startswith("Map State:"):
+ if line.split()[-1] != "IsViewable":
+ # Window is not mapped yet.
+ raise UnmappedWindowError
+ else:
+ mapped = True
+ if line.startswith("Width:"):
+ width = int(line.split()[1])
+ elif line.startswith("Height:"):
+ height = int(line.split()[1])
+ if width and height and mapped:
+ return [width, height]
+ else:
+ # What, no width and height???
+ print "No window geometry info returned by " + XWINFINFO
+ raise UnmappedWindowError
+
+ retries = 5
+ width = 0
+ height = 0
+ for tries in range(retries):
+ try:
+ width, height = _get_window_geometry(display,
+ 'Zone Console: ' + zonename)
+ print "Discovered xterm geometry: %d x %d" % (width, height)
+ break
+ except UnmappedWindowError:
+ if tries < retries:
+ print "Discovered xterm not mapped yet. Retrying in 0.5s"
+ time.sleep(0.5)
+ continue
+ else:
+ print "Discovered xterm window is taking too long to map"
+ return
+ else:
+ print "Too many failed attempts to discover xterm window geometry"
+ return
+
+ # Generate a mode line for width and height, with a refresh of 60.0Hz
+ cmd = [GTF, str(width), str(height), '60.0', '-x']
+ gtf = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = gtf.communicate()
+ retcode = gtf.wait()
+ if retcode != 0:
+ print "Error creating new modeline for VNC display: " + err
+ return
+
+ for line in out.splitlines():
+ line = line.strip()
+ if line.startswith('Modeline'):
+ modeline = line.split('Modeline')[1]
+ print "New optimal modeline for Xvnc server: " + modeline
+ mode = modeline.split()
+ break
+
+ # Create a new mode for the Xvnc server using the modeline generated by gtf
+ cmd = [XRANDR, '-d', display, '--newmode']
+ cmd.extend(mode)
+ newmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = newmode.communicate()
+ retcode = newmode.wait()
+ if retcode != 0:
+ print "Error creating new xrandr modeline for VNC display: " + err
+ return
+
+ # Add the new mode to the default display output
+ modename = mode[0]
+ cmd = [XRANDR, '-d', display, '--addmode', 'default', modename]
+ addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = addmode.communicate()
+ retcode = addmode.wait()
+ if retcode != 0:
+ print "Error adding new xrandr modeline for VNC display: " + err
+ return
+
+ # Activate the new mode on the default display output
+ cmd = [XRANDR, '-d', display, '--output', 'default', '--mode', modename]
+ addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = addmode.communicate()
+ retcode = addmode.wait()
+ if retcode != 0:
+ print "Error setting new xrandr modeline for VNC display: " + err
+ return
+
if __name__ == "__main__":
os.putenv("LC_ALL", "C")
smf_include.smf_main()