Commit 88a9b09c authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Handle Ganeti msgs for resized VMs

Handle 'OP_INSTANCE_SET_PARAMS' notifications from Ganeti that have
'beparams' attribute, which corresponds to resized VMs. In such
case, the corresponding flavor is found an the VM in DB is updated to
have this flavor.

Refs #3870
parent 00311ecd
......@@ -36,7 +36,8 @@ from datetime import datetime
from synnefo.db.models import (Backend, VirtualMachine, Network,
BackendNetwork, BACKEND_STATUSES,
pooled_rapi_client, VirtualMachineDiagnostic)
pooled_rapi_client, VirtualMachineDiagnostic,
Flavor)
from synnefo.logic import utils
from synnefo import quotas
from synnefo.api.util import release_resource
......@@ -55,7 +56,8 @@ _reverse_tags = dict((v.split(':')[3], k) for k, v in _firewall_tags.items())
@transaction.commit_on_success
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None,
beparams=None):
"""Process a job progress notification from the backend
Process an incoming message from the backend (currently Ganeti).
......@@ -78,10 +80,23 @@ def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
if status == 'success' and state_for_success is not None:
vm.operstate = state_for_success
# Update the NICs of the VM
if status == "success" and nics is not None:
# Update the NICs of the VM
_process_net_status(vm, etime, nics)
if beparams:
assert(opcode == "OP_INSTANCE_SET_PARAMS"), "'beparams' should exist"\
" only for SET_PARAMS"
# VM Resize
if status == "success":
# VM has been resized. Change the flavor
_process_resize(vm, beparams)
vm.operstate = "STOPPED"
elif status in ("canceled", "error"):
vm.operstate = "STOPPED"
else:
vm.operstate = "RESIZE"
# Special case: if OP_INSTANCE_CREATE fails --> ERROR
if opcode == 'OP_INSTANCE_CREATE' and status in ('canceled', 'error'):
vm.operstate = 'ERROR'
......@@ -111,6 +126,25 @@ def process_op_status(vm, etime, jobid, opcode, status, logmsg, nics=None):
vm.save()
def _process_resize(vm, beparams):
"""Change flavor of a VirtualMachine based on new beparams."""
old_flavor = vm.flavor
vcpus = beparams.get("vcpus", None) or old_flavor.cpu
minmem, maxmem = beparams.get("minmem"), beparams.get("maxmem")
assert(minmem == maxmem), "Different minmem from maxmem"
if vcpus is None and maxmem is None:
return
ram = maxmem or old_flavor.ram
try:
new_flavor = Flavor.objects.get(cpu=vcpus, ram=ram,
disk=old_flavor.disk,
disk_template=old_flavor.disk_template)
except Flavor.DoesNotExist:
raise Exception("Can not find flavor for VM")
vm.flavor = new_flavor
vm.save()
@transaction.commit_on_success
def process_net_status(vm, etime, nics):
"""Wrap _process_net_status inside transaction."""
......
......@@ -171,8 +171,10 @@ def update_db(vm, msg, event_time):
return
nics = msg.get("nics", None)
beparams = msg.get("beparams", None)
backend.process_op_status(vm, event_time, msg['jobId'], msg['operation'],
msg['status'], msg['logmsg'], nics)
msg['status'], msg['logmsg'], nics=nics,
beparams=beparams)
log.debug("Done processing ganeti-op-status msg for vm %s.",
msg['instance'])
......
......@@ -193,6 +193,68 @@ class UpdateDBTest(TestCase):
self.assertEqual(db_vm.operstate, vm.operstate)
self.assertEqual(db_vm.backendtime, vm.backendtime)
def test_resize_msg(self, client):
vm = mfactory.VirtualMachineFactory()
# Test empty beparams
for status in ["success", "error"]:
msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
instance=vm.backend_vm_id,
beparams={},
status=status)
client.reset_mock()
update_db(client, msg)
self.assertTrue(client.basic_ack.called)
db_vm = VirtualMachine.objects.get(id=vm.id)
self.assertEqual(db_vm.operstate, vm.operstate)
# Test intermediate states
for status in ["queued", "waiting", "running"]:
msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
instance=vm.backend_vm_id,
beparams={"vcpus": 4, "minmem": 2048,
"maxmem": 2048},
status=status)
client.reset_mock()
update_db(client, msg)
self.assertTrue(client.basic_ack.called)
db_vm = VirtualMachine.objects.get(id=vm.id)
self.assertEqual(db_vm.operstate, "RESIZE")
# Test operstate after error
msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
instance=vm.backend_vm_id,
beparams={"vcpus": 4},
status="error")
client.reset_mock()
update_db(client, msg)
self.assertTrue(client.basic_ack.called)
db_vm = VirtualMachine.objects.get(id=vm.id)
self.assertEqual(db_vm.operstate, "STOPPED")
# Test success
f1 = mfactory.FlavorFactory(cpu=4, ram=1024, disk_template="drbd",
disk=1024)
vm.flavor = f1
vm.save()
f2 = mfactory.FlavorFactory(cpu=8, ram=2048, disk_template="drbd",
disk=1024)
msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
instance=vm.backend_vm_id,
beparams={"vcpus": 8, "minmem": 2048,
"maxmem": 2048},
status="success")
client.reset_mock()
update_db(client, msg)
self.assertTrue(client.basic_ack.called)
db_vm = VirtualMachine.objects.get(id=vm.id)
self.assertEqual(db_vm.operstate, "STOPPED")
self.assertEqual(db_vm.flavor, f2)
msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
instance=vm.backend_vm_id,
beparams={"vcpus": 100, "minmem": 2048,
"maxmem": 2048},
status="success")
client.reset_mock()
update_db(client, msg)
self.assertTrue(client.basic_reject.called)
@patch('synnefo.lib.amqp.AMQPClient')
class UpdateNetTest(TestCase):
......
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