commit 094716113c9be59cb60e56105faa4d59d1a7eac4
Author: Vangelis Koukis <vkoukis@cslab.ece.ntua.gr>
Date:   Sun Apr 3 03:11:41 2011 +0300

    Extend api/v1.1redux to support VNC OOB consoles
    
    Add support in api/v1.1redux for OOB consoles using VNC, refs #349.
    More specifically:
     * Implement a new action ("console") for POST to /servers/id/action.
     * Extend api/servers.py to pass the Django request object to actions.
     * Import a version of the vncauthproxy control client under util/.
     * Extend the cloud command-line tool to support getting OOB console access.
    
    Example json: {"console": {"type": "VNC"}}
    Example XML:  <console type="VNC" />
    
    To service the call, the API will contact a running vncauthproxy to set up a
    time-limited port for VNC forwarding, where the client can connect.
    
    This is WIP. Still missing:
     * A patched version of vncauthproxy.
     * Client-side (GUI) support for requesting an OOB console.

diff --git a/api/actions.py b/api/actions.py
index 328e8d8..f5accb2 100644
--- a/api/actions.py
+++ b/api/actions.py
@@ -4,9 +4,13 @@
 
 from django.conf import settings
 from django.http import HttpResponse
+from django.template.loader import render_to_string
+from django.utils import simplejson as json
 
 from synnefo.api.errors import *
+from synnefo.api.util import random_password
 from synnefo.util.rapi import GanetiRapiClient
+from synnefo.util.vapclient import request_forwarding as request_vnc_forwarding
 from synnefo.logic import backend, utils
 
 server_actions = {}
@@ -20,9 +24,64 @@ def server_action(name):
         return func
     return decorator
 
+@server_action('console')
+def get_console(request, server, args):
+    """Arrange for an OOB console of the specified type
+
+    This method arranges for an OOB console of the specified type.
+    Only "vnc" type consoles are supported for now.
+    It uses a running instance of vncauthproxy to setup proper
+    VNC forwarding with a random password, then returns the necessary
+    VNC connection info to the caller.
+
+    """
+    # Normal Response Code: 200
+    # Error Response Codes: computeFault (400, 500),
+    #                       serviceUnavailable (503),
+    #                       unauthorized (401),
+    #                       badRequest (400),
+    #                       badMediaType(415),
+    #                       itemNotFound (404),
+    #                       buildInProgress (409),
+    #                       overLimit (413)
+    try:
+        console_type = args.get('type', '')
+        if console_type != 'VNC':
+            raise BadRequest(message="type can only be 'VNC'")
+    except KeyError:
+        raise BadRequest()
+
+    # Use RAPI to get VNC console information for this instance
+    if utils.get_rsapi_state(server) != 'ACTIVE':
+        raise BadRequest(message="Server not in ACTIVE state")
+    console_data = rapi.GetInstanceConsole(server.backend_id)
+    if console_data['kind'] != 'vnc':
+        raise ServiceUnavailable()
+
+    # Let vncauthproxy decide on the source port.
+    # FIXME
+    # sport = 0
+    sport = console_data['port'] - 1000
+    daddr = console_data['host']
+    dport = console_data['port']
+    passwd = random_password()
+
+    request_vnc_forwarding(sport, daddr, dport, passwd)
+    vnc = { 'host': '62.217.120.67', 'port': sport, 'password': passwd }
+
+    # Format to be reviewed by [verigak], FIXME
+    if request.type == 'xml':
+        mimetype = 'application/xml'
+        data = render_to_string('vnc.xml', {'vnc': vnc})
+    else:
+        mimetype = 'application/json'
+        data = json.dumps(vnc)
+
+    return HttpResponse(data, mimetype=mimetype, status=200)
+
 
 @server_action('changePassword')
-def change_password(server, args):
+def change_password(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
@@ -41,7 +100,7 @@ def change_password(server, args):
     raise ServiceUnavailable()
 
 @server_action('reboot')
-def reboot(server, args):
+def reboot(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
@@ -61,7 +120,7 @@ def reboot(server, args):
     return HttpResponse(status=202)
 
 @server_action('start')
-def start(server, args):
+def start(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: serviceUnavailable (503), itemNotFound (404)
 
@@ -70,7 +129,7 @@ def start(server, args):
     return HttpResponse(status=202)
 
 @server_action('shutdown')
-def shutdown(server, args):
+def shutdown(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: serviceUnavailable (503), itemNotFound (404)
     
@@ -79,7 +138,7 @@ def shutdown(server, args):
     return HttpResponse(status=202)
 
 @server_action('rebuild')
-def rebuild(server, args):
+def rebuild(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
@@ -94,7 +153,7 @@ def rebuild(server, args):
     raise ServiceUnavailable()
 
 @server_action('resize')
-def resize(server, args):
+def resize(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
@@ -110,7 +169,7 @@ def resize(server, args):
     raise ResizeNotAllowed()
 
 @server_action('confirmResize')
-def confirm_resize(server, args):
+def confirm_resize(request, server, args):
     # Normal Response Code: 204
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
@@ -126,7 +185,7 @@ def confirm_resize(server, args):
     raise ResizeNotAllowed()
 
 @server_action('revertResize')
-def revert_resize(server, args):
+def revert_resize(request, server, args):
     # Normal Response Code: 202
     # Error Response Codes: computeFault (400, 500),
     #                       serviceUnavailable (503),
