[PATCH] Changed the proxy.py
by stephan.conrad@gmail.com
From: Stephan Conrad <stephan.conrad(a)gmail.com>
I changed the proxy.py to search for the http user which is used in Arch Linux.
The new way to lookup if a user exist do not use an exception and is also extenable for other users.
Stephan Conrad (1):
Changed the proxy.py to search for the http user used in Arch Linux
src/wok/proxy.py | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
--
2.6.2
9 years
[PATCH] [Kimchi] Restore control/config.py
by Aline Manera
This file was wrongly removed by commit 14eefb
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
control/config.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 control/config.py
diff --git a/control/config.py b/control/config.py
new file mode 100644
index 0000000..15df68f
--- /dev/null
+++ b/control/config.py
@@ -0,0 +1,57 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013-2014
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from wok.control.base import Collection, Resource
+from wok.control.utils import UrlSubNode
+
+
+@UrlSubNode("config")
+class Config(Resource):
+ def __init__(self, model, id=None):
+ super(Config, self).__init__(model, id)
+ self.capabilities = Capabilities(self.model)
+ self.distros = Distros(model)
+
+ @property
+ def data(self):
+ return self.info
+
+
+class Capabilities(Resource):
+ def __init__(self, model, id=None):
+ super(Capabilities, self).__init__(model, id)
+
+ @property
+ def data(self):
+ return self.info
+
+
+class Distros(Collection):
+ def __init__(self, model):
+ super(Distros, self).__init__(model)
+ self.resource = Distro
+
+
+class Distro(Resource):
+ def __init__(self, model, ident):
+ super(Distro, self).__init__(model, ident)
+
+ @property
+ def data(self):
+ return self.info
--
2.5.0
9 years
Entering code freeze for Wok/Kimchi 2.0
by Aline Manera
Hi all,
We are now entering on code freeze, which means only bug fixes will be
accepted on master branch from now on.
To allow parallel development, Wok and Kimchi will have a next branch in
which new features can be merged.
After Wok/Kimchi 2.0 announcement, the next branch will be merged on
master branch, and then, we turn back with a single branch (master) per
repository.
There is a hug list of bugs open on github, so please, let's do our best
to have the most bugs closed till the release.
Kimchi issues: https://github.com/kimchi-project/kimchi/issues
Thank you,
Aline Manera
9 years
[PATCH] font path for Arch Linux
by stephan.conrad@gmail.com
From: Stephan Conrad <stephan.conrad(a)gmail.com>
I added a path to the font config for Arch Linux
Stephan Conrad (1):
Added the font path /usr/share/fonts/TTF to support archlinux.
src/wok/config.py.in | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
--
2.6.2
9 years
[PATCH] [Wok] Form validation classes to new-ui
by sguimaraes943@gmail.com
From: samhenri <samuel.guimaraes(a)eldorado.org.br>
This patch fixes Bootstrap .has-error, .has-succes and .has-warning classes with correct colors.
The new classes can be seen in action in Live Migration modal window.
samhenri (1):
Adding form validation classes to new-ui
ui/css/src/modules/_validation.scss | 11 +++++++++++
ui/css/src/wok.scss | 2 ++
2 files changed, 13 insertions(+)
create mode 100644 ui/css/src/modules/_validation.scss
--
1.9.3
9 years
[[kimchi] PATCH] Bugfix 782: Fix creating storage pools
by Jose Ricardo Ziviani
- Javascript code was getting wrong values from storage pool form, so
it was not possible create any storage pools.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.storagepool_add_main.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
index 39c0ac7..39b2cb7 100644
--- a/ui/js/src/kimchi.storagepool_add_main.js
+++ b/ui/js/src/kimchi.storagepool_add_main.js
@@ -360,7 +360,8 @@ kimchi.addPool = function(event) {
if (kimchi.validateForm()) {
var formData = $('#form-pool-add').serializeObject();
delete formData.authname;
- var poolType = $('#poolTypeId').selectMenu('value');
+ var poolType = $('#poolTypeInputId').val();
+ formData.type = poolType;
if (poolType === 'dir') {
formData.path = $('#pathId').val();
} else if (poolType === 'logical') {
@@ -423,4 +424,4 @@ kimchi.addPool = function(event) {
storagePoolAddingFunc();
}
}
-};
\ No newline at end of file
+};
--
1.9.1
9 years
[PATCH] [Kimchi] Bug fix: Update UI according to changes on /networks API
by Aline Manera
The /networks API was updated to allow user creates Linux bridges besides
macvtap bridges.
But as the UI needs to be updated accordingly to add this option, Kimchi 2.0
will continue to use macvtap bridge as default.
This patch also fixes the issue while creating a brigded VLAN tagging.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.network.js | 2 +-
ui/js/src/kimchi.network_add_main.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index 539010e..c1c1ebd 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -35,7 +35,7 @@ kimchi.initNetworkListView = function() {
in_use : data[i].in_use,
state : data[i].state === "active" ? "up" : "down"
};
- if (data[i].connection === "bridge") {
+ if (data[i].connection === "macvtap") {
network.type = kimchi.NETWORK_TYPE_BRIDGE;
} else {
network.type = data[i].connection;
diff --git a/ui/js/src/kimchi.network_add_main.js b/ui/js/src/kimchi.network_add_main.js
index ea69421..c28c9ce 100644
--- a/ui/js/src/kimchi.network_add_main.js
+++ b/ui/js/src/kimchi.network_add_main.js
@@ -34,7 +34,7 @@ kimchi.startNetworkCreation = function() {
connection: network.type
};
if (network.type === kimchi.NETWORK_TYPE_BRIDGE) {
- data.connection = "bridge";
+ data.connection = "macvtap";
data.interface = network.interface;
if ($("#enableVlan").prop("checked")) {
data.vlan_id = network.vlan_id;
--
2.5.0
9 years
[PATCH 0/4 - v333] Add multiple disks in Template
by Rodrigo Trujillo
V3 - Addresses Aline's comments
* fixes backend, API.md and API.json
* fixes test cases and add new test
V2 - Rebase over latest new UI patches
V1 - Initial version
Rodrigo Trujillo (4):
Fix Template backend create/update for multiple disks
UI - Implement multiple disks support in Template edit window
Test: Fix and add test related to disks in templates
Fix pep8 problems
src/wok/plugins/kimchi/API.json | 19 +-
src/wok/plugins/kimchi/docs/API.md | 3 +
src/wok/plugins/kimchi/model/templates.py | 9 +-
src/wok/plugins/kimchi/model/vms.py | 6 +-
src/wok/plugins/kimchi/osinfo.py | 1 +
src/wok/plugins/kimchi/tests/test_rest.py | 8 +-
src/wok/plugins/kimchi/tests/test_template.py | 36 ++-
.../kimchi/ui/js/src/kimchi.template_edit_main.js | 246 ++++++++++-----------
.../kimchi/ui/pages/template-edit.html.tmpl | 5 +-
src/wok/plugins/kimchi/vmtemplate.py | 13 +-
10 files changed, 204 insertions(+), 142 deletions(-)
--
2.1.0
9 years
[PATCH] Update VCPU by using libvirt function
by Jose Ricardo Ziviani
From: Crístian Deives <cristiandeives(a)gmail.com>
Currently, the VCPU count is updated by editing the configuration XML
(tag: <vcpu>). However, in some cases, editing that tag will only update
the maximum VCPU count, not the current one. For example, if the VM has
the following XML tag:
<vcpu current='2'>8</vcpu>
and the user updates the VCPU value to 10, Kimchi will update the XML
tag to:
<vcpu current='2'>10</vcpu>
Use the libvirt function "setVcpusFlags" to update the current and the
maximum VCPU values to the one specified by the user.
* This patch also changes vcpu setting when guest is being
created. Now 'current' is always set and is the total of cpus
from the Template and maxVcpu is the >= 255 or (sockets * cores
* threads) in PPC. In x86 it is the value of libvirt getMaxVcpu.
* Change NUMA management. Sets all cpus to 0. There is not
impact to the guest.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
Signed-off-by: Crístian Deives <cristiandeives(a)gmail.com>
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/wok/plugins/kimchi/i18n.py | 3 +
src/wok/plugins/kimchi/mockmodel.py | 51 +++++------
src/wok/plugins/kimchi/model/vms.py | 144 +++++++++++++++++++++++++++---
src/wok/plugins/kimchi/tests/test_rest.py | 10 ++-
src/wok/plugins/kimchi/vmtemplate.py | 26 +++++-
5 files changed, 186 insertions(+), 48 deletions(-)
diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py
index de7962a..29a7222 100644
--- a/src/wok/plugins/kimchi/i18n.py
+++ b/src/wok/plugins/kimchi/i18n.py
@@ -128,6 +128,9 @@ messages = {
"KCHVM0069E": _("Password field must be a string."),
"KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."),
"KCHVM0071E": _("Memory value %(mem)s must be aligned to %(alignment)sMiB."),
+ "KCHVM0072E": _("Unable to update the following parameters while the VM is offline: %(params)s"),
+ "KCHVM0073E": _("Unable to update the following parameters while the VM is online: %(params)s"),
+ "KCHVM0074E": _("Cannot change VCPU value because '%(vm)s' has a topology defined - sockets: %(sockets)s, cores: %(cores)s, threads: %(threads)s."),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."),
diff --git a/src/wok/plugins/kimchi/mockmodel.py b/src/wok/plugins/kimchi/mockmodel.py
index 9186f78..cca38e5 100644
--- a/src/wok/plugins/kimchi/mockmodel.py
+++ b/src/wok/plugins/kimchi/mockmodel.py
@@ -21,6 +21,8 @@ import libvirt
import lxml.etree as ET
import os
import time
+
+from collections import defaultdict
from lxml import objectify
from lxml.builder import E
@@ -60,10 +62,9 @@ storagevolumes.VALID_RAW_CONTENT = ['dos/mbr boot sector',
class MockModel(Model):
- _mock_vms = {}
+ _mock_vms = defaultdict(list)
_mock_snapshots = {}
_XMLDesc = libvirt.virDomain.XMLDesc
- _defineXML = libvirt.virConnect.defineXML
_undefineDomain = libvirt.virDomain.undefine
_libvirt_get_vol_path = LibvirtVMTemplate._get_volume_path
@@ -81,7 +82,6 @@ class MockModel(Model):
cpuinfo.get_topo_capabilities = MockModel.get_topo_capabilities
vmifaces.getDHCPLeases = MockModel.getDHCPLeases
- libvirt.virConnect.defineXML = MockModel.domainDefineXML
libvirt.virDomain.XMLDesc = MockModel.domainXMLDesc
libvirt.virDomain.undefine = MockModel.undefineDomain
libvirt.virDomain.attachDeviceFlags = MockModel.attachDeviceFlags
@@ -124,7 +124,7 @@ class MockModel(Model):
imageinfo.probe_image = self._probe_image
def reset(self):
- MockModel._mock_vms = {}
+ MockModel._mock_vms = defaultdict(list)
MockModel._mock_snapshots = {}
if hasattr(self, 'objstore'):
@@ -157,21 +157,15 @@ class MockModel(Model):
return ET.fromstring(xml)
@staticmethod
- def domainDefineXML(conn, xml):
- name = objectify.fromstring(xml).name.text
- try:
- dom = conn.lookupByName(name)
- if not dom.isActive():
- MockModel._mock_vms[name] = xml
- except:
- pass
+ def domainXMLDesc(dom, flags=0):
+ xml = MockModel._XMLDesc(dom, flags)
+ root = objectify.fromstring(xml)
- return MockModel._defineXML(conn, xml)
+ for dev_xml in MockModel._mock_vms.get(dom.name(), []):
+ dev = objectify.fromstring(dev_xml)
+ root.devices.append(dev)
- @staticmethod
- def domainXMLDesc(dom, flags=0):
- return MockModel._mock_vms.get(dom.name(),
- MockModel._XMLDesc(dom, flags))
+ return ET.tostring(root, encoding="utf-8")
@staticmethod
def undefineDomain(dom):
@@ -182,12 +176,7 @@ class MockModel(Model):
@staticmethod
def attachDeviceFlags(dom, xml, flags=0):
- old_xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
- root = objectify.fromstring(old_xml)
- dev = objectify.fromstring(xml)
- root.devices.append(dev)
-
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ MockModel._mock_vms[dom.name()].append(xml)
@staticmethod
def _get_device_node(dom, xml):
@@ -213,16 +202,18 @@ class MockModel(Model):
@staticmethod
def detachDeviceFlags(dom, xml, flags=0):
- root, dev = MockModel._get_device_node(dom, xml)
- root.devices.remove(dev)
-
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ node = ET.fromstring(xml)
+ xml = ET.tostring(node, encoding="utf-8", pretty_print=True)
+ if xml in MockModel._mock_vms[dom.name()]:
+ MockModel._mock_vms[dom.name()].remove(xml)
@staticmethod
def updateDeviceFlags(dom, xml, flags=0):
- root, old_dev = MockModel._get_device_node(dom, xml)
- root.devices.replace(old_dev, objectify.fromstring(xml))
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ _, old_dev = MockModel._get_device_node(dom, xml)
+ old_xml = ET.tostring(old_dev, encoding="utf-8", pretty_print=True)
+ if old_xml in MockModel._mock_vms[dom.name()]:
+ MockModel._mock_vms[dom.name()].remove(old_xml)
+ MockModel._mock_vms[dom.name()].append(xml)
@staticmethod
def volResize(vol, size, flags=0):
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py
index 7e12b91..a67044d 100644
--- a/src/wok/plugins/kimchi/model/vms.py
+++ b/src/wok/plugins/kimchi/model/vms.py
@@ -56,6 +56,7 @@ from wok.plugins.kimchi.model.utils import get_ascii_nonascii_name, get_vm_name
from wok.plugins.kimchi.model.utils import get_metadata_node
from wok.plugins.kimchi.model.utils import remove_metadata_node
from wok.plugins.kimchi.model.utils import set_metadata_node
+from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
from wok.plugins.kimchi.screenshot import VMScreenshot
from wok.plugins.kimchi.utils import template_name_from_uri
from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml
@@ -71,10 +72,15 @@ DOM_STATE_MAP = {0: 'nostate',
6: 'crashed',
7: 'pmsuspended'}
-VM_STATIC_UPDATE_PARAMS = {'name': './name',
- 'cpus': './vcpu'}
+VM_STATIC_UPDATE_PARAMS = {'name': './name'}
VM_LIVE_UPDATE_PARAMS = {}
+# update parameters which are updatable when the VM is online
+VM_ONLINE_UPDATE_PARAMS = ['graphics', 'groups', 'memory', 'users']
+# update parameters which are updatable when the VM is offline
+VM_OFFLINE_UPDATE_PARAMS = ['cpus', 'graphics', 'groups', 'memory', 'name',
+ 'users']
+
XPATH_DOMAIN_DISK = "/domain/devices/disk[@device='disk']/source/@file"
XPATH_DOMAIN_DISK_BY_FILE = "./devices/disk[@device='disk']/source[@file='%s']"
XPATH_DOMAIN_NAME = '/domain/name'
@@ -84,9 +90,12 @@ XPATH_DOMAIN_MAC_BY_ADDRESS = "./devices/interface[@type='network']/"\
XPATH_DOMAIN_MEMORY = '/domain/memory'
XPATH_DOMAIN_MEMORY_UNIT = '/domain/memory/@unit'
XPATH_DOMAIN_UUID = '/domain/uuid'
+XPATH_DOMAIN_DEV_CPU_ID = '/domain/devices/spapr-cpu-socket/@id'
XPATH_NUMA_CELL = './cpu/numa/cell'
+XPATH_TOPOLOGY = './cpu/topology'
+
# key: VM name; value: lock object
vm_locks = {}
@@ -98,6 +107,17 @@ class VMsModel(object):
self.caps = CapabilitiesModel(**kargs)
self.task = TaskModel(**kargs)
+ def _get_host_maxcpu(self):
+ if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
+ cpu_model = CPUInfoModel(conn=self.conn)
+ max_vcpu_val = (cpu_model.cores_available *
+ cpu_model.threads_per_core)
+ if max_vcpu_val > 255:
+ max_vcpu_val = 255
+ else:
+ max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
+ return max_vcpu_val
+
def create(self, params):
t_name = template_name_from_uri(params['template'])
vm_list = self.get_list()
@@ -163,7 +183,8 @@ class VMsModel(object):
xml = t.to_vm_xml(name, vm_uuid,
libvirt_stream_protocols=stream_protocols,
graphics=graphics,
- volumes=vol_list)
+ volumes=vol_list,
+ max_vcpus=self._get_host_maxcpu())
cb('Defining new VM')
try:
@@ -231,6 +252,30 @@ class VMModel(object):
self.vmsnapshots = cls(**kargs)
self.stats = {}
+ def has_topology(self, dom):
+ xml = dom.XMLDesc(0)
+ sockets = xpath_get_text(xml, XPATH_TOPOLOGY + '/@sockets')
+ cores = xpath_get_text(xml, XPATH_TOPOLOGY + '/@cores')
+ threads = xpath_get_text(xml, XPATH_TOPOLOGY + '/@threads')
+ return sockets and cores and threads
+
+ def get_vm_max_sockets(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@sockets')[0])
+
+ def get_vm_sockets(self, dom):
+ current_vcpu = dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_CURRENT)
+ return (current_vcpu / self.get_vm_cores(dom) /
+ self.get_vm_threads(dom))
+
+ def get_vm_cores(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@cores')[0])
+
+ def get_vm_threads(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@threads')[0])
+
def update(self, name, params):
lock = vm_locks.get(name)
if lock is None:
@@ -247,8 +292,20 @@ class VMModel(object):
'alignment': str(PPC_MEM_ALIGN)})
dom = self.get_vm(name, self.conn)
- vm_name, dom = self._static_vm_update(name, dom, params)
+
+ if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
+ ext_params = set(params.keys()) - set(VM_OFFLINE_UPDATE_PARAMS)
+ if len(ext_params) > 0:
+ raise InvalidParameter('KCHVM0072E',
+ {'params': ', '.join(ext_params)})
+ else:
+ ext_params = set(params.keys()) - set(VM_ONLINE_UPDATE_PARAMS)
+ if len(ext_params) > 0:
+ raise InvalidParameter('KCHVM0073E',
+ {'params': ', '.join(ext_params)})
+
self._live_vm_update(dom, params)
+ vm_name, dom = self._static_vm_update(name, dom, params)
return vm_name
def clone(self, name):
@@ -713,23 +770,34 @@ class VMModel(object):
params['name'], nonascii_name = get_ascii_nonascii_name(name)
for key, val in params.items():
+ change_numa = True
if key in VM_STATIC_UPDATE_PARAMS:
if type(val) == int:
val = str(val)
xpath = VM_STATIC_UPDATE_PARAMS[key]
- new_xml = xml_item_update(new_xml, xpath, val)
+ attrib = None
+ if key == 'cpus':
+ if self.has_topology(dom) or dom.isActive():
+ change_numa = False
+ continue
+ # Update maxvcpu firstly
+ new_xml = xml_item_update(new_xml, xpath,
+ str(self._get_host_maxcpu()),
+ attrib)
+ # Update current vcpu
+ attrib = 'current'
+ new_xml = xml_item_update(new_xml, xpath, val, attrib)
# Updating memory and NUMA if necessary, if vm is offline
if not dom.isActive():
if 'memory' in params:
new_xml = self._update_memory_config(new_xml, params)
- elif 'cpus' in params and \
+ elif 'cpus' in params and change_numa and \
(xpath_get_text(new_xml, XPATH_NUMA_CELL + '/@memory') != []):
- vcpus = params['cpus']
new_xml = xml_item_update(
new_xml,
XPATH_NUMA_CELL,
- value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
+ value='0',
attr='cpus')
if 'graphics' in params:
@@ -764,6 +832,8 @@ class VMModel(object):
raise OperationFailed("KCHVM0008E", {'name': vm_name,
'err': e.get_error_message()})
+ if name is not None:
+ vm_name = name
return (nonascii_name if nonascii_name is not None else vm_name, dom)
def _update_memory_config(self, xml, params):
@@ -775,21 +845,20 @@ class VMModel(object):
vcpus = params.get('cpus')
if numa_mem == []:
if vcpus is None:
- vcpus = int(xpath_get_text(xml,
- VM_STATIC_UPDATE_PARAMS['cpus'])[0])
+ vcpus = int(xpath_get_text(xml, 'vcpu')[0])
cpu = root.find('./cpu')
if cpu is None:
- cpu = get_cpu_xml(vcpus, params['memory'] << 10)
+ cpu = get_cpu_xml(0, params['memory'] << 10)
root.insert(0, ET.fromstring(cpu))
else:
- numa_element = get_numa_xml(vcpus, params['memory'] << 10)
+ numa_element = get_numa_xml(0, params['memory'] << 10)
cpu.insert(0, ET.fromstring(numa_element))
else:
if vcpus is not None:
xml = xml_item_update(
xml,
XPATH_NUMA_CELL,
- value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
+ value='0',
attr='cpus')
root = ET.fromstring(xml_item_update(xml, XPATH_NUMA_CELL,
str(params['memory'] << 10),
@@ -853,11 +922,60 @@ class VMModel(object):
return new_xml
return ET.tostring(root, encoding="utf-8")
+ def _get_host_maxcpu(self):
+ if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
+ cpu_model = CPUInfoModel(conn=self.conn)
+ max_vcpu_val = (cpu_model.cores_available *
+ cpu_model.threads_per_core)
+ if max_vcpu_val > 255:
+ max_vcpu_val = 255
+ else:
+ max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
+ return max_vcpu_val
+
def _live_vm_update(self, dom, params):
self._vm_update_access_metadata(dom, params)
if 'memory' in params and dom.isActive():
self._update_memory_live(dom, params)
+ if 'cpus' in params:
+ cpus = params['cpus']
+
+ if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
+
+ # user cannot change vcpu if topology is defined. In this case
+ # vcpu must always be sockets * cores * threads.
+ current_vcpu = dom.vcpusFlags(libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
+
+ if self.has_topology(dom):
+ if current_vcpu != cpus:
+ raise InvalidOperation('KCHVM0074E',
+ {'vm': dom.name(),
+ 'sockets':
+ self.get_vm_max_sockets(),
+ 'cores':
+ self.get_vm_cores(),
+ 'threads':
+ self.get_vm_threads()})
+
+ # do not need to update vcpu if the value edit did not
+ # change
+ else:
+ return
+
+ try:
+ # set maximum VCPU count
+ max_vcpus = self._get_host_maxcpu()
+ dom.setVcpusFlags(max_vcpus,
+ libvirt.VIR_DOMAIN_AFFECT_CONFIG |
+ libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
+
+ # set current VCPU count
+ dom.setVcpusFlags(cpus, libvirt.VIR_DOMAIN_AFFECT_CONFIG)
+ except libvirt.libvirtError, e:
+ raise OperationFailed('KCHVM0008E', {'name': dom.name(),
+ 'err': e.message})
+
def _update_memory_live(self, dom, params):
# Check if host supports memory device
if not self.caps.mem_hotplug_support:
diff --git a/src/wok/plugins/kimchi/tests/test_rest.py b/src/wok/plugins/kimchi/tests/test_rest.py
index 544f2e6..9fa8c8d 100644
--- a/src/wok/plugins/kimchi/tests/test_rest.py
+++ b/src/wok/plugins/kimchi/tests/test_rest.py
@@ -144,6 +144,10 @@ class RestTests(unittest.TestCase):
vm = json.loads(self.request('/plugins/kimchi/vms/vm-1').read())
self.assertEquals('vm-1', vm['name'])
+ req = json.dumps({'cpus': 3})
+ resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
+ self.assertEquals(200, resp.status)
+
resp = self.request('/plugins/kimchi/vms/vm-1/start', '{}', 'POST')
self.assertEquals(200, resp.status)
@@ -155,9 +159,10 @@ class RestTests(unittest.TestCase):
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
self.assertEquals(400, resp.status)
- req = json.dumps({'cpus': 3})
+ # Unable to do CPU hotplug
+ req = json.dumps({'cpus': 5})
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
- self.assertEquals(200, resp.status)
+ self.assertEquals(400, resp.status)
# Check if there is support to memory hotplug, once vm is running
resp = self.request('/plugins/kimchi/config/capabilities').read()
@@ -171,6 +176,7 @@ class RestTests(unittest.TestCase):
req = json.dumps({"graphics": {'passwd': "abcdef"}})
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
+ self.assertEquals(200, resp.status)
info = json.loads(resp.read())
self.assertEquals('abcdef', info["graphics"]["passwd"])
self.assertEquals(None, info["graphics"]["passwdValidTo"])
diff --git a/src/wok/plugins/kimchi/vmtemplate.py b/src/wok/plugins/kimchi/vmtemplate.py
index 3097b66..8d669db 100644
--- a/src/wok/plugins/kimchi/vmtemplate.py
+++ b/src/wok/plugins/kimchi/vmtemplate.py
@@ -298,7 +298,7 @@ class VMTemplate(object):
cpu_info = self.info.get('cpu_info')
if cpu_info is not None:
cpu_topo = cpu_info.get('topology')
- return get_cpu_xml(self.info.get('cpus'),
+ return get_cpu_xml(0,
self.info.get('memory') << 10,
cpu_topo)
@@ -311,7 +311,6 @@ class VMTemplate(object):
params['qemu-namespace'] = ''
params['cdroms'] = ''
params['qemu-stream-cmdline'] = ''
- params['cpu_info'] = self._get_cpu_xml()
params['disks'] = self._get_disks_xml(vm_uuid)
params['serial'] = get_serial_xml(params)
@@ -322,6 +321,8 @@ class VMTemplate(object):
libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', [])
cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols)
+ max_vcpus = kwargs.get('max_vcpus', 1)
+
if not urlparse.urlparse(self.info.get('cdrom', "")).scheme in \
libvirt_stream_protocols and \
params.get('iso_stream', False):
@@ -344,6 +345,25 @@ class VMTemplate(object):
if distro == "IBM_PowerKVM":
params['slots'] = 32
+ cpu_topo = self.info.get('cpu_info').get('topology')
+ if (cpu_topo is not None):
+ sockets = int(max_vcpus / (cpu_topo['cores'] *
+ cpu_topo['threads']))
+ self.info['cpu_info']['topology']['sockets'] = sockets
+
+ # Reduce maxvcpu to fit number of sockets if necessary
+ total_max_vcpu = sockets * cpu_topo['cores'] * cpu_topo['threads']
+ if total_max_vcpu != max_vcpus:
+ max_vcpus = total_max_vcpu
+
+ params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
+ (params['cpus'], max_vcpus)
+ else:
+ params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
+ (params['cpus'], max_vcpus)
+
+ params['cpu_info'] = self._get_cpu_xml()
+
xml = """
<domain type='%(domain)s'>
%(qemu-stream-cmdline)s
@@ -351,7 +371,7 @@ class VMTemplate(object):
<uuid>%(uuid)s</uuid>
<maxMemory slots='%(slots)s' unit='KiB'>%(max_memory)s</maxMemory>
<memory unit='MiB'>%(memory)s</memory>
- <vcpu>%(cpus)s</vcpu>
+ %(vcpus)s
%(cpu_info)s
<os>
<type arch='%(arch)s'>hvm</type>
--
1.9.1
9 years