Commit a8110fa2 authored by Christos Stavrakakis's avatar Christos Stavrakakis

cyclades: Check VM state when getting vnc console

vncauthproxy will return error status if the instance is not running.
Cyclades check that the VM in marked as started in DB before allowing a
console action. However, the VM may be stopped without Cyclades knowing
it (unsynced state). This commit fixes this issue by checking the VM is
actually running, by looking the 'oper_state' field in the result of the
query, that is already performed to get the instance host and port. In
case the VM is stopped, state is reconciled by issuing a mocked
'OP_INSTANCE_SHUTDOWN' Ganeti job, that will take care of state and
quotas.

Also, remove use of 'TEST' setting that was used for unittests. Instead,
properly mock Ganeti and vncauthproxy responses.
parent 52b09a57
# Copyright 2012 GRNET S.A. All rights reserved.
# Copyright 2012-2014 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -837,9 +837,19 @@ class ServerVNCConsole(ComputeAPITest):
vm.save()
data = json.dumps({'console': {'type': 'vnc'}})
with override_settings(settings, TEST=True):
response = self.mypost('servers/%d/action' % vm.id,
vm.userid, data, 'json')
with patch('synnefo.logic.rapi_pool.GanetiRapiClient') as rapi:
rapi().GetInstance.return_value = {"pnode": "node1",
"network_port": 5055,
"oper_state": True,
"hvparams": {
"serial_console": False
}}
with patch("synnefo.logic.servers.request_vnc_forwarding") as vnc:
vnc.return_value = {"status": "OK",
"source_port": 42}
response = self.mypost('servers/%d/action' % vm.id,
vm.userid, data, 'json')
self.assertEqual(response.status_code, 200)
reply = json.loads(response.content)
self.assertEqual(reply.keys(), ['console'])
......
# Copyright 2011-2013 GRNET S.A. All rights reserved.
# Copyright 2011-2014 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -730,34 +730,6 @@ def resize_instance(vm, vcpus, memory):
return client.ModifyInstance(vm.backend_vm_id, beparams=beparams)
def get_instance_console(vm):
# RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
# which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
# useless (see #783).
#
# Until this is fixed on the Ganeti side, construct a console info reply
# directly.
#
# WARNING: This assumes that VNC runs on port network_port on
# the instance's primary node, and is probably
# hypervisor-specific.
#
log.debug("Getting console for vm %s", vm)
console = {}
console['kind'] = 'vnc'
with pooled_rapi_client(vm) as client:
i = client.GetInstance(vm.backend_vm_id)
if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
raise Exception("hv parameter serial_console cannot be true")
console['host'] = i['pnode']
console['port'] = i['network_port']
return console
def get_instance_info(vm):
with pooled_rapi_client(vm) as client:
return client.GetInstance(vm.backend_vm_id)
......
......@@ -29,6 +29,7 @@
import logging
from datetime import datetime
from socket import getfqdn
from functools import wraps
from django import dispatch
......@@ -42,7 +43,8 @@ from synnefo.api import util
from synnefo.logic import backend, ips, utils
from synnefo.logic.backend_allocator import BackendAllocator
from synnefo.db.models import (NetworkInterface, VirtualMachine,
VirtualMachineMetadata, IPAddressLog, Network)
VirtualMachineMetadata, IPAddressLog, Network,
pooled_rapi_client)
from vncauthproxy.client import request_forwarding as request_vnc_forwarding
from synnefo.logic import rapi
......@@ -384,18 +386,41 @@ def console(vm, console_type):
"""
log.info("Get console VM %s, type %s", vm, console_type)
# Use RAPI to get VNC console information for this instance
if vm.operstate != "STARTED":
raise faults.BadRequest('Server not in ACTIVE state.')
if settings.TEST:
console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
else:
console_data = backend.get_instance_console(vm)
if console_data['kind'] != 'vnc':
message = 'got console of kind %s, not "vnc"' % console_data['kind']
raise faults.ServiceUnavailable(message)
# Use RAPI to get VNC console information for this instance
# RAPI GetInstanceConsole() returns endpoints to the vnc_bind_address,
# which is a cluster-wide setting, either 0.0.0.0 or 127.0.0.1, and pretty
# useless (see #783).
#
# Until this is fixed on the Ganeti side, construct a console info reply
# directly.
#
# WARNING: This assumes that VNC runs on port network_port on
# the instance's primary node, and is probably
# hypervisor-specific.
def get_console_data(i):
return {"kind": "vnc",
"host": i["pnode"],
"port": i["network_port"]}
with pooled_rapi_client(vm) as c:
i = c.GetInstance(vm.backend_vm_id)
console_data = get_console_data(i)
if vm.backend.hypervisor == "kvm" and i['hvparams']['serial_console']:
raise Exception("hv parameter serial_console cannot be true")
# Check that the instance is really running
if not i["oper_state"]:
log.warning("VM '%s' is marked as '%s' in DB while DOWN in Ganeti",
vm.id, vm.operstate)
# Instance is not running. Mock a shutdown job to sync DB
backend.process_op_status(vm, etime=datetime.now(), jobid=0,
opcode="OP_INSTANCE_SHUTDOWN",
status="success",
logmsg="Reconciliation simulated event")
raise faults.BadRequest('Server not in ACTIVE state.')
# Let vncauthproxy decide on the source port.
# The alternative: static allocation, e.g.
......@@ -405,20 +430,18 @@ def console(vm, console_type):
dport = console_data['port']
password = util.random_password()
if settings.TEST:
fwd = {'source_port': 1234, 'status': 'OK'}
else:
vnc_extra_opts = settings.CYCLADES_VNCAUTHPROXY_OPTS
fwd = request_vnc_forwarding(sport, daddr, dport, password,
**vnc_extra_opts)
vnc_extra_opts = settings.CYCLADES_VNCAUTHPROXY_OPTS
fwd = request_vnc_forwarding(sport, daddr, dport, password,
**vnc_extra_opts)
if fwd['status'] != "OK":
raise faults.ServiceUnavailable('vncauthproxy returned error status')
# Verify that the VNC server settings haven't changed
if not settings.TEST:
if console_data != backend.get_instance_console(vm):
raise faults.ServiceUnavailable('VNC Server settings changed.')
with pooled_rapi_client(vm) as c:
i = c.GetInstance(vm.backend_vm_id)
if get_console_data(i) != console_data:
raise faults.ServiceUnavailable('VNC Server settings changed.')
console = {
'type': 'vnc',
......
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