diff --git a/computes/templates/overview.html b/computes/templates/overview.html index 81398b9..e6f824f 100644 --- a/computes/templates/overview.html +++ b/computes/templates/overview.html @@ -52,14 +52,33 @@ </div> <div class="col-xs-8 col-sm-9"> <p>{{ hostname }}</p> - <p>{% for arch, hpv in hypervisor.items %} - <span class="glyphicon glyphicon-chevron-right"></span> - <span class="label label-default">{{ arch }}</span> - {% for h in hpv %} - <span class="label label-primary">{{ h }}</span> - {% endfor %} - {% endfor %} - </p> + <p><div class="btn-group" style="margin-left: 8px"> + {% for arch, hpv in hypervisor.items %} + {% if forloop.counter < 4 %} + <div class="btn-group" > + <button class="btn btn-xs btn-default dropdown-toggle" type="button" id="dropdownMenuButton{{ forloop.counter0 }}" data-toggle="dropdown"> + {{ arch }} + <span class="caret"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ forloop.counter0 }}" role="menu"> + {% for h in hpv %} + <li><a href="#">{{ h }}</a></li> + {% endfor %} + </ul> + </div> + {% else %} + <button class="btn btn-xs btn-default dropdown-toggle" type="button" id="dropdownMenuButton{{ forloop.counter0 }}" data-toggle="dropdown"> + {{ hypervisor|length }} {% trans 'more' %}... + <span class="caret"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ forloop.counter0 }}" role="menu"> + {% for arc in hypervisor.keys %} + <li><a href="#">{{ arc }}</a></li> + {% endfor %} + </ul> + {% endif %} + {% endfor %} + </div></p> <p>{{ emulator }}</p> <p> <span class="label label-default">Qemu</span> diff --git a/dev/libvirt-bootstrap.sh b/dev/libvirt-bootstrap.sh index c01a071..0f878a9 100644 --- a/dev/libvirt-bootstrap.sh +++ b/dev/libvirt-bootstrap.sh @@ -396,7 +396,8 @@ install_centos_post() { exit 1 fi if [ -f /etc/libvirt/qemu.conf ]; then - sed -i 's/#vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf else echoerror "/etc/libvirt/qemu.conf not found. Exiting..." exit 1 @@ -487,7 +488,8 @@ install_fedora_post() { exit 1 fi if [ -f /etc/libvirt/qemu.conf ]; then - sed -i 's/#vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf else echoerror "/etc/libvirt/qemu.conf not found. Exiting..." exit 1 @@ -548,7 +550,8 @@ install_opensuse_post() { exit 1 fi if [ -f /etc/libvirt/qemu.conf ]; then - sed -i 's/#vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf else echoerror "/etc/libvirt/qemu.conf not found. Exiting..." exit 1 @@ -618,11 +621,9 @@ install_ubuntu_post() { exit 1 fi if [ -f /etc/libvirt/qemu.conf ]; then - if ([ $DISTRO_MAJOR_VERSION -eq 12 ] && [ $DISTRO_MINOR_VERSION -eq 04 ]); then - sed -i 's/# vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf - else - sed -i 's/#vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf - fi + sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + else echoerror "/etc/libvirt/qemu.conf not found. Exiting..." exit 1 @@ -663,9 +664,9 @@ daemons_running_ubuntu() { install_debian() { apt-get update || return 1 if [ $DISTRO_MAJOR_VERSION -lt 10 ]; then - apt-get -y install qemu-kvm libvirt-bin bridge-utils sasl2-bin python-guestfs supervisor || return 1 + apt-get -y install qemu-kvm libvirt-bin bridge-utils sasl2-bin python-guestfs supervisor || return 1 else - apt-get -y install qemu qemu-kvm qemu-system qemu-utils libvirt-clients libvirt-daemon-system sasl2-bin virtinst supervisor || return 1 + apt-get -y install qemu qemu-kvm qemu-system qemu-utils libvirt-clients libvirt-daemon-system sasl2-bin virtinst supervisor || return 1 fi return 0 } @@ -697,11 +698,19 @@ install_debian_post() { exit 1 fi if [ -f /etc/libvirt/qemu.conf ]; then - sed -i 's/# vnc_listen/vnc_listen/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*vnc_listen.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf + sed -i 's/#[ ]*spice_listen.*/spice_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf else echoerror "/etc/libvirt/qemu.conf not found. Exiting..." exit 1 fi + if [ -f /etc/sasl2/libvirt.conf ]; then + sed -i 's/: gssapi/: digest-md5/g' /etc/sasl2/libvirt.conf + sed -i 's/#sasldb_path/sasldb_path/g' /etc/sasl2/libvirt.conf + else + echoerror "/etc/sasl2/libvirt.conf not found. Exiting..." + exit 1 + fi if [ -f /etc/supervisor/supervisord.conf ]; then wget -O /usr/local/bin/gstfsd https://raw.githubusercontent.com/retspen/webvirtcloud/master/conf/daemon/gstfsd chmod +x /usr/local/bin/gstfsd diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 00ab3bb..5b6d3cd 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -363,10 +363,11 @@ <div class="tab-content"> <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm_cpu"> {% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %} + {% if status == 5 or not vcpus %} <form class="form-horizontal" method="post" role="form">{% csrf_token %} <p style="font-weight:bold;">{% trans "Logical host CPUs" %} : {{ vcpu_host }}</p> <div class="form-group"> - <label class="col-sm-4 control-label" style="font-weight:normal;"> {% trans "Current allocation" %}</label> + <label class="col-sm-4 control-label" style="font-weight:normal;"> {% trans "Current Allocation" %}</label> <div class="col-sm-4"> <select name="cur_vcpu" class="form-control"> {% for cpu in vcpu_range %} @@ -380,7 +381,7 @@ </div> </div> <div class="form-group"> - <label class="col-sm-4 control-label" style="font-weight:normal;">{% trans "Maximum allocation" %}</label> + <label class="col-sm-4 control-label" style="font-weight:normal;">{% trans "Maximum Allocation" %}</label> <div class="col-sm-4"> <select name="vcpu" class="form-control"> {% for cpu in vcpu_range %} @@ -396,6 +397,28 @@ <button class="btn btn-lg btn-success pull-right disabled">{% trans "Resize" %}</button> {% endifequal %} </form> + <div class="clearfix"></div> + {% else %} + <p style="font-weight:bold;">{% trans "Logical Instance Active/Maximum CPUs" %} : {{ cur_vcpu }} / {{ vcpu }} </p> + <div class="col-sm-3"></div> + <div class="col-sm-6"> + {% for id, vcpu in vcpus.items %} + <form method="post" role="form">{% csrf_token %} + <div class="col-sm-3"> + <input name="id" value="{{ id }}" hidden/> + {% if vcpu.enabled == 'yes' and vcpu.hotpluggable == "yes" %} + <button type="submit" class="btn btn-block btn-success" value="False" name="set_vcpu" title="Disable">{{ id }}</button> + {% elif vcpu.enabled == 'yes' and vcpu.hotpluggable == "no" %} + <button type="button" class="btn btn btn-block btn-info" title="Constant">{{ id }}</button> + {% else %} + <button type="submit" class="btn btn btn-block btn-default" value="True" name="set_vcpu" title="Enable">{{ id }}</button> + {% endif %} + </div> + </form> + {% endfor %} + </div> + <div class="col-sm-3"></div> + {% endif %} {% else %} {% trans "You don't have permission for resizing instance" %} <button class="btn btn-lg btn-success pull-right disabled">{% trans "Resize" %}</button> @@ -1358,6 +1381,30 @@ </form> <div class="clearfix"></div> </div> + <div class="well"> + <p>{% trans "To set instance vCPUs hotpluggable" %}</p> + <form class="form-horizontal" method="post" role="form">{% csrf_token %} + <div class="form-group"> + <label for="video_model_select" class="col-sm-3 control-label">{% trans "vCPU Hot Plug" %}</label> + <div class="col-sm-6"> + <div class="input-group"> + <select id="vcpu_hotplug" class="form-control" name="vcpu_hotplug"> + <option value="True" {% if vcpus %} selected {% endif %}>{% trans 'Enabled' %}</option> + <option value="False" {% if not vcpus %} selected {% endif %}>{% trans 'Disabled' %}</option> + </select> + <span class="input-group-btn"> + {% if status == 5 %} + <button type="submit" class="btn btn-success" name="set_vcpu_hotplug">{% trans "Set" %}</button> + {% else %} + <button class="btn btn-success" name="set_vcpu_hotplug" disabled>{% trans "Set" %}</button> + {% endif %} + </span> + </div> + </div> + </div> + </form> + <div class="clearfix"></div> + </div> </div> {% endif %} </div> diff --git a/instances/views.py b/instances/views.py index 51246ce..019234a 100644 --- a/instances/views.py +++ b/instances/views.py @@ -26,6 +26,7 @@ from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE from logs.views import addlogmsg from django.conf import settings from django.contrib import messages +from collections import OrderedDict @login_required @@ -44,7 +45,7 @@ def allinstances(request): :param request: :return: """ - all_host_vms = {} + all_host_vms = OrderedDict() error_messages = [] computes = Compute.objects.all().order_by("name") @@ -76,7 +77,7 @@ def instances(request, compute_id): :param compute_id :return: """ - all_host_vms = {} + all_host_vms = OrderedDict() error_messages = [] compute = get_object_or_404(Compute, pk=compute_id) @@ -266,6 +267,7 @@ def instance(request, compute_id, vname): boot_order = conn.get_bootorder() vcpu = conn.get_vcpu() cur_vcpu = conn.get_cur_vcpu() + vcpus = conn.get_vcpus() uuid = conn.get_uuid() memory = conn.get_memory() cur_memory = conn.get_cur_memory() @@ -668,6 +670,29 @@ def instance(request, compute_id, vname): addlogmsg(request.user.username, instance.name, msg) return HttpResponseRedirect(request.get_full_path() + '#suspend') + if 'set_vcpu' in request.POST: + id = request.POST.get('id', '') + enabled = request.POST.get('set_vcpu', '') + if enabled == 'True': + conn.set_vcpu(id, 1) + else: + conn.set_vcpu(id, 0) + msg = _("vCPU {} is enabled={}".format(id, enabled)) + messages.success(request, msg) + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#resize') + + if 'set_vcpu_hotplug' in request.POST: + status = request.POST.get('vcpu_hotplug', '') + msg = _("vCPU Hot-plug is enabled={}".format(status)) + try: + conn.set_vcpu_hotplug(eval(status)) + except libvirtError as lib_err: + messages.error(request, lib_err.message) + messages.success(request, msg) + addlogmsg(request.user.username, instance.name, msg) + return HttpResponseRedirect(request.get_full_path() + '#resize') + if 'set_autostart' in request.POST: conn.set_autostart(1) msg = _("Set autostart") @@ -1036,7 +1061,7 @@ def get_host_instances(request, comp): inst_on_db = Instance(compute_id=comp["id"], name=inst_name, uuid=info['uuid']) inst_on_db.save() - all_host_vms = {} + all_host_vms = OrderedDict() status = connection_manager.host_is_up(comp.type, comp.hostname) if status is True: diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 9fe7d67..08ab9c2 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -5,10 +5,12 @@ try: VIR_DOMAIN_AFFECT_LIVE, VIR_DOMAIN_AFFECT_CONFIG except: from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE + from vrtManager import util from xml.etree import ElementTree from lxml import etree from datetime import datetime +from collections import OrderedDict from vrtManager.connection import wvmConnect from vrtManager.storage import wvmStorage from webvirtcloud.settings import QEMU_CONSOLE_TYPES @@ -184,6 +186,18 @@ class wvmInstance(wvmConnect): if cur_vcpu: return int(cur_vcpu) + def get_vcpus(self): + vcpus = OrderedDict() + tree = etree.fromstring(self._XMLDesc(0)) + for vcpu in tree.xpath("/domain/vcpus/vcpu"): + id = vcpu.get("id") + enabled = vcpu.get("enabled") + hotplug = vcpu.get("hotpluggable") + order = vcpu.get("order") + vcpus[id] = {"enabled": enabled, "hotpluggable": hotplug, "order": order} + + return vcpus + def get_memory(self): mem = util.get_xml_path(self._XMLDesc(0), "/domain/memory") return int(mem) / 1024 @@ -554,6 +568,35 @@ class wvmInstance(wvmConnect): cpu_usage['cpu'] = 0 return cpu_usage + def set_vcpu(self, cpu_id, enabled): + self.instance.setVcpu(str(cpu_id), enabled) + + def set_vcpu_hotplug(self, status, vcpus_hotplug=0): + """ vcpus_hotplug = 0 make all vpus hotpluggable """ + vcpus_hotplug = int(self.get_vcpu()) if vcpus_hotplug == 0 else vcpus_hotplug + if self.get_status() == 5: # shutoff + if status: + xml = """ <vcpus>""" + xml += """<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>""" + for i in range(1, vcpus_hotplug): + xml += """<vcpu id='{}' enabled='yes' hotpluggable='yes' order='{}'/>""".format(i, i+1) + xml += """</vcpus>""" + + tree = etree.fromstring(self._XMLDesc(0)) + vcpus = tree.xpath("/domain/vcpus") + if not vcpus: + tree.append(etree.fromstring(xml)) + self._defineXML(etree.tostring(tree)) + else: + tree = etree.fromstring(self._XMLDesc(0)) + vcpus = tree.xpath("/domain/vcpus") + for vcpu in vcpus: + parent = vcpu.getparent() + parent.remove(vcpu) + self._defineXML(etree.tostring(tree)) + else: + raise libvirtError("Please shutdown the instance then try to enable vCPU hotplug") + def mem_usage(self): mem_usage = {} if self.get_status() == 1: @@ -785,51 +828,27 @@ class wvmInstance(wvmConnect): parent.append(etree.fromstring(video_xml)) self._defineXML(etree.tostring(tree)) - def resize(self, cur_memory, memory, cur_vcpu, vcpu, disks=[]): - """ - Function change ram and cpu on vds. - """ - memory = int(memory) * 1024 - cur_memory = int(cur_memory) * 1024 - # if dom is running change only ram - if self.get_status() == VIR_DOMAIN_RUNNING: - self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_LIVE) - self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_CONFIG) - return - - xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE) - tree = ElementTree.fromstring(xml) - - set_mem = tree.find('memory') - set_mem.text = str(memory) - set_cur_mem = tree.find('currentMemory') - set_cur_mem.text = str(cur_memory) - set_vcpu = tree.find('vcpu') - set_vcpu.text = vcpu - set_vcpu.set('current', cur_vcpu) - - for disk in disks: - source_dev = disk['path'] - vol = self.get_volume_by_path(source_dev) - vol.resize(disk['size_new']) - - new_xml = ElementTree.tostring(tree) - self._defineXML(new_xml) - def resize_cpu(self, cur_vcpu, vcpu): """ - Function change ram and cpu on vds. + Function change ram and cpu on instance. """ + is_vcpus_enabled = self.get_vcpus() + if is_vcpus_enabled: + self.set_vcpu_hotplug(False) + xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE) - tree = ElementTree.fromstring(xml) + tree = etree.fromstring(xml) set_vcpu = tree.find('vcpu') set_vcpu.text = vcpu set_vcpu.set('current', cur_vcpu) - new_xml = ElementTree.tostring(tree) + new_xml = etree.tostring(tree) self._defineXML(new_xml) + if is_vcpus_enabled: + self.set_vcpu_hotplug(True, int(cur_vcpu)) + def resize_mem(self, cur_memory, memory): """ Function change ram and cpu on vds. diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 88078be..58469f0 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -103,8 +103,8 @@ WS_PORT = 6080 # Websock host WS_HOST = '0.0.0.0' -# Websock public port -WS_PUBLIC_PORT = '443' +# Websock public port - 80 or 443 if reverse-proxy, else 6080 +WS_PUBLIC_PORT = 6080 # Websock public host WS_PUBLIC_HOST = None