forked from uncloud/uncloud
Add debug to opennebula, create VM disks from opennebula correctly
This commit is contained in:
parent
10c5257f90
commit
08fe3e689e
8 changed files with 100 additions and 43 deletions
|
@ -1,13 +1,18 @@
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
from opennebula.models import VM as VMModel
|
from opennebula.models import VM as VMModel
|
||||||
from uncloud_vm.models import VMHost, VMProduct, VMNetworkCard, VMDiskImageProduct, VMDiskProduct
|
from uncloud_vm.models import VMHost, VMProduct, VMNetworkCard, VMDiskImageProduct, VMDiskProduct
|
||||||
|
|
||||||
from uncloud_pay.models import Order
|
from uncloud_pay.models import Order
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
def convert_mac_to_int(mac_address: str):
|
def convert_mac_to_int(mac_address: str):
|
||||||
# Remove octet connecting characters
|
# Remove octet connecting characters
|
||||||
|
@ -41,24 +46,35 @@ def create_nics(one_vm, vm_product):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_disk_and_image(one_vm, vm_product):
|
def sync_disk_and_image(one_vm, vm_product, disk_owner):
|
||||||
for disk in one_vm.disks:
|
"""
|
||||||
owner = one_vm.owner
|
a) Check all opennebula disk if they are in the uncloud VM, if not add
|
||||||
name = disk.get('image')
|
b) Check all uncloud disks and remove them if they are not in the opennebula VM
|
||||||
|
"""
|
||||||
|
|
||||||
# TODO: Fix the following hard coded values
|
vmdisknum = 0
|
||||||
is_os_image, is_public, status = True, True, 'active'
|
|
||||||
|
one_disks_extra_data = []
|
||||||
|
|
||||||
|
for disk in one_vm.disks:
|
||||||
|
vmowner = one_vm.owner
|
||||||
|
name = disk.get('image')
|
||||||
|
vmdisknum += 1
|
||||||
|
|
||||||
|
log.info("Checking disk {} for VM {}".format(name, one_vm))
|
||||||
|
|
||||||
|
is_os_image, is_public, status = True, False, 'active'
|
||||||
|
|
||||||
image_size_in_gb = disk.get('image_size_in_gb')
|
image_size_in_gb = disk.get('image_size_in_gb')
|
||||||
disk_size_in_gb = disk.get('size_in_gb')
|
disk_size_in_gb = disk.get('size_in_gb')
|
||||||
storage_class = disk.get('pool_name')
|
storage_class = disk.get('storage_class')
|
||||||
image_source = disk.get('source')
|
image_source = disk.get('source')
|
||||||
image_source_type = disk.get('source_type')
|
image_source_type = disk.get('source_type')
|
||||||
|
|
||||||
image, _ = VMDiskImageProduct.objects.update_or_create(
|
image, _ = VMDiskImageProduct.objects.update_or_create(
|
||||||
name=name,
|
name=name,
|
||||||
defaults={
|
defaults={
|
||||||
'owner': owner,
|
'owner': disk_owner,
|
||||||
'is_os_image': is_os_image,
|
'is_os_image': is_os_image,
|
||||||
'is_public': is_public,
|
'is_public': is_public,
|
||||||
'size_in_gb': image_size_in_gb,
|
'size_in_gb': image_size_in_gb,
|
||||||
|
@ -68,29 +84,59 @@ def create_disk_and_image(one_vm, vm_product):
|
||||||
'status': status
|
'status': status
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
VMDiskProduct.objects.update_or_create(
|
|
||||||
owner=owner, vm=vm_product,
|
# identify vmdisk from opennebula - primary mapping key
|
||||||
defaults={
|
extra_data = {
|
||||||
'image': image,
|
'opennebula_vm': one_vm.vmid,
|
||||||
'size_in_gb': disk_size_in_gb
|
'opennebula_size_in_gb': disk_size_in_gb,
|
||||||
|
'opennebula_source': disk.get('opennebula_source'),
|
||||||
|
'opennebula_disk_num': vmdisknum
|
||||||
}
|
}
|
||||||
|
# Save for comparing later
|
||||||
|
one_disks_extra_data.append(extra_data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
vm_disk = VMDiskProduct.objects.get(extra_data=extra_data)
|
||||||
|
except VMDiskProduct.DoesNotExist:
|
||||||
|
vm_disk = VMDiskProduct.objects.create(
|
||||||
|
owner=vmowner,
|
||||||
|
vm=vm_product,
|
||||||
|
image=image,
|
||||||
|
size_in_gb=disk_size_in_gb,
|
||||||
|
extra_data=extra_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Now remove all disks that are not in above extra_data list
|
||||||
|
for disk in VMDiskProduct.objects.filter(vm=vm_product):
|
||||||
|
extra_data = disk.extra_data
|
||||||
|
if not extra_data in one_disks_extra_data:
|
||||||
|
log.info("Removing disk {} from VM {}".format(disk, vm_product))
|
||||||
|
disk.delete()
|
||||||
|
|
||||||
|
disks = [ disk.extra_data for disk in VMDiskProduct.objects.filter(vm=vm_product) ]
|
||||||
|
log.info("VM {} has disks: {}".format(vm_product, disks))
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Migrate Opennebula VM to regular (uncloud) vm'
|
help = 'Migrate Opennebula VM to regular (uncloud) vm'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--disk-owner', required=True, help="The user who owns the the opennebula disks")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
log.debug("{} {}".format(args, options))
|
||||||
|
|
||||||
|
disk_owner = get_user_model().objects.get(username=options['disk_owner'])
|
||||||
|
|
||||||
for one_vm in VMModel.objects.all():
|
for one_vm in VMModel.objects.all():
|
||||||
|
|
||||||
if not one_vm.last_host:
|
if not one_vm.last_host:
|
||||||
print("No VMHost for VM {} - VM might be on hold - skipping".format(one_vm.vmid))
|
log.warning("No VMHost for VM {} - VM might be on hold - skipping".format(one_vm.vmid))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vmhost = VMHost.objects.get(hostname=one_vm.last_host)
|
vmhost = VMHost.objects.get(hostname=one_vm.last_host)
|
||||||
except VMHost.DoesNotExist:
|
except VMHost.DoesNotExist:
|
||||||
print("VMHost {} does not exist, aborting".format(one_vm.last_host))
|
log.error("VMHost {} does not exist, aborting".format(one_vm.last_host))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
cores = one_vm.cores
|
cores = one_vm.cores
|
||||||
|
@ -98,9 +144,6 @@ class Command(BaseCommand):
|
||||||
owner = one_vm.owner
|
owner = one_vm.owner
|
||||||
status = 'active'
|
status = 'active'
|
||||||
|
|
||||||
# Total Amount of SSD Storage
|
|
||||||
# TODO: What would happen if the attached storage is not SSD but HDD?
|
|
||||||
|
|
||||||
ssd_size = sum([ disk['size_in_gb'] for disk in one_vm.disks if disk['pool_name'] in ['ssd', 'one'] ])
|
ssd_size = sum([ disk['size_in_gb'] for disk in one_vm.disks if disk['pool_name'] in ['ssd', 'one'] ])
|
||||||
hdd_size = sum([ disk['size_in_gb'] for disk in one_vm.disks if disk['pool_name'] in ['hdd'] ])
|
hdd_size = sum([ disk['size_in_gb'] for disk in one_vm.disks if disk['pool_name'] in ['hdd'] ])
|
||||||
|
|
||||||
|
@ -119,30 +162,32 @@ class Command(BaseCommand):
|
||||||
len(ipv4), len(ipv6))
|
len(ipv4), len(ipv6))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vm_product = VMProduct.objects.get(name=one_vm.uncloud_name)
|
vm_product = VMProduct.objects.get(extra_data__opennebula_id=one_vm.vmid)
|
||||||
except VMProduct.DoesNotExist:
|
except VMProduct.DoesNotExist:
|
||||||
order = Order.objects.create(
|
order = Order.objects.create(
|
||||||
owner=owner,
|
owner=owner,
|
||||||
creation_date=creation_date,
|
creation_date=creation_date,
|
||||||
starting_date=starting_date
|
starting_date=starting_date
|
||||||
# one_time_price=one_time_price,
|
|
||||||
# recurring_price=recurring_price,
|
|
||||||
# recurring_period=recurring_period
|
|
||||||
)
|
)
|
||||||
vm_product, _ = VMProduct.objects.update_or_create(
|
vm_product = VMProduct(
|
||||||
|
extra_data={ 'opennebula_id': one_vm.vmid },
|
||||||
name=one_vm.uncloud_name,
|
name=one_vm.uncloud_name,
|
||||||
defaults={
|
order=order
|
||||||
'cores': cores,
|
|
||||||
'ram_in_gb': ram_in_gb,
|
|
||||||
'owner': owner,
|
|
||||||
'vmhost': vmhost,
|
|
||||||
'order': order,
|
|
||||||
'status': status
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# we don't use update_or_create, as filtering by json AND setting json
|
||||||
|
# at the same time does not work
|
||||||
|
|
||||||
|
vm_product.vmhost = vmhost
|
||||||
|
vm_product.owner = owner
|
||||||
|
vm_product.cores = cores
|
||||||
|
vm_product.ram_in_gb = ram_in_gb
|
||||||
|
vm_product.status = status
|
||||||
|
|
||||||
|
vm_product.save()
|
||||||
|
|
||||||
# Create VMNetworkCards
|
# Create VMNetworkCards
|
||||||
create_nics(one_vm, vm_product)
|
create_nics(one_vm, vm_product)
|
||||||
|
|
||||||
# Create VMDiskImageProduct and VMDiskProduct
|
# Create VMDiskImageProduct and VMDiskProduct
|
||||||
create_disk_and_image(one_vm, vm_product)
|
sync_disk_and_image(one_vm, vm_product, disk_owner=disk_owner)
|
||||||
|
|
|
@ -3,6 +3,12 @@ from django.db import models
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
|
|
||||||
|
# ungleich specific
|
||||||
|
storage_class_mapping = {
|
||||||
|
'one': 'ssd',
|
||||||
|
'ssd': 'ssd',
|
||||||
|
'hdd': 'hdd'
|
||||||
|
}
|
||||||
|
|
||||||
class VM(models.Model):
|
class VM(models.Model):
|
||||||
vmid = models.IntegerField(primary_key=True)
|
vmid = models.IntegerField(primary_key=True)
|
||||||
|
@ -48,7 +54,8 @@ class VM(models.Model):
|
||||||
'pool_name': d['POOL_NAME'],
|
'pool_name': d['POOL_NAME'],
|
||||||
'image': d['IMAGE'],
|
'image': d['IMAGE'],
|
||||||
'source': d['SOURCE'],
|
'source': d['SOURCE'],
|
||||||
'source_type': d['TM_MAD']
|
'source_type': d['TM_MAD'],
|
||||||
|
'storage_class': storage_class_mapping[d['POOL_NAME']]
|
||||||
|
|
||||||
}
|
}
|
||||||
for d in disks
|
for d in disks
|
||||||
|
|
|
@ -5,4 +5,6 @@ from opennebula.models import VM
|
||||||
class OpenNebulaVMSerializer(serializers.HyperlinkedModelSerializer):
|
class OpenNebulaVMSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VM
|
model = VM
|
||||||
fields = '__all__'
|
fields = [ 'vmid', 'owner', 'data',
|
||||||
|
'uncloud_name', 'cores', 'ram_in_gb',
|
||||||
|
'disks', 'nics', 'ips' ]
|
||||||
|
|
|
@ -28,9 +28,9 @@ router = routers.DefaultRouter()
|
||||||
|
|
||||||
# VM
|
# VM
|
||||||
router.register(r'vm/snapshot', vmviews.VMSnapshotProductViewSet, basename='vmsnapshotproduct')
|
router.register(r'vm/snapshot', vmviews.VMSnapshotProductViewSet, basename='vmsnapshotproduct')
|
||||||
|
router.register(r'vm/diskimage', vmviews.VMDiskImageProductViewSet, basename='vmdiskimageproduct')
|
||||||
router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct')
|
router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct')
|
||||||
router.register(r'vm/image/mine', vmviews.VMDiskImageProductMineViewSet, basename='vmdiskimagemineproduct')
|
|
||||||
router.register(r'vm/image/public', vmviews.VMDiskImageProductPublicViewSet, basename='vmdiskimagepublicproduct')
|
|
||||||
|
|
||||||
# images the provider provides :-)
|
# images the provider provides :-)
|
||||||
# router.register(r'vm/image/official', vmviews.VMDiskImageProductPublicViewSet, basename='vmdiskimagepublicproduct')
|
# router.register(r'vm/image/official', vmviews.VMDiskImageProductPublicViewSet, basename='vmdiskimagepublicproduct')
|
||||||
|
|
|
@ -9,5 +9,7 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
model = get_user_model()
|
model = get_user_model()
|
||||||
fields = ['username', 'email', 'balance', 'maximum_credit' ]
|
fields = ['username', 'email', 'balance', 'maximum_credit' ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
balance = serializers.DecimalField(max_digits=AMOUNT_MAX_DIGITS,
|
balance = serializers.DecimalField(max_digits=AMOUNT_MAX_DIGITS,
|
||||||
decimal_places=AMOUNT_DECIMALS)
|
decimal_places=AMOUNT_DECIMALS)
|
||||||
|
|
|
@ -119,7 +119,7 @@ class VMDiskImageProduct(UncloudModel):
|
||||||
|
|
||||||
name = models.CharField(max_length=256)
|
name = models.CharField(max_length=256)
|
||||||
is_os_image = models.BooleanField(default=False)
|
is_os_image = models.BooleanField(default=False)
|
||||||
is_public = models.BooleanField(default=False)
|
is_public = models.BooleanField(default=False, editable=False) # only allow admins to set this
|
||||||
|
|
||||||
size_in_gb = models.FloatField(null=True, blank=True)
|
size_in_gb = models.FloatField(null=True, blank=True)
|
||||||
import_url = models.URLField(null=True, blank=True)
|
import_url = models.URLField(null=True, blank=True)
|
||||||
|
|
|
@ -84,8 +84,9 @@ class VMProductSerializer(serializers.ModelSerializer):
|
||||||
model = VMProduct
|
model = VMProduct
|
||||||
fields = ['uuid', 'order', 'owner', 'status', 'name',
|
fields = ['uuid', 'order', 'owner', 'status', 'name',
|
||||||
'cores', 'ram_in_gb', 'recurring_period',
|
'cores', 'ram_in_gb', 'recurring_period',
|
||||||
'snapshots', 'disks' ]
|
'snapshots', 'disks',
|
||||||
read_only_fields = ['uuid', 'order', 'owner', 'status']
|
'extra_data' ]
|
||||||
|
read_only_fields = ['uuid', 'order', 'owner', 'status' ]
|
||||||
|
|
||||||
# Custom field used at creation (= ordering) only.
|
# Custom field used at creation (= ordering) only.
|
||||||
recurring_period = serializers.ChoiceField(
|
recurring_period = serializers.ChoiceField(
|
||||||
|
|
|
@ -24,7 +24,7 @@ class VMHostViewSet(viewsets.ModelViewSet):
|
||||||
queryset = VMHost.objects.all()
|
queryset = VMHost.objects.all()
|
||||||
permission_classes = [permissions.IsAdminUser]
|
permission_classes = [permissions.IsAdminUser]
|
||||||
|
|
||||||
class VMDiskImageProductMineViewSet(viewsets.ModelViewSet):
|
class VMDiskImageProductViewSet(viewsets.ModelViewSet):
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
serializer_class = VMDiskImageProductSerializer
|
serializer_class = VMDiskImageProductSerializer
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class VMDiskImageProductMineViewSet(viewsets.ModelViewSet):
|
||||||
if self.request.user.is_superuser:
|
if self.request.user.is_superuser:
|
||||||
obj = VMDiskImageProduct.objects.all()
|
obj = VMDiskImageProduct.objects.all()
|
||||||
else:
|
else:
|
||||||
obj = VMDiskImageProduct.objects.filter(owner=self.request.user)
|
obj = VMDiskImageProduct.objects.filter(owner=self.request.user) | VMDiskImageProduct.objects.filter(is_public=True)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue