components/openstack/nova/files/zone-vnc-console
changeset 3998 5bd484384122
parent 3652 7e731a1b0b39
child 4046 47a996abe340
equal deleted inserted replaced
3997:0ca3f3d6c919 3998:5bd484384122
     1 #!/usr/bin/python2.6
     1 #!/usr/bin/python2.6
       
     2 
       
     3 # Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
       
     4 #
       
     5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
       
     6 #    not use this file except in compliance with the License. You may obtain
       
     7 #    a copy of the License at
       
     8 #
       
     9 #         http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 #    Unless required by applicable law or agreed to in writing, software
       
    12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    14 #    License for the specific language governing permissions and limitations
       
    15 #    under the License.
     2 
    16 
     3 import errno
    17 import errno
     4 import os
    18 import os
     5 import pwd
    19 import pwd
     6 import smf_include
    20 import smf_include
     7 import subprocess
    21 import subprocess
     8 import sys
    22 import sys
       
    23 import time
     9 
    24 
    10 from subprocess import CalledProcessError, check_call, Popen
    25 from subprocess import CalledProcessError, check_call, Popen
    11 from tempfile import mkstemp
    26 from tempfile import mkstemp
    12 
    27 
       
    28 GTF = "/usr/bin/gtf"
    13 SVCCFG = "/usr/sbin/svccfg"
    29 SVCCFG = "/usr/sbin/svccfg"
    14 SVCPROP = "/usr/bin/svcprop"
    30 SVCPROP = "/usr/bin/svcprop"
    15 VNCSERVER = "/usr/bin/vncserver"
    31 VNCSERVER = "/usr/bin/vncserver"
       
    32 XRANDR = "/usr/bin/xrandr"
    16 XSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \
    33 XSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \
    17               "#   DO NOT EDIT THIS FILE.  EDITS WILL BE LOST.\n"
    34               "#   DO NOT EDIT THIS FILE.  EDITS WILL BE LOST.\n"
    18 XTERM = "/usr/bin/xterm"
    35 XTERM = "/usr/bin/xterm"
    19 # Monospsce font, point size 15, white foreground on black background"
    36 # Borderless, Monospsce font, point size 14, white foreground on black
    20 XTERMOPTS = ' -fa Monospace -fs 15 -fg white -bg black'
    37 # background are reasonable defaults.
       
    38 XTERMOPTS = ' -b 0 -fa Monospace -fs 14 -fg white -bg black -title ' + \
       
    39             '"Zone Console: $ZONENAME"'
       
    40 XWININFO = "/usr/bin/xwininfo"
    21 # Enclose command in comments to prevent xterm consuming zlogin opts
    41 # Enclose command in comments to prevent xterm consuming zlogin opts
    22 ZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n'
    42 ZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n'
    23 XSTARTUP = XSTARTUPHDR + XTERM + XTERMOPTS + ZLOGINOPTS
    43 XSTARTUP = XSTARTUPHDR + XTERM + XTERMOPTS + ZLOGINOPTS
    24 
    44 
    25 
    45 
    36         zonename = fmri.rsplit(':', 1)[1]
    56         zonename = fmri.rsplit(':', 1)[1]
    37         os.putenv("ZONENAME", zonename)
    57         os.putenv("ZONENAME", zonename)
    38         desktop_name = zonename + ' console'
    58         desktop_name = zonename + ' console'
    39         # NOTE: 'geometry' below is that which matches the size of standard
    59         # NOTE: 'geometry' below is that which matches the size of standard
    40         # 80 character undecorated xterm window using font style specified in
    60         # 80 character undecorated xterm window using font style specified in
    41         # XTERMOPTS. Update this geometry whenever XTERMOPTS are changed.
    61         # XTERMOPTS. The geometry doesn't matter too much because the display
    42         # Avoids exposing X root window within noVNC canvas widget.
    62         # will be resized using xrandr once the xterm geometry is established.
    43         cmd = [VNCSERVER, "-name", desktop_name, "-SecurityTypes=None",
    63         cmd = [VNCSERVER, "-name", desktop_name, "-SecurityTypes=None",
    44                "-geometry", "964x580", "-localhost", "-autokill"]
    64                "-geometry", "964x580", "-localhost", "-autokill"]
    45         vnc = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    65         vnc = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    46                     env=None)
    66                     env=None)
    47         out, err = vnc.communicate()
    67         out, err = vnc.communicate()
    70             out, err = svccfg.communicate()
    90             out, err = svccfg.communicate()
    71             retcode = svccfg.wait()
    91             retcode = svccfg.wait()
    72             if retcode != 0:
    92             if retcode != 0:
    73                 print "Error updating 'vnc/port' property: " + err
    93                 print "Error updating 'vnc/port' property: " + err
    74                 return smf_include.SMF_EXIT_ERR_FATAL
    94                 return smf_include.SMF_EXIT_ERR_FATAL
       
    95     resize_xserver(display, zonename)
       
    96 
    75     return smf_include.SMF_EXIT_OK
    97     return smf_include.SMF_EXIT_OK
    76 
    98 
    77 
    99 
    78 def stop():
   100 def stop():
    79     try:
   101     try:
   133     os.close(stemp[0])
   155     os.close(stemp[0])
   134     os.chmod(stemp[1], 0700)
   156     os.chmod(stemp[1], 0700)
   135     os.rename(stemp[1], xstartup_path)
   157     os.rename(stemp[1], xstartup_path)
   136 
   158 
   137 
   159 
       
   160 def resize_xserver(display, zonename):
       
   161     """ Try to determine xterm window geometry and resize the Xvnc display
       
   162         to match using XRANDR. Treat failure as non-fatal since an
       
   163         incorrectly sized console is arguably better than none.
       
   164     """
       
   165     class UnmappedWindowError(Exception):
       
   166         pass
       
   167 
       
   168     def _get_window_geometry(display, windowname):
       
   169         """ Find the xterm xwindow by name/title and extract its geometry
       
   170             Returns: tuple of window [width, height]
       
   171             Raises: UnmappedWindowError if window is not viewable/unmapped
       
   172         """
       
   173         cmd = [XWININFO, '-d', display, '-name', windowname]
       
   174         xwininfo = subprocess.Popen(cmd, stdout=subprocess.PIPE,
       
   175                                     stderr=subprocess.PIPE)
       
   176         out, err = xwininfo.communicate()
       
   177         retcode = xwininfo.wait()
       
   178         if retcode != 0:
       
   179             print "Error finding console xwindow info: " + err
       
   180             return
       
   181 
       
   182         width = None
       
   183         height = None
       
   184         mapped = False
       
   185         for line in out.splitlines():
       
   186             line = line.strip()
       
   187             if line.startswith("Map State:"):
       
   188                 if line.split()[-1] != "IsViewable":
       
   189                     # Window is not mapped yet.
       
   190                     raise UnmappedWindowError
       
   191                 else:
       
   192                     mapped = True
       
   193             if line.startswith("Width:"):
       
   194                 width = int(line.split()[1])
       
   195             elif line.startswith("Height:"):
       
   196                 height = int(line.split()[1])
       
   197             if width and height and mapped:
       
   198                 return [width, height]
       
   199         else:
       
   200             # What, no width and height???
       
   201             print "No window geometry info returned by " + XWINFINFO
       
   202             raise UnmappedWindowError
       
   203 
       
   204     retries = 5
       
   205     width = 0
       
   206     height = 0
       
   207     for tries in range(retries):
       
   208         try:
       
   209             width, height = _get_window_geometry(display,
       
   210                                                  'Zone Console: ' + zonename)
       
   211             print "Discovered xterm geometry: %d x %d" % (width, height)
       
   212             break
       
   213         except UnmappedWindowError:
       
   214             if tries < retries:
       
   215                 print "Discovered xterm not mapped yet. Retrying in 0.5s"
       
   216                 time.sleep(0.5)
       
   217                 continue
       
   218             else:
       
   219                 print "Discovered xterm window is taking too long to map"
       
   220                 return
       
   221     else:
       
   222         print "Too many failed attempts to discover xterm window geometry"
       
   223         return
       
   224 
       
   225     # Generate a mode line for width and height, with a refresh of 60.0Hz
       
   226     cmd = [GTF, str(width), str(height), '60.0', '-x']
       
   227     gtf = subprocess.Popen(cmd, stdout=subprocess.PIPE,
       
   228                            stderr=subprocess.PIPE)
       
   229     out, err = gtf.communicate()
       
   230     retcode = gtf.wait()
       
   231     if retcode != 0:
       
   232         print "Error creating new modeline for VNC display: " + err
       
   233         return
       
   234 
       
   235     for line in out.splitlines():
       
   236         line = line.strip()
       
   237         if line.startswith('Modeline'):
       
   238             modeline = line.split('Modeline')[1]
       
   239             print "New optimal modeline for Xvnc server: " + modeline
       
   240             mode = modeline.split()
       
   241             break
       
   242 
       
   243     # Create a new mode for the Xvnc server using the modeline generated by gtf
       
   244     cmd = [XRANDR, '-d', display, '--newmode']
       
   245     cmd.extend(mode)
       
   246     newmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
       
   247                                stderr=subprocess.PIPE)
       
   248     out, err = newmode.communicate()
       
   249     retcode = newmode.wait()
       
   250     if retcode != 0:
       
   251         print "Error creating new xrandr modeline for VNC display: " + err
       
   252         return
       
   253 
       
   254     # Add the new mode to the default display output
       
   255     modename = mode[0]
       
   256     cmd = [XRANDR, '-d', display, '--addmode', 'default', modename]
       
   257     addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
       
   258                                stderr=subprocess.PIPE)
       
   259     out, err = addmode.communicate()
       
   260     retcode = addmode.wait()
       
   261     if retcode != 0:
       
   262         print "Error adding new xrandr modeline for VNC display: " + err
       
   263         return
       
   264 
       
   265     # Activate the new mode on the default display output
       
   266     cmd = [XRANDR, '-d', display, '--output', 'default', '--mode', modename]
       
   267     addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
       
   268                                stderr=subprocess.PIPE)
       
   269     out, err = addmode.communicate()
       
   270     retcode = addmode.wait()
       
   271     if retcode != 0:
       
   272         print "Error setting new xrandr modeline for VNC display: " + err
       
   273         return
       
   274 
   138 if __name__ == "__main__":
   275 if __name__ == "__main__":
   139     os.putenv("LC_ALL", "C")
   276     os.putenv("LC_ALL", "C")
   140     smf_include.smf_main()
   277     smf_include.smf_main()