Commit 0ac4ac5c authored by Vangelis Koukis's avatar Vangelis Koukis
Browse files

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.
parent 8b244558
......@@ -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),
......
......@@ -25,5 +25,8 @@ class ResizeNotAllowed(Fault):
class ItemNotFound(Fault):
code = 404
class NotImplemented(Fault):
code = 501
class ServiceUnavailable(Fault):
code = 503
......@@ -251,7 +251,7 @@ def server_action(request, server_id):
if key not in server_actions:
raise BadRequest
return server_actions[key](server, req[key])
return server_actions[key](request, server, req[key])
@api_method('GET')
def list_addresses(request, server_id):
......
<?xml version="1.0" encoding="UTF-8"?>
<vnc xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" host="{{ vnc.host }}" port="{{ vnc.port }}" password="{{ vnc.password }}">
</vnc>
......@@ -231,6 +231,21 @@ class StartServer(Command):
self.http_post(path, body)
@command_name('console')
class ServerConsole(Command):
description = 'get VNC console'
syntax = '<server id>'
def add_options(self, parser):
pass
def execute(self, server_id):
path = '/api/%s/servers/%d/action' % (self.api, int(server_id))
body = json.dumps({'console':{'type':'VNC'}})
reply = self.http_post(path, body)
print_dict(reply)
@command_name('lsaddr')
class ListAddresses(Command):
description = 'list server addresses'
......
#!/usr/bin/env python
#
# Copyright (c) 2010 GRNET SA
#
# 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.
import sys
import socket
CTRL_SOCKET = "/tmp/vncproxy.sock"
def request_forwarding(sport, daddr, dport, password):
sport = str(int(sport))
dport = str(int(dport))
assert(len(password) > 0)
request = ":".join([sport, daddr, dport, password])
ctrl = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
ctrl.connect(CTRL_SOCKET)
ctrl.send(request)
response = ctrl.recv(1024)
if response == "OK":
return True
else:
return False
if __name__ == '__main__':
request_forwarding(*sys.argv[1:])
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment