mirror of
				https://github.com/retspen/webvirtcloud
				synced 2025-07-31 12:41:08 +00:00 
			
		
		
		
	added new feature: create and attach new volume to existing instance
move wvmCreate.get_cache_modes() to wvmConnect add wvmConnect.get_busses(), wvmConnect.get_image_formats(), used in forms add corresponding default values to settings (INSTANCE_VOLUME_DEFAULT_FORMAT INSTANCE_VOLUME_DEFAULT_BUS INSTANCE_VOLUME_DEFAULT_CACHE)
This commit is contained in:
		
							parent
							
								
									b095a77da5
								
							
						
					
					
						commit
						53f5518706
					
				
					 6 changed files with 170 additions and 15 deletions
				
			
		| 
						 | 
				
			
			@ -309,6 +309,11 @@
 | 
			
		|||
                                                    {% trans "Resize Instance" %}
 | 
			
		||||
                                                </a>
 | 
			
		||||
                                            </li>
 | 
			
		||||
                                            <li role="presentation">
 | 
			
		||||
                                                <a href="#addvolume" aria-controls="addvolume" role="tab" data-toggle="tab">
 | 
			
		||||
                                                    {% trans "Add New Volume" %}
 | 
			
		||||
                                                </a>
 | 
			
		||||
                                            </li>
 | 
			
		||||
                                        </ul>
 | 
			
		||||
                                        <!-- Tab panes -->
 | 
			
		||||
                                        <div class="tab-content">
 | 
			
		||||
| 
						 | 
				
			
			@ -390,6 +395,88 @@
 | 
			
		|||
                                                {% endif %}
 | 
			
		||||
                                                <div class="clearfix"></div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div role="tabpanel" class="tab-pane tab-pane-bordered" id="addvolume">
 | 
			
		||||
                                                {% if request.user.is_superuser or userinstace.is_change %}
 | 
			
		||||
                                                    <form class="form-horizontal" method="post" role="form">{% csrf_token %}
 | 
			
		||||
                                                        <p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <select name="storage" class="form-control image-format">
 | 
			
		||||
                                                                {% for storage in storages %}
 | 
			
		||||
                                                                    <option value="{{ storage }}">{{ storage }}</option>
 | 
			
		||||
                                                                {% endfor %}
 | 
			
		||||
                                                                </select>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Name" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                            <div class="col-sm-2">
 | 
			
		||||
                                                                <select name="extension" class="form-control image-format">
 | 
			
		||||
                                                                {% for format in formats %}
 | 
			
		||||
                                                                    <option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
 | 
			
		||||
                                                                {% endfor %}
 | 
			
		||||
                                                                </select>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Format" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <select name="format" class="form-control image-format">
 | 
			
		||||
                                                                {% for format in formats %}
 | 
			
		||||
                                                                    <option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
 | 
			
		||||
                                                                {% endfor %}
 | 
			
		||||
                                                                </select>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Size" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+">
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                            <label class="col-sm-1 control-label">{% trans "GB" %}</label>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <select name="bus" class="form-control image-format">
 | 
			
		||||
                                                                {% for bus in busses %}
 | 
			
		||||
                                                                    <option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
 | 
			
		||||
                                                                {% endfor %}
 | 
			
		||||
                                                                </select>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <select name="cache" class="form-control image-format">
 | 
			
		||||
                                                                {% for mode, name in cache_modes %}
 | 
			
		||||
                                                                    <option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
 | 
			
		||||
                                                                {% endfor %}
 | 
			
		||||
                                                                </select>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="form-group meta-prealloc">
 | 
			
		||||
                                                            <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Metadata" %}</label>
 | 
			
		||||
                                                            <div class="col-sm-4">
 | 
			
		||||
                                                                <input type="checkbox" name="meta_prealloc" value="true">
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        {% ifequal status 5 %}
 | 
			
		||||
                                                            <button type="submit" class="btn btn-lg btn-success pull-right" name="addvolume">{% trans "Add volume" %}</button>
 | 
			
		||||
                                                        {% else %}
 | 
			
		||||
                                                            <button class="btn btn-lg btn-success pull-right disabled">{% trans "Add volume" %}</button>
 | 
			
		||||
                                                        {% endifequal %}
 | 
			
		||||
                                                    </form>
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                    {% trans "You don't have permission for resizing instance" %}
 | 
			
		||||
                                                    <button class="btn btn-lg btn-success pull-right disabled">{% trans "Add volume" %}</button>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                                <div class="clearfix"></div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </div>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import json
 | 
			
		|||
import socket
 | 
			
		||||
import crypt
 | 
			
		||||
import re
 | 
			
		||||
from string import letters, digits
 | 
			
		||||
import string
 | 
			
		||||
from random import choice
 | 
			
		||||
from bisect import insort
 | 
			
		||||
from django.http import HttpResponse, HttpResponseRedirect
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +18,7 @@ from accounts.models import UserInstance, UserSSHKey
 | 
			
		|||
from vrtManager.hostdetails import wvmHostDetails
 | 
			
		||||
from vrtManager.instance import wvmInstance, wvmInstances
 | 
			
		||||
from vrtManager.connection import connection_manager
 | 
			
		||||
from vrtManager.create import wvmCreate
 | 
			
		||||
from vrtManager.util import randomPasswd
 | 
			
		||||
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE
 | 
			
		||||
from webvirtcloud.settings import QEMU_KEYMAPS, QEMU_CONSOLE_TYPES
 | 
			
		||||
| 
						 | 
				
			
			@ -279,13 +280,25 @@ def instance(request, compute_id, vname):
 | 
			
		|||
                msg += " (%s > %s)" % (disk_size, ua.max_disk_size)
 | 
			
		||||
        return msg
 | 
			
		||||
 | 
			
		||||
    def get_new_disk_dev(disks, bus):
 | 
			
		||||
        if bus == "virtio":
 | 
			
		||||
            dev_base = "vd"
 | 
			
		||||
        else:
 | 
			
		||||
            dev_base = "sd"
 | 
			
		||||
        existing_devs = [ disk['dev'] for disk in disks ]
 | 
			
		||||
        for l in string.lowercase:
 | 
			
		||||
            dev = dev_base + l
 | 
			
		||||
            if dev not in existing_devs:
 | 
			
		||||
                return dev
 | 
			
		||||
        raise Exception(_('None available device name'))
 | 
			
		||||
            
 | 
			
		||||
    try:
 | 
			
		||||
        conn = wvmInstance(compute.hostname,
 | 
			
		||||
                           compute.login,
 | 
			
		||||
                           compute.password,
 | 
			
		||||
                           compute.type,
 | 
			
		||||
                           vname)
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        status = conn.get_status()
 | 
			
		||||
        autostart = conn.get_autostart()
 | 
			
		||||
        vcpu = conn.get_vcpu()
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +331,13 @@ def instance(request, compute_id, vname):
 | 
			
		|||
        console_passwd = conn.get_console_passwd()
 | 
			
		||||
        clone_free_names = get_clone_free_names()
 | 
			
		||||
        user_quota_msg = check_user_quota(0, 0, 0, 0)
 | 
			
		||||
        storages = sorted(conn.get_storages())
 | 
			
		||||
        cache_modes = sorted(conn.get_cache_modes().items())
 | 
			
		||||
        default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE
 | 
			
		||||
        default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT
 | 
			
		||||
        formats = conn.get_image_formats()
 | 
			
		||||
        default_bus = settings.INSTANCE_VOLUME_DEFAULT_BUS
 | 
			
		||||
        busses = conn.get_busses()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            instance = Instance.objects.get(compute_id=compute_id, name=vname)
 | 
			
		||||
| 
						 | 
				
			
			@ -457,6 +477,27 @@ def instance(request, compute_id, vname):
 | 
			
		|||
                    addlogmsg(request.user.username, instance.name, msg)
 | 
			
		||||
                    return HttpResponseRedirect(request.get_full_path() + '#resize')
 | 
			
		||||
 | 
			
		||||
            if 'addvolume' in request.POST and (request.user.is_superuser or userinstace.is_change):
 | 
			
		||||
                connCreate = wvmCreate(compute.hostname,
 | 
			
		||||
                                   compute.login,
 | 
			
		||||
                                   compute.password,
 | 
			
		||||
                                   compute.type)
 | 
			
		||||
                storage = request.POST.get('storage', '')
 | 
			
		||||
                name = request.POST.get('name', '')
 | 
			
		||||
                extension = request.POST.get('extension', '')
 | 
			
		||||
                format = request.POST.get('format', '')
 | 
			
		||||
                size = request.POST.get('size', 0)
 | 
			
		||||
                meta_prealloc = request.POST.get('meta_prealloc', False)
 | 
			
		||||
                bus = request.POST.get('bus', '')
 | 
			
		||||
                cache = request.POST.get('cache', '')
 | 
			
		||||
                target = get_new_disk_dev(disks, bus)
 | 
			
		||||
                
 | 
			
		||||
                path = connCreate.create_volume(storage, name, size, format, meta_prealloc, extension)
 | 
			
		||||
                conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus)
 | 
			
		||||
                msg = _('Attach new disk')
 | 
			
		||||
                addlogmsg(request.user.username, instance.name, msg)
 | 
			
		||||
                return HttpResponseRedirect(request.get_full_path() + '#resize')
 | 
			
		||||
 | 
			
		||||
            if 'umount_iso' in request.POST:
 | 
			
		||||
                image = request.POST.get('path', '')
 | 
			
		||||
                dev = request.POST.get('umount_iso', '')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -380,6 +380,25 @@ class wvmConnect(object):
 | 
			
		|||
            interface.append(inface)
 | 
			
		||||
        return interface
 | 
			
		||||
 | 
			
		||||
    def get_cache_modes(self):
 | 
			
		||||
        """Get cache available modes"""
 | 
			
		||||
        return {
 | 
			
		||||
            'default': 'Default',
 | 
			
		||||
            'none': 'Disabled',
 | 
			
		||||
            'writethrough': 'Write through',
 | 
			
		||||
            'writeback': 'Write back',
 | 
			
		||||
            'directsync': 'Direct sync',  # since libvirt 0.9.5
 | 
			
		||||
            'unsafe': 'Unsafe',  # since libvirt 0.9.7
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
    def get_busses(self):
 | 
			
		||||
        """Get available busses"""
 | 
			
		||||
        return [ 'ide', 'scsi', 'usb', 'virtio' ]
 | 
			
		||||
 | 
			
		||||
    def get_image_formats(self):
 | 
			
		||||
        """Get available image formats"""
 | 
			
		||||
        return [ 'raw', 'qcow', 'qcow2' ]
 | 
			
		||||
 | 
			
		||||
    def get_iface(self, name):
 | 
			
		||||
        return self.wvm.interfaceLookupByName(name)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,23 +48,12 @@ class wvmCreate(wvmConnect):
 | 
			
		|||
        """Get guest capabilities"""
 | 
			
		||||
        return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
 | 
			
		||||
 | 
			
		||||
    def get_cache_modes(self):
 | 
			
		||||
        """Get cache available modes"""
 | 
			
		||||
        return {
 | 
			
		||||
            'default': 'Default',
 | 
			
		||||
            'none': 'Disabled',
 | 
			
		||||
            'writethrough': 'Write through',
 | 
			
		||||
            'writeback': 'Write back',
 | 
			
		||||
            'directsync': 'Direct sync',  # since libvirt 0.9.5
 | 
			
		||||
            'unsafe': 'Unsafe',  # since libvirt 0.9.7
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def create_volume(self, storage, name, size, format='qcow2', metadata=False):
 | 
			
		||||
    def create_volume(self, storage, name, size, format='qcow2', metadata=False, image_extension='img'):
 | 
			
		||||
        size = int(size) * 1073741824
 | 
			
		||||
        stg = self.get_storage(storage)
 | 
			
		||||
        storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
 | 
			
		||||
        if storage_type == 'dir':
 | 
			
		||||
            name += '.img'
 | 
			
		||||
            name += '.' + image_extension
 | 
			
		||||
            alloc = 0
 | 
			
		||||
        else:
 | 
			
		||||
            alloc = size
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -340,6 +340,22 @@ class wvmInstance(wvmConnect):
 | 
			
		|||
            xmldom = ElementTree.tostring(tree)
 | 
			
		||||
        self._defineXML(xmldom)
 | 
			
		||||
 | 
			
		||||
    def attach_disk(self, source, target, sourcetype='file', type='disk', driver='qemu', subdriver='raw', cache='none', targetbus='ide'):
 | 
			
		||||
        tree = ElementTree.fromstring(self._XMLDesc(0))
 | 
			
		||||
        xml_disk = """
 | 
			
		||||
        <disk type='%s' device='%s'>
 | 
			
		||||
          <driver name='%s' type='%s' cache='%s'/>
 | 
			
		||||
          <source file='%s'/>
 | 
			
		||||
          <target dev='%s' bus='%s'/>
 | 
			
		||||
        </disk>
 | 
			
		||||
        """ % (sourcetype, type, driver, subdriver, cache, source, target, targetbus)
 | 
			
		||||
        if self.get_status() == 5:
 | 
			
		||||
            devices = tree.find('devices')
 | 
			
		||||
            elm_disk = ElementTree.fromstring(xml_disk)
 | 
			
		||||
            devices.append(elm_disk)
 | 
			
		||||
            xmldom = ElementTree.tostring(tree)
 | 
			
		||||
            self._defineXML(xmldom)
 | 
			
		||||
 | 
			
		||||
    def cpu_usage(self):
 | 
			
		||||
        cpu_usage = {}
 | 
			
		||||
        if self.get_status() == 1:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,3 +124,6 @@ ALLOW_EMPTY_PASSWORD = True
 | 
			
		|||
SHOW_ACCESS_ROOT_PASSWORD = False
 | 
			
		||||
SHOW_ACCESS_SSH_KEYS = False
 | 
			
		||||
SHOW_PROFILE_EDIT_PASSWORD = False
 | 
			
		||||
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
 | 
			
		||||
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
 | 
			
		||||
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue