[PATCH 0/3] Bugfix #496 System leftovers after uninstalling Kimchi
by Ramon Medeiros
Ramon Medeiros (3):
Remove empty directories after rpm -e kimchi on fedora
Don't let empty directories left on suse
Remove directories under /var/lib/kimchi if empty on ubuntu
contrib/kimchi.spec.fedora.in | 21 ++++++++++++++++++++-
contrib/kimchi.spec.suse.in | 20 +++++++++++++++++++-
contrib/make-deb.sh.in | 4 ++++
3 files changed, 43 insertions(+), 2 deletions(-)
--
1.8.3.1
9 years, 9 months
[PATCH 0/3] Bugfix #496 System leftovers after uninstalling Kimchi v3
by Ramon Medeiros
v2:
Fix suse based on fedora
v3:
Put ubuntu changes on Makefile
Use the variables instead path
Ramon Medeiros (3):
Remove empty directories after rpm -e kimchi on fedora
Don't let empty directories left on suse
Remove directories under /var/lib/kimchi if empty on ubuntu
Makefile.am | 5 +++++
contrib/kimchi.spec.fedora.in | 21 ++++++++++++++++++++-
contrib/kimchi.spec.suse.in | 20 +++++++++++++++++++-
3 files changed, 44 insertions(+), 2 deletions(-)
--
1.8.3.1
9 years, 9 months
[PATCH v3 0/2] Issue #543
by Crístian Viana
The differences between this and the previous patchset are:
- Patch "Support different "tags" when generating a new name in
"get_next_clone_name"" has been discarded.
- Patch 2/2 has been updated to generate unique names in the form
"index.html (1)".
Crístian Viana (2):
bugfix: Fix regexp in "kimchi.utils.get_next_clone_name"
issue #543: Generate unique names when creating volumes without name
src/kimchi/model/storagevolumes.py | 9 +++++++--
src/kimchi/utils.py | 37 ++++++++++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 3 deletions(-)
--
1.9.3
9 years, 9 months
[PATCHv2] Issue#536: raise proper error message when disk os info absent
by lvroyce@linux.vnet.ibm.com
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
When disk os cannot be decided by libguestfs,
it will leave it to 'unknown' distro and version.
But if no boot information available,
ImageFormatError is left unhandled and this will introduce
500 error when disk os info is absent.
Handle this error to fix this.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/vmtemplate.py | 6 +++++-
tests/test_model.py | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index 8582d48..3266e7b 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -29,6 +29,7 @@ from lxml.builder import E
from kimchi import imageinfo
from kimchi import osinfo
from kimchi.exception import InvalidParameter, IsoFormatError, MissingParameter
+from kimchi.exception import ImageFormatError, OperationFailed
from kimchi.isoinfo import IsoImage
from kimchi.utils import check_url_path, pool_name_from_uri
from kimchi.xmlutils.disk import get_disk_xml
@@ -51,7 +52,10 @@ class VMTemplate(object):
self.fc_host_support = args.get('fc_host_support')
# Fetch defaults based on the os distro and version
- distro, version = self._get_os_info(args, scan)
+ try:
+ distro, version = self._get_os_info(args, scan)
+ except ImageFormatError as e:
+ raise OperationFailed('KCHTMPL0020E', {'err': e.message})
os_distro = args.get('os_distro', distro)
os_version = args.get('os_version', version)
entry = osinfo.lookup(os_distro, os_version)
diff --git a/tests/test_model.py b/tests/test_model.py
index aa2a1bb..2bcb075 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -196,7 +196,7 @@ class ModelTests(unittest.TestCase):
vol_path = inst.storagevolume_lookup('default', vol)['path']
params = {'name': 'test', 'disks': [{'base': vol_path}]}
- self.assertRaises(ImageFormatError, inst.templates_create, params)
+ self.assertRaises(OperationFailed, inst.templates_create, params)
# Hack the model objstore to add a new template
# It is needed as the image file must be a bootable image when
--
1.8.3.2
9 years, 9 months
[PATCH] Po support: translation for Chinese
by Wen Wang
Added Chinese translation in po file.
Signed-off-by: Wen Wang <wenwang(a)linux.vnet.ibm.com>
---
po/zh_CN.po | 118 +++++++++++++++++++++++++++++++++---------------------------
1 file changed, 65 insertions(+), 53 deletions(-)
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 9bf50b8..7a9a33f 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-15 11:01-0200\n"
+"POT-Creation-Date: 2014-12-17 13:57+0800\n"
"PO-Revision-Date: 2013-06-27 10:48+0000\n"
"Last-Translator: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>\n"
"Language-Team: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>\n"
@@ -75,7 +75,7 @@ msgstr "由于错误%(err)s任务启动失败"
#, python-format
msgid "Timeout of %(seconds)s seconds expired while running task '%(task)s."
-msgstr ""
+msgstr "任务'%(task)s超时%(seconds)s秒。"
#, python-format
msgid "Authentication failed for user '%(username)s'. [Error code: %(code)s]"
@@ -90,20 +90,20 @@ msgstr "指定登录Kimchi的%(item)s"
#, python-format
msgid "User %(user_id)s not found with given LDAP settings."
-msgstr ""
+msgstr "使用指定的LDAP配置未找到%(user_id)s用户"
#, python-format
msgid "Invalid LDAP configuration: %(item)s : %(value)s"
-msgstr ""
+msgstr "无效的LDAP配置:%(item)s : %(value)s"
msgid "Unknown \"_cap\" specified"
-msgstr ""
+msgstr "未识别的\"_cap\""
msgid "\"_passthrough\" should be \"true\" or \"false\""
-msgstr ""
+msgstr "\"_passthrough\"值应为\"true\"或者\"false\""
msgid "\"_passthrough_affected_by\" should be a device name string"
-msgstr ""
+msgstr "\"_passthrough_affected_by\"应为一个字符串型设备名"
#, python-format
msgid "Error while getting block devices. Details: %(err)s"
@@ -302,24 +302,24 @@ msgstr "客户机命令行密码有效时间必须是一个数字。"
#, python-format
msgid "Virtual machine '%(name)s' must be stopped before cloning it."
-msgstr ""
+msgstr "虚拟机'%(name)s'在制作副本前必须关机。"
#, python-format
msgid "Insufficient disk space to clone virtual machine '%(name)s'"
-msgstr ""
+msgstr "制作虚拟机'%(name)s'副本所需的磁盘空间不足"
#, python-format
msgid "Unable to clone VM '%(name)s'. Details: %(err)s"
-msgstr ""
+msgstr "未能成功制作虚拟机'%(name)s'副本。详情:%(err)s"
#, python-format
msgid ""
"VM %(vmid)s does not contain directly assigned host device %(dev_name)s."
-msgstr ""
+msgstr "虚拟机%(vmid)s未指明被分配的主机%(dev_name)s。"
#, python-format
msgid "The host device %(dev_name)s is not allowed to directly assign to VM."
-msgstr ""
+msgstr "主机%(dev_name)s不允许直接分配给虚拟机。"
msgid ""
"No IOMMU groups found. Host PCI pass through needs IOMMU group to function "
@@ -328,9 +328,13 @@ msgid ""
"to your Kernel parameter in /boot/grub2/grub.conf. For AMD CPU, add iommu=pt "
"iommu=1."
msgstr ""
+"未找到IOMMU groups。主机PCI pass through需要IOMMU group才可以正确工作。请在"
+"BIOS设置里将Intel VT-d 或者 AMD IOMMU 设为使能,而后确认内核支持IOMMU。对于"
+"Intel CPU,在路径/boot/grub2/grub.conf中添加内核变量intel_iommu=on。对于AMD "
+"CPU,则添加iommu=pt iommu=1。"
msgid "\"name\" should be a device name string"
-msgstr ""
+msgstr "\"name\"应该为一个字符串型的设备名"
#, python-format
msgid "Interface %(iface)s does not exist in virtual machine %(name)s"
@@ -444,17 +448,19 @@ msgstr "未能识别基础镜像%(path)s格式"
msgid ""
"When specifying CPU topology, VCPUs must be a product of sockets, cores, and "
"threads."
-msgstr ""
+msgstr "CPU拓扑中,VCPUs必须包括sockets, cores 以及threads。"
msgid ""
"When specifying CPU topology, each element must be an integer greater than "
"zero."
-msgstr ""
+msgstr "CPU拓扑中,每一个参数必须为大于零的整数。"
msgid ""
"Invalid disk image format. Valid formats: bochs, cloop, cow, dmg, qcow, "
"qcow2, qed, raw, vmdk, vpc."
msgstr ""
+"无效的磁盘镜像格式。有效的格式为:bochs, cloop, cow, dmg, qcow, qcow2, qed, "
+"raw, vmdk, vpc。"
#, python-format
msgid "Storage pool %(name)s already exists"
@@ -660,6 +666,8 @@ msgid ""
"Storage volume format not supported. Valid formats: bochs, cloop, cow, dmg, "
"qcow, qcow2, qed, raw, vmdk, vpc."
msgstr ""
+"不支持该存储卷格式,支持的格式:bochs, cloop, cow, dmg, qcow, qcow2, qed, "
+"raw, vmdk, vpc。"
msgid "Storage volume requires a volume name"
msgstr "存储卷需要名字"
@@ -685,13 +693,13 @@ msgstr "存储卷URL必须为http://,https://,ftp://或ftps://"
#, python-format
msgid "Unable to access file %(url)s. Please, check it."
-msgstr ""
+msgstr "未能连接文件%(url)s,请检查该文件是否存在。"
#, python-format
msgid ""
"Unable to clone storage volume '%(name)s' in pool '%(pool)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能于存储池'%(pool)s'制作存储卷'%(name)s'的副本,详情:%(err)s"
#, python-format
msgid "Interface %(name)s does not exist"
@@ -778,7 +786,7 @@ msgstr "网络接口%(iface)s启动失败,请检查网络连接情况。"
#, python-format
msgid "Failed to start network %(name)s. Details: %(err)s"
-msgstr ""
+msgstr "启动网络%(name)s失败,详情:%(err)s"
#, python-format
msgid "Debug report %(name)s does not exist"
@@ -838,7 +846,7 @@ msgid "Node device '%(name)s' not found"
msgstr "没有找到节点设备'%(name)s'"
msgid "Conflicting flag filters specified."
-msgstr ""
+msgstr "flag filters冲突。"
msgid "No packages marked for update"
msgstr "没有软件包标识要升级"
@@ -1035,60 +1043,62 @@ msgstr "软件仓库不支持配置类型: %(items)s"
#, python-format
msgid ""
"Virtual machine '%(vm)s' must be stopped before creating a snapshot of it."
-msgstr ""
+msgstr "虚拟机'%(vm)s'在制作快照前必须关机。"
#, python-format
msgid ""
"Unable to create snapshot '%(name)s' on virtual machine '%(vm)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能为虚拟机'%(vm)s'制作快照'%(name)s'。详情:%(err)s"
#, python-format
msgid "Snapshot '%(name)s' does not exist on virtual machine '%(vm)s'."
-msgstr ""
+msgstr "快照'%(name)s'不存在虚拟机'%(vm)s'上。"
#, python-format
msgid ""
"Unable to retrieve snapshot '%(name)s' on virtual machine '%(vm)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能在虚拟机'%(vm)s'找到快照'%(name)s'。详情:%(err)s"
#, python-format
msgid "Unable to list snapshots on virtual machine '%(vm)s'. Details: %(err)s"
-msgstr ""
+msgstr "未能列出虚拟机'%(vm)s'的快照。详情:%(err)s"
#, python-format
msgid ""
"Unable to delete snapshot '%(name)s' on virtual machine '%(vm)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能删除虚拟机'%(vm)s'快照'%(name)s'。详情:%(err)s"
#, python-format
msgid ""
"Unable to retrieve current snapshot of virtual machine '%(vm)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能找到虚拟机'%(vm)s'当前快照。详情:%(err)s"
#, python-format
msgid ""
"Unable to revert virtual machine '%(vm)s' to snapshot '%(name)s'. Details: "
"%(err)s"
-msgstr ""
+msgstr "未能恢复虚拟机'%(vm)s'到快照'%(name)s'。详情:%(err)s"
#, python-format
msgid ""
"Unable to create snapshot of virtual machine '%(vm)s' because it contains a "
"disk with format '%(format)s'; only 'qcow2' is supported."
msgstr ""
+"未能为虚拟机'%(vm)s'创建快照因为其使用了格式为'%(format)s'的磁盘;当前仅支"
+"持'qcow2'格式。"
msgid "The number of vCPUs is too large for this system."
-msgstr ""
+msgstr "vCPUs的数量对该系统而言太大。"
msgid "Invalid vCPU/topology combination."
-msgstr ""
+msgstr "无效的vCPU/topology组合。"
msgid "This host (or current configuration) does not allow CPU topology."
-msgstr ""
+msgstr "当前主机(或当前配置)不允许CPU拓扑。"
msgid "ERROR CODE"
msgstr "错误码"
@@ -1159,10 +1169,10 @@ msgid "Permission"
msgstr "权限"
msgid "Host PCI Device"
-msgstr ""
+msgstr "主机PCI设备"
msgid "Snapshot"
-msgstr ""
+msgstr "快照"
msgid "Name"
msgstr "名称"
@@ -1195,28 +1205,28 @@ msgid "Selected system users and groups"
msgstr "已选的系统用户及用户组"
msgid "User"
-msgstr ""
+msgstr "用户"
msgid "All"
msgstr "所有"
msgid "To Add"
-msgstr ""
+msgstr "待添加"
msgid "Added"
-msgstr ""
+msgstr "已添加"
msgid "filter"
-msgstr ""
+msgstr "过滤器"
msgid "Product"
-msgstr ""
+msgstr "产品"
msgid "Vendor"
msgstr "厂商"
msgid "Created"
-msgstr ""
+msgstr "创建于"
msgid "Save"
msgstr "保存"
@@ -1231,10 +1241,10 @@ msgid "Cancel"
msgstr "取消"
msgid "revert"
-msgstr ""
+msgstr "恢复"
msgid "Cloning"
-msgstr ""
+msgstr "正在制作副本"
msgid "Start"
msgstr "启用"
@@ -1538,6 +1548,8 @@ msgid ""
"default storage pool. The same will happen when the target pool does not "
"have enough space to clone the volumes. Do you want to continue?"
msgstr ""
+"当目标客户机使用SCSI或者iSCSI存储卷时,这些存储卷的副本将被放置于默认存储池"
+"中。在目标存储池没有足够空间放置其他存储卷的时候也会如此。确认继续?"
msgid ""
"This CDROM will be detached permanently and you can re-attach it. Continue "
@@ -1565,25 +1577,25 @@ msgid ""
msgstr "该磁盘将会被永久卸载,你可以重新添加它,继续执行卸载操作吗?"
msgid "interface:"
-msgstr ""
+msgstr "接口:"
msgid "address:"
-msgstr ""
+msgstr "地址:"
msgid "link_type:"
-msgstr ""
+msgstr "连接类型:"
msgid "block:"
-msgstr ""
+msgstr "块:"
msgid "drive_type:"
-msgstr ""
+msgstr "设备类型:"
msgid "model:"
-msgstr ""
+msgstr "模型:"
msgid "Affected devices:"
-msgstr ""
+msgstr "被影响的设备:"
msgid "The VLAN id must be between 1 and 4094."
msgstr "VLAN 标识符必须在1至4094之间"
@@ -1872,7 +1884,7 @@ msgid "Upload a file"
msgstr "上传一个文件"
msgid "Choose the file you want to upload."
-msgstr ""
+msgstr "选择需要上传的文件。"
msgid "Add Template"
msgstr "创建模板"
@@ -1938,19 +1950,19 @@ msgid "Graphics"
msgstr "图形"
msgid "Disk(GB)"
-msgstr ""
+msgstr "磁盘(GB)"
msgid "CPU Number"
msgstr "CPU个数"
msgid "Manually set CPU topology"
-msgstr ""
+msgstr "手动配置CPU拓扑"
msgid "Cores"
-msgstr ""
+msgstr "内核数"
msgid "Threads"
-msgstr ""
+msgstr "线程"
msgid "CPU"
msgstr "处理器"
@@ -1983,7 +1995,7 @@ msgid "OS Code Name"
msgstr "操作系统代号"
msgid "CPU(s)"
-msgstr ""
+msgstr "CPU(s)"
msgid "System Statistics"
msgstr "系统统计信息"
--
1.9.3
9 years, 9 months
[v7 1/1] Virtual machine migration
by simonjin@linux.vnet.ibm.com
From: Simon Jin <simonjin(a)linux.vnet.ibm.com>
v7-v6:
Add migrate check in API.json.
Add abstract a helper function task_is_running.
Signed-off-by: Simon Jin <simonjin(a)linux.vnet.ibm.com>
Signed-off-by: yuntong <yuntongjin(a)gmail.com>
---
docs/API.md | 5 ++
src/kimchi/API.json | 27 ++++++++
src/kimchi/control/vms.py | 3 +
src/kimchi/i18n.py | 12 ++++
src/kimchi/model/utils.py | 18 ++++++
src/kimchi/model/vms.py | 159 +++++++++++++++++++++++++++++++++++++++++++++-
6 files changed, 222 insertions(+), 2 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index 210a036..349c950 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -132,6 +132,11 @@ the following general conventions:
It emulates the power reset button on a machine. Note that there is a
risk of data loss caused by reset without the guest OS shutdown.
* connect: Prepare the connection for spice or vnc
+* migrate: Migrate a virtual machine to a remote server, only support live mode without block migration.
+ * remoteHost: IP address or hostname of the remote server.
+ * user: User of the remote server.
+ * passwd: Passwd of user on remote server.
+ * secure *(optional)*: Migrate in P2P secure tunel mode.
* clone: Create a new VM identical to this VM. The new VM's name, UUID and
network MAC addresses will be generated automatically. Each existing
diff --git a/src/kimchi/API.json b/src/kimchi/API.json
index c5d7bdb..f6aff5e 100644
--- a/src/kimchi/API.json
+++ b/src/kimchi/API.json
@@ -305,6 +305,33 @@
}
}
},
+ "vm_migrate": {
+ "type": "object",
+ "properties": {
+ "remoteHost": {
+ "description": "IP address or hostname of the remote server",
+ "type": "string",
+ "minLength": 1,
+ "error": "KCHVM0044E"
+ },
+ "user": {
+ "description": "User of the remote server",
+ "type": "string",
+ "minLength": 1,
+ "error": "KCHVM0045E"
+ },
+ "passwd": {
+ "description": "Passwd of user on remote server",
+ "type": "string",
+ "error": "KCHVM0046E"
+ },
+ "secure": {
+ "description": "Migrate in P2P secure tunel mode(optional)",
+ "type": "boolean",
+ "error": "KCHVM0047E"
+ }
+ }
+ },
"networks_create": {
"type": "object",
"error": "KCHNET0016E",
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index a1589ef..3638b3a 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -42,6 +42,9 @@ class VM(Resource):
for ident, node in sub_nodes.items():
setattr(self, ident, node(model, self.ident))
self.start = self.generate_action_handler('start')
+ self.migrate = self.generate_action_handler_task('migrate',
+ ['remoteHost', 'user',
+ 'passwd', 'secure'])
self.poweroff = self.generate_action_handler('poweroff')
self.shutdown = self.generate_action_handler('shutdown')
self.reset = self.generate_action_handler('reset')
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index e3a051c..90eae92 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -107,6 +107,18 @@ messages = {
"KCHVM0033E": _("Virtual machine '%(name)s' must be stopped before cloning it."),
"KCHVM0034E": _("Insufficient disk space to clone virtual machine '%(name)s'"),
"KCHVM0035E": _("Unable to clone VM '%(name)s'. Details: %(err)s"),
+ "KCHVM0036E": _("Migrate %(name)s to localhost %(remoteHost)s is not allowed."),
+ "KCHVM0037E": _("vm %(name)s is already in migrating, can not perform migrate on it again."),
+ "KCHVM0038E": _("Can't migrate vm %(name)s on Hypervisor arch %(srcarch)s.to a different Hypervisor arch %(destarch)s."),
+ "KCHVM0039E": _("Can't migrate %(name)s from %(srchyp)s to a different Hypervisor type %(desthyp)s."),
+ "KCHVM0040E": _("Can't migrate vm %(name)s, vm has passthrough device %(hostdevs)s."),
+ "KCHVM0041E": _("Can not migrate vm %(name)s when its in %(state)s state."),
+ "KCHVM0042E": _("Failed Migrate vm %(name)s due error: %(err)s"),
+ "KCHVM0043E": _("Failed to destroy vm %(name)s from migration recover. Details: %(err)s"),
+ "KCHVM0044E": _("Migrate remoteHost must be a string"),
+ "KCHVM0045E": _("User name of the remote server must be a string"),
+ "KCHVM0046E": _("The passwd of remote server must be a string"),
+ "KCHVM0047E": _("Migrate secure must be a boolean value."),
"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/kimchi/model/utils.py b/src/kimchi/model/utils.py
index 9896289..8c67943 100644
--- a/src/kimchi/model/utils.py
+++ b/src/kimchi/model/utils.py
@@ -24,7 +24,9 @@ from lxml import etree, objectify
from lxml.builder import E, ElementMaker
from kimchi.exception import OperationFailed
+from kimchi.exception import NotFoundError
from kimchi.model.featuretests import FeatureTests
+from kimchi.utils import kimchi_log
KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi"
KIMCHI_NAMESPACE = "kimchi"
@@ -161,3 +163,19 @@ def get_metadata_node(dom, tag, metadata_support, mode="current"):
if node is not None:
return etree.tostring(node)
return ""
+
+
+def task_is_running(tasks, task, target_uri):
+ try:
+ alltasks = tasks.get_list()
+ for taskid in alltasks:
+ taskobj = task.lookup(taskid)
+ if taskobj['target_uri'] == target_uri \
+ and taskobj['status'] == "running":
+ kimchi_log.debug('task with target_uri %s is running.'
+ % target_uri)
+ return True
+ except NotFoundError:
+ kimchi_log.debug('task with target_uri %s is not running.'
+ % target_uri)
+ return False
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index 3aa1145..94412e8 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -33,13 +33,15 @@ from cherrypy.process.plugins import BackgroundTask
from kimchi import model, vnc
from kimchi.config import READONLY_POOL_TYPE, config
from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.exception import NotFoundError, OperationFailed, KimchiException
from kimchi.model.config import CapabilitiesModel
-from kimchi.model.tasks import TaskModel
+from kimchi.model.libvirtconnection import LibvirtConnection
+from kimchi.model.tasks import TaskModel, TasksModel
from kimchi.model.templates import TemplateModel
from kimchi.model.utils import get_vm_name
from kimchi.model.utils import get_metadata_node
from kimchi.model.utils import set_metadata_node
+from kimchi.model.utils import task_is_running
from kimchi.rollbackcontext import RollbackContext
from kimchi.screenshot import VMScreenshot
from kimchi.utils import add_task, get_next_clone_name, import_class
@@ -268,6 +270,7 @@ class VMModel(object):
self.groups = import_class('kimchi.model.groups.GroupsModel')(**kargs)
self.vms = VMsModel(**kargs)
self.task = TaskModel(**kargs)
+ self.tasks = TasksModel(**kargs)
self.storagepool = model.storagepools.StoragePoolModel(**kargs)
self.storagevolume = model.storagevolumes.StorageVolumeModel(**kargs)
self.storagevolumes = model.storagevolumes.StorageVolumesModel(**kargs)
@@ -850,6 +853,158 @@ class VMModel(object):
raise OperationFailed("KCHVM0019E",
{'name': name, 'err': e.get_error_message()})
+ def _preCheck(self, name, destConn, remoteHost):
+ """Pre check before migration"""
+
+ kimchi_log.debug('precheck migrate %s' % name)
+ _destConn = destConn.get()
+ if remoteHost in ['localhost', '127.0.0.1']:
+ kimchi_log.debug('vm %s Can not migrate to localhost.' % name)
+ raise OperationFailed("KCHVM0036E", {'name': name,
+ 'remoteHost': remoteHost})
+ # check if the vm already in migration
+ if task_is_running(self.tasks, self.task, "/vms/%s/migrate" % name):
+ raise OperationFailed("KCHVM0037E", {'name': name})
+
+ srcarch = self.conn.get().getInfo()[0]
+ destarch = _destConn.getInfo()[0]
+ if srcarch != destarch:
+ kimchi_log.debug('vm %s can not migrate to different arch server.'
+ % (name, destarch))
+ raise OperationFailed("KCHVM0038E", {'name': name,
+ 'srcarch': srcarch,
+ 'destarch': destarch})
+ srchyp = self.conn.get().getType()
+ desthyp = _destConn.getType()
+ if srchyp != desthyp:
+ kimchi_log.debug('vm %s can not migrate to a different hypervisor'
+ % name)
+ raise OperationFailed("KCHVM0039E", {'name': name,
+ 'srchyp': srchyp,
+ 'desthyp': desthyp})
+
+ # model.host import DOM_STATE_MAP, vmhostdevs import host,
+ # to void loop import DOM_STATE_MAP, move import VMHostDevsModel here
+ from kimchi.model.vmhostdevs import VMHostDevsModel
+ # check if there is any passthrough devices belong to this vm
+ vmhostdevs = VMHostDevsModel(conn=self.conn)
+ hostdevs = vmhostdevs.get_list(name)
+ if hostdevs:
+ raise OperationFailed("KCHVM0040E", {'name': name,
+ 'hostdevs': hostdevs})
+
+ @staticmethod
+ def _getRemoteConn(remoteHost, user, passwd, transport='ssh'):
+ """open destination connect to migrate"""
+
+ duri = 'qemu+%s://%s@%s/system' % (transport, user, remoteHost)
+ conn = LibvirtConnection(duri)
+ return conn
+
+ def _do_migrate(self, cb, params):
+ """Asynchronous function which performs the migrate operation.
+
+ Arguments:
+ cb -- A callback function to signal the Task's progress.
+ params -- A dict with the following values:
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ "remoteHost": The migration destination server.
+ "secure": Migrate in P2P secure tunel mode.(optional)
+ """
+
+ name = params['name'].decode('utf-8')
+ destConn = params['destConn']
+ remoteHost = params['remoteHost']
+ secure = params['secure']
+ _destConn = destConn.get()
+
+ cb('starting a migration')
+ kimchi_log.debug('migrate %s start' % name)
+ dom = self.get_vm(name, self.conn)
+ flag = 0
+ if secure:
+ flag |= (libvirt.VIR_MIGRATE_PEER2PEER |
+ libvirt.VIR_MIGRATE_TUNNELLED)
+
+ state = DOM_STATE_MAP[dom.info()[0]]
+ if state in ['running', 'paused']:
+ flag |= libvirt.VIR_MIGRATE_LIVE
+ elif state == 'shutoff':
+ flag |= (libvirt.VIR_MIGRATE_OFFLINE |
+ libvirt.VIR_MIGRATE_PERSIST_DEST)
+ else:
+ kimchi_log.debug('Can not migrate vm %s when its in %s.'
+ % (name, state))
+ raise OperationFailed("KCHVM0041E", {'name': name,
+ 'state': state})
+ try:
+ dom.migrate(_destConn, flag, None, None, 0)
+ except libvirt.libvirtError as e:
+ kimchi_log.error('migrate %s to %s failed' % (name, remoteHost))
+ self._recover(name, destConn, cb)
+ kimchi_log.debug('finish migrate %s' % name)
+ cb('Migrate finished', False)
+ raise OperationFailed('KCHVM0042E', {'err': e.message,
+ 'name': name})
+ kimchi_log.debug('finish migrate %s' % name)
+ cb('Migrate finished', True)
+
+ def migrate(self, name, remoteHost, user, passwd=None, secure=False):
+ """Migrate a virtual machine to a remote server.
+
+ Arguments:
+ name -- The name of the virtual machine to be migrated.
+ remoteHost -- Remote server that the VM will be migrated to.
+ user -- Login user on remote server,prefer root.
+ passwd -- The password of the user on remote server,
+ if the local server
+ has ssk key exchanged, then this param can be NULL.
+ secure -- Migrate in P2P secure tunel mode.(optional)
+
+ Return:
+ A Task running the clone operation.
+ """
+ name = name.decode('utf-8')
+ remoteHost = remoteHost.decode('utf-8')
+ user = user.decode('utf-8')
+
+ destConn = self._getRemoteConn(remoteHost, user, passwd)
+ self._preCheck(name, destConn, remoteHost)
+
+ params = {'name': name,
+ 'destConn': destConn,
+ 'remoteHost': remoteHost,
+ 'secure': secure}
+ task_id = add_task('/vms/%s/migrate' % name, self._do_migrate,
+ self.objstore, params)
+
+ return self.task.lookup(task_id)
+
+ def _recover(self, name, destconn, cb):
+ """Recover from a failed migration.
+
+ Arguments:
+ "cb": A callback function to signal the Task's progress.
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ """
+ try:
+ destvm = self.get_vm(name, destconn)
+ except KimchiException as e:
+ if type(e) == NotFoundError:
+ return
+
+ cb('recover from a failed migration', False)
+ kimchi_log.debug('recover from a failed migrate %s' % name)
+ try:
+ destvm.destroy()
+ kimchi_log.error("Failed to destory migrate vm %s on \
+ destination server" % name)
+ except libvirt.libvirtError as e:
+ raise OperationFailed('KCHVM0043E', {'name': name,
+ 'err': e.message})
+
def poweroff(self, name):
dom = self.get_vm(name, self.conn)
try:
--
1.8.3.1
9 years, 9 months
[v6 1/1] Virtual machine migration
by simonjin@linux.vnet.ibm.com
From: Simon Jin <simonjin(a)linux.vnet.ibm.com>
v6-v5:Fix pep8, delete migratation finish func.
Signed-off-by: Simon Jin <simonjin(a)linux.vnet.ibm.com>
---
docs/API.md | 5 ++
src/kimchi/control/vms.py | 3 +
src/kimchi/i18n.py | 8 +++
src/kimchi/model/vms.py | 166 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 180 insertions(+), 2 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index 210a036..4408764 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -132,6 +132,11 @@ the following general conventions:
It emulates the power reset button on a machine. Note that there is a
risk of data loss caused by reset without the guest OS shutdown.
* connect: Prepare the connection for spice or vnc
+* migrate: Migrate a virtual machine to a remote server, only support live mode without block migration.
+ * remoteHost: IP address or hostname of the remote server.
+ * user: User of the remote server.
+ * passwd: Passwd of user on remote server.
+ * secure: Migrate in P2P secure tunel mode.(optional)
* clone: Create a new VM identical to this VM. The new VM's name, UUID and
network MAC addresses will be generated automatically. Each existing
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index a1589ef..3638b3a 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -42,6 +42,9 @@ class VM(Resource):
for ident, node in sub_nodes.items():
setattr(self, ident, node(model, self.ident))
self.start = self.generate_action_handler('start')
+ self.migrate = self.generate_action_handler_task('migrate',
+ ['remoteHost', 'user',
+ 'passwd', 'secure'])
self.poweroff = self.generate_action_handler('poweroff')
self.shutdown = self.generate_action_handler('shutdown')
self.reset = self.generate_action_handler('reset')
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index e3a051c..81a6dbb 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -107,6 +107,14 @@ messages = {
"KCHVM0033E": _("Virtual machine '%(name)s' must be stopped before cloning it."),
"KCHVM0034E": _("Insufficient disk space to clone virtual machine '%(name)s'"),
"KCHVM0035E": _("Unable to clone VM '%(name)s'. Details: %(err)s"),
+ "KCHVM0036E": _("Migrate %(name)s to localhost %(remoteHost)s is not allowed."),
+ "KCHVM0037E": _("vm %(name)s is already in migrating, can not perform migrate on it again."),
+ "KCHVM0038E": _("Can't migrate vm %(name)s on Hypervisor arch %(srcarch)s.to a different Hypervisor arch %(destarch)s."),
+ "KCHVM0039E": _("Can't migrate %(name)s from %(srchyp)s to a different Hypervisor type %(desthyp)s."),
+ "KCHVM0040E": _("Can't migrate vm %(name)s, vm has passthrough device %(hostdevs)s."),
+ "KCHVM0041E": _("Can not migrate vm %(name)s when its in %(state)s state."),
+ "KCHVM0042E": _("Failed Migrate vm %(name)s due error: %(err)s"),
+ "KCHVM0043E": _("Failed to destroy vm %(name)s from migration recover. Details: %(err)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/kimchi/model/vms.py b/src/kimchi/model/vms.py
index 3aa1145..023bd10 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -33,9 +33,10 @@ from cherrypy.process.plugins import BackgroundTask
from kimchi import model, vnc
from kimchi.config import READONLY_POOL_TYPE, config
from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.exception import NotFoundError, OperationFailed, KimchiException
from kimchi.model.config import CapabilitiesModel
-from kimchi.model.tasks import TaskModel
+from kimchi.model.libvirtconnection import LibvirtConnection
+from kimchi.model.tasks import TaskModel, TasksModel
from kimchi.model.templates import TemplateModel
from kimchi.model.utils import get_vm_name
from kimchi.model.utils import get_metadata_node
@@ -268,6 +269,7 @@ class VMModel(object):
self.groups = import_class('kimchi.model.groups.GroupsModel')(**kargs)
self.vms = VMsModel(**kargs)
self.task = TaskModel(**kargs)
+ self.tasks = TasksModel(**kargs)
self.storagepool = model.storagepools.StoragePoolModel(**kargs)
self.storagevolume = model.storagevolumes.StorageVolumeModel(**kargs)
self.storagevolumes = model.storagevolumes.StorageVolumesModel(**kargs)
@@ -850,6 +852,166 @@ class VMModel(object):
raise OperationFailed("KCHVM0019E",
{'name': name, 'err': e.get_error_message()})
+ def _preCheck(self, name, destConn, remoteHost):
+ """Pre check before migration"""
+
+ kimchi_log.debug('precheck migrate %s' % name)
+ _destConn = destConn.get()
+ if remoteHost in ['localhost', '127.0.0.1']:
+ kimchi_log.debug('vm %s Can not migrate to localhost.' % name)
+ raise OperationFailed("KCHVM0036E", {'name': name,
+ 'remoteHost': remoteHost})
+ # check if the vm already in migration
+ try:
+ alltasks = self.tasks.get_list()
+ for taskid in alltasks:
+ taskobj = self.task.lookup(taskid)
+ if taskobj['target_uri'] == "/vms/%s/migrate" % name \
+ and taskobj['status'] == "running":
+ kimchi_log.debug('vm %s is already in migrating.' % name)
+ raise OperationFailed("KCHVM0037E", {'name': name})
+ except NotFoundError:
+ kimchi_log.debug('No previous migrate on vm %s.' % name)
+
+ srcarch = self.conn.get().getInfo()[0]
+ destarch = _destConn.getInfo()[0]
+ if srcarch != destarch:
+ kimchi_log.debug('vm %s can not migrate to different arch server.'
+ % (name, destarch))
+ raise OperationFailed("KCHVM0038E", {'name': name,
+ 'srcarch': srcarch,
+ 'destarch': destarch})
+ srchyp = self.conn.get().getType()
+ desthyp = _destConn.getType()
+ if srchyp != desthyp:
+ kimchi_log.debug('vm %s can not migrate to a different hypervisor'
+ % name)
+ raise OperationFailed("KCHVM0039E", {'name': name,
+ 'srchyp': srchyp,
+ 'desthyp': desthyp})
+
+ # model.host import DOM_STATE_MAP, vmhostdevs import host,
+ # to void loop import DOM_STATE_MAP, move import VMHostDevsModel here
+ from kimchi.model.vmhostdevs import VMHostDevsModel
+ # check if there is any passthrough devices belong to this vm
+ vmhostdevs = VMHostDevsModel(conn=self.conn)
+ hostdevs = vmhostdevs.get_list(name)
+ if hostdevs:
+ raise OperationFailed("KCHVM0040E", {'name': name,
+ 'hostdevs': hostdevs})
+
+ @staticmethod
+ def _getRemoteConn(remoteHost, user, passwd, transport='ssh'):
+ """open destination connect to migrate"""
+
+ duri = 'qemu+%s://%s@%s/system' % (transport, user, remoteHost)
+ conn = LibvirtConnection(duri)
+ return conn
+
+ def _do_migrate(self, cb, params):
+ """Asynchronous function which performs the migrate operation.
+
+ Arguments:
+ cb -- A callback function to signal the Task's progress.
+ params -- A dict with the following values:
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ "remoteHost": The migration destination server.
+ "secure": Migrate in P2P secure tunel mode.(optional)
+ """
+
+ name = params['name'].decode('utf-8')
+ destConn = params['destConn']
+ remoteHost = params['remoteHost']
+ secure = params['secure']
+ _destConn = destConn.get()
+
+ cb('starting a migration')
+ kimchi_log.debug('migrate %s start' % name)
+ dom = self.get_vm(name, self.conn)
+ flag = 0
+ if secure:
+ flag |= (libvirt.VIR_MIGRATE_PEER2PEER |
+ libvirt.VIR_MIGRATE_TUNNELLED)
+
+ state = DOM_STATE_MAP[dom.info()[0]]
+ if state in ['running', 'paused']:
+ flag |= libvirt.VIR_MIGRATE_LIVE
+ elif state == 'shutoff':
+ flag |= (libvirt.VIR_MIGRATE_OFFLINE |
+ libvirt.VIR_MIGRATE_PERSIST_DEST)
+ else:
+ kimchi_log.debug('Can not migrate vm %s when its in %s.'
+ % (name, state))
+ raise OperationFailed("KCHVM0041E", {'name': name,
+ 'state': state})
+ try:
+ dom.migrate(_destConn, flag, None, None, 0)
+ except libvirt.libvirtError as e:
+ kimchi_log.error('migrate %s to %s failed' % (name, remoteHost))
+ self._recover(name, destConn, cb)
+ kimchi_log.debug('finish migrate %s' % name)
+ cb('Migrate finished', False)
+ raise OperationFailed('KCHVM0042E', {'err': e.message,
+ 'name': name})
+ kimchi_log.debug('finish migrate %s' % name)
+ cb('Migrate finished', True)
+
+ def migrate(self, name, remoteHost, user, passwd=None, secure=False):
+ """Migrate a virtual machine to a remote server.
+
+ Arguments:
+ name -- The name of the virtual machine to be migrated.
+ remoteHost -- Remote server that the VM will be migrated to.
+ user -- Login user on remote server,prefer root.
+ passwd -- The password of the user on remote server,
+ if the local server
+ has ssk key exchanged, then this param can be NULL.
+ secure -- Migrate in P2P secure tunel mode.(optional)
+
+ Return:
+ A Task running the clone operation.
+ """
+ name = name.decode('utf-8')
+ remoteHost = remoteHost.decode('utf-8')
+ user = user.decode('utf-8')
+
+ destConn = self._getRemoteConn(remoteHost, user, passwd)
+ self._preCheck(name, destConn, remoteHost)
+
+ params = {'name': name,
+ 'destConn': destConn,
+ 'remoteHost': remoteHost,
+ 'secure': secure}
+ task_id = add_task('/vms/%s/migrate' % name, self._do_migrate,
+ self.objstore, params)
+
+ return self.task.lookup(task_id)
+
+ def _recover(self, name, destconn, cb):
+ """Recover from a failed migration.
+
+ Arguments:
+ "cb": A callback function to signal the Task's progress.
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ """
+ try:
+ destvm = self.get_vm(name, destconn)
+ except KimchiException as e:
+ if type(e) == NotFoundError:
+ return
+
+ cb('recover from a failed migration', False)
+ kimchi_log.debug('recover from a failed migrate %s' % name)
+ try:
+ destvm.destroy()
+ kimchi_log.error("Failed to destory migrate vm %s on \
+ destination server" % name)
+ except libvirt.libvirtError as e:
+ raise OperationFailed('KCHVM0043E', {'name': name,
+ 'err': e.message})
+
def poweroff(self, name):
dom = self.get_vm(name, self.conn)
try:
--
1.8.3.1
9 years, 9 months
Scrum meetings during the holidays
by Aline Manera
Hi all,
Specially in the next two weeks, the scrum meeting will be on Tuesdays
at the usual time (11AM BRST) due the holidays.
So instead of Dec 24th, the scrum meeting will be on Dec 23th; and
instead of Dec 31st, it will be on Dec 30th.
See you there.
Regards,
Aline Manera
9 years, 9 months
Kimchi 1.4 is released!
by Aline Manera
On behalf of everyone who has worked hard on this release, I am pleased
to announce the availability of *Kimchi 1.4*!
This release adds many new features including:
✔ Guest PCI passthrough devices
✔ Guest disk hot plug
✔ Guest cloning
✔ Snapshot support
✔ SMT support on Templates
✔ LDAP authentication
We have worked hard to ensure that Kimchi runs well on the most popular
Linux distributions including: Fedora 20, Ubuntu 14.10, openSUSE 13.2,
and RHEL7. Kimchi uses standard Linux interfaces so it should run well
on many other distributions too.
You can easily grab this release in tarball format or via git:
✔ https://github.com/kimchi-project/kimchi/archive/1.4.0.tar.gz
✔ git clone https://github.com/kimchi-project/kimchi.git
There are also some packages available at:
✔ http://kimchi-project.github.io/kimchi/downloads/
Go ahead! Give it a try and let us know what you think!
Regards,
Aline Manera
9 years, 10 months