Commit eabb2f53 by Kálmán Viktor

Merge branch 'master' into feature-store

parents 4816e34a d0891d38
......@@ -1395,6 +1395,7 @@
"raw_data": "",
"vnc_port": 1234,
"num_cores": 2,
"status": "RUNNING",
"modified": "2013-10-14T07:27:38.192Z"
}
},
......
......@@ -199,6 +199,8 @@ class VmDetailTest(LoginMixin, TestCase):
inst = Instance.objects.get(pk=1)
inst.set_level(self.u1, 'owner')
inst.add_interface(vlan=Vlan.objects.get(pk=1), user=self.us)
inst.status = 'RUNNING'
inst.save()
iface_count = inst.interface_set.count()
c.post("/dashboard/interface/1/delete/")
......@@ -211,6 +213,8 @@ class VmDetailTest(LoginMixin, TestCase):
inst.set_level(self.u1, 'owner')
vlan = Vlan.objects.get(pk=1)
inst.add_interface(vlan=vlan, user=self.us)
inst.status = 'RUNNING'
inst.save()
iface_count = inst.interface_set.count()
response = c.post("/dashboard/interface/1/delete/",
......
......@@ -185,12 +185,24 @@ class Disk(AclBase, TimeStampedModel):
return {
'qcow2-norm': 'vd',
'qcow2-snap': 'vd',
'iso': 'hd',
'iso': 'sd',
'raw-ro': 'vd',
'raw-rw': 'vd',
}[self.type]
@property
def device_bus(self):
"""Returns the proper device prefix for different types of images.
"""
return {
'qcow2-norm': 'virtio',
'qcow2-snap': 'virtio',
'iso': 'scsi',
'raw-ro': 'virtio',
'raw-rw': 'virtio',
}[self.type]
@property
def is_deletable(self):
"""True if the associated file can be deleted.
"""
......@@ -251,6 +263,7 @@ class Disk(AclBase, TimeStampedModel):
'driver_type': self.vm_format,
'driver_cache': 'none',
'target_device': self.device_type + self.dev_num,
'target_bus': self.device_bus,
'disk_device': 'cdrom' if self.type == 'iso' else 'disk'
}
......
......@@ -9,7 +9,10 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Removing unique constraint on 'InstanceTemplate', fields ['name']
db.delete_unique(u'vm_instancetemplate', ['name'])
try:
db.delete_unique(u'vm_instancetemplate', ['name'])
except Exception as e:
print unicode(e)
# Changing field 'InstanceTemplate.parent'
......@@ -281,4 +284,4 @@ class Migration(SchemaMigration):
}
}
complete_apps = ['vm']
\ No newline at end of file
complete_apps = ['vm']
......@@ -7,34 +7,45 @@ class Migration(SchemaMigration):
def forwards(self, orm):
db.start_transaction()
# Adding field 'InstanceActivity.result_data'
db.add_column(u'vm_instanceactivity', 'result_data',
self.gf('jsonfield.fields.JSONField')(null=True, blank=True),
keep_default=False)
# Adding field 'NodeActivity.result_data'
db.add_column(u'vm_nodeactivity', 'result_data',
self.gf('jsonfield.fields.JSONField')(null=True, blank=True),
keep_default=False)
db.commit_transaction()
db.start_transaction()
for i in orm.InstanceActivity.objects.all():
result = i.result.replace("%", "%%") if i.result else ""
i.result_data = {"user_text_template": "",
"admin_text_template": result, "params": {}}
i.save()
# Deleting field 'InstanceActivity.result'
db.delete_column(u'vm_instanceactivity', 'result')
# Adding field 'NodeActivity.result_data'
db.add_column(u'vm_nodeactivity', 'result_data',
self.gf('jsonfield.fields.JSONField')(null=True, blank=True),
keep_default=False)
for i in orm.NodeActivity.objects.all():
result = i.result.replace("%", "%%") if i.result else ""
i.result_data = {"user_text_template": "",
"admin_text_template": result, "params": {}}
i.save()
db.commit_transaction()
db.start_transaction()
# Deleting field 'InstanceActivity.result'
db.delete_column(u'vm_instanceactivity', 'result')
# Deleting field 'NodeActivity.result'
db.delete_column(u'vm_nodeactivity', 'result')
db.commit_transaction()
def backwards(self, orm):
# Adding field 'InstanceActivity.result'
......
......@@ -763,6 +763,52 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
"""
return scheduler.select_node(self, Node.objects.all())
def attach_disk(self, disk, timeout=15):
queue_name = self.get_remote_queue_name('vm', 'fast')
return vm_tasks.attach_disk.apply_async(
args=[self.vm_name,
disk.get_vmdisk_desc()],
queue=queue_name
).get(timeout=timeout)
def detach_disk(self, disk, timeout=15):
try:
queue_name = self.get_remote_queue_name('vm', 'fast')
return vm_tasks.detach_disk.apply_async(
args=[self.vm_name,
disk.get_vmdisk_desc()],
queue=queue_name
).get(timeout=timeout)
except Exception as e:
if e.libvirtError and "not found" in str(e):
logger.debug("Disk %s was not found."
% disk.name)
else:
raise
def attach_network(self, network, timeout=15):
queue_name = self.get_remote_queue_name('vm', 'fast')
return vm_tasks.attach_network.apply_async(
args=[self.vm_name,
network.get_vmnetwork_desc()],
queue=queue_name
).get(timeout=timeout)
def detach_network(self, network, timeout=15):
try:
queue_name = self.get_remote_queue_name('vm', 'fast')
return vm_tasks.detach_network.apply_async(
args=[self.vm_name,
network.get_vmnetwork_desc()],
queue=queue_name
).get(timeout=timeout)
except Exception as e:
if e.libvirtError and "not found" in str(e):
logger.debug("Interface %s was not found."
% (network.__unicode__()))
else:
raise
def deploy_disks(self):
"""Deploy all associated disks.
"""
......
......@@ -18,6 +18,7 @@
from __future__ import absolute_import, unicode_literals
from logging import getLogger
from re import search
from string import ascii_lowercase
from django.core.exceptions import PermissionDenied
from django.utils import timezone
......@@ -35,7 +36,6 @@ from .models import (
NodeActivity,
)
logger = getLogger(__name__)
......@@ -94,6 +94,11 @@ class AddInterfaceOperation(InstanceOperation):
"the VM.")
required_perms = ()
def check_precond(self):
super(AddInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, vlan, managed=None):
if managed is None:
managed = vlan.managed
......@@ -102,6 +107,8 @@ class AddInterfaceOperation(InstanceOperation):
managed=managed, owner=user, vlan=vlan)
if self.instance.is_running:
with activity.sub_activity('attach_network'):
self.instance.attach_network(net)
net.deploy()
return net
......@@ -115,6 +122,7 @@ register_operation(AddInterfaceOperation)
class CreateDiskOperation(InstanceOperation):
activity_code_suffix = 'create_disk'
id = 'create_disk'
name = _("create disk")
......@@ -123,20 +131,29 @@ class CreateDiskOperation(InstanceOperation):
def check_precond(self):
super(CreateDiskOperation, self).check_precond()
# TODO remove check when hot-attach is implemented
if self.instance.status not in ['STOPPED', 'PENDING']:
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, size, name=None):
# TODO implement with hot-attach when it'll be available
def _operation(self, user, size, activity, name=None):
from storage.models import Disk
if not name:
name = "new disk"
disk = Disk.create(size=size, name=name, type="qcow2-norm")
disk.full_clean()
devnums = list(ascii_lowercase)
for d in self.instance.disks.all():
devnums.remove(d.dev_num)
disk.dev_num = devnums.pop(0)
disk.save()
self.instance.disks.add(disk)
if self.instance.is_running:
with activity.sub_activity('deploying_disk'):
disk.deploy()
with activity.sub_activity('attach_disk'):
self.instance.attach_disk(disk)
def get_activity_name(self, kwargs):
return create_readable(ugettext_noop("create %(size)s disk"),
size=kwargs['size'])
......@@ -156,21 +173,29 @@ class DownloadDiskOperation(InstanceOperation):
def check_precond(self):
super(DownloadDiskOperation, self).check_precond()
# TODO remove check when hot-attach is implemented
if self.instance.status not in ['STOPPED', 'PENDING']:
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, url, task, activity, name=None):
activity.result = url
# TODO implement with hot-attach when it'll be available
from storage.models import Disk
disk = Disk.download(url=url, name=name, task=task)
devnums = list(ascii_lowercase)
for d in self.instance.disks.all():
devnums.remove(d.dev_num)
disk.dev_num = devnums.pop(0)
disk.full_clean()
disk.save()
self.instance.disks.add(disk)
activity.readable_name = create_readable(
ugettext_noop("download %(name)s"), name=disk.name)
# TODO iso (cd) hot-plug is not supported by kvm/guests
if self.instance.is_running and disk.type not in ["iso"]:
with activity.sub_activity('attach_disk'):
self.instance.attach_disk(disk)
register_operation(DownloadDiskOperation)
......@@ -366,8 +391,15 @@ class RemoveInterfaceOperation(InstanceOperation):
description = _("Remove the specified network interface from the VM.")
required_perms = ()
def check_precond(self):
super(RemoveInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, interface):
if self.instance.is_running:
with activity.sub_activity('detach_network'):
self.instance.detach_network(interface)
interface.shutdown()
interface.destroy()
......@@ -386,12 +418,13 @@ class RemoveDiskOperation(InstanceOperation):
def check_precond(self):
super(RemoveDiskOperation, self).check_precond()
# TODO remove check when hot-detach is implemented
if self.instance.status not in ['STOPPED']:
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, disk):
# TODO implement with hot-detach when it'll be available
if self.instance.is_running and disk.type not in ["iso"]:
with activity.sub_activity('detach_disk'):
self.instance.detach_disk(disk)
return self.instance.disks.remove(disk)
......
......@@ -62,6 +62,26 @@ def get_queues():
return result
@celery.task(name='vmdriver.attach_disk')
def attach_disk(vm, disk):
pass
@celery.task(name='vmdriver.detach_disk')
def detach_disk(vm, disk):
pass
@celery.task(name='vmdriver.attach_network')
def attach_network(vm, net):
pass
@celery.task(name='vmdriver.detach_network')
def detach_network(vm, net):
pass
@celery.task(name='vmdriver.create')
def deploy(params):
pass
......
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 sign in to comment