Reviewed-by: Daniel Barboza <danielhb(a)linux.vnet.ibm.com>
On 04/11/2014 05:58 PM, Aline Manera wrote:
From: Aline Manera <alinefm(a)br.ibm.com>
To update (add/remove) the users and groups of a virtual machine, you
should use the REST command:
/vms/<vm_name> PUT "{ 'users': [ 'user1', 'user2' ],
'groups': [ 'group1', 'group2' ] }"
Users and groups associated with a virtual machine must be valid when
they are
added to the VM. Such verification is not done when they are removed
from the
system. This means that virtual machines may contain outdated
information about
users and groups.
Signed-off-by: Crístian Viana <vianac(a)linux.vnet.ibm.com>
Signed-off-by: Aline Manera <alinefm(a)br.ibm.com>
---
docs/API.md | 2 ++
po/en_US.po | 24 +++++++++++++++++--
po/kimchi.pot | 24 +++++++++++++++++--
po/pt_BR.po | 57 +++++++++++++++++++++++++++++++--------------
po/zh_CN.po | 56 ++++++++++++++++++++++++++++++--------------
src/kimchi/control/vms.py | 2 +-
src/kimchi/i18n.py | 4 +++-
src/kimchi/mockmodel.py | 3 +--
src/kimchi/model/vms.py | 55 +++++++++++++++++++++++++++++++++++++------
tests/test_model.py | 40 +++++++++++++++++++++++++++++++
10 files changed, 217 insertions(+), 50 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index b726b9c..2c57665 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -101,6 +101,8 @@ the following general conventions:
* **DELETE**: Remove the Virtual Machine
* **PUT**: update the parameters of existed VM
* name: New name for this VM (only applied for shutoff VM)
+ * users: New list of system users.
+ * groups: New list of system groups.
* **POST**: *See Virtual Machine Actions*
**Actions (POST):**
diff --git a/po/en_US.po b/po/en_US.po
index 0b05f1e..f7b6e38 100644
--- a/po/en_US.po
+++ b/po/en_US.po
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-04-09 17:11-0300\n"
+"POT-Creation-Date: 2014-04-10 14:32-0300\n"
"PO-Revision-Date: 2013-07-11 17:32-0400\n"
"Last-Translator: Crístian Viana <vianac(a)linux.vnet.ibm.com>\n"
"Language-Team: English\n"
@@ -914,7 +914,7 @@ msgid "Unable to create virtual machine %(name)s. Details:
%(err)s"
msgstr ""
#, python-format
-msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
+msgid "Unable to update virtual machine %(name)s. Details: %(err)s"
msgstr ""
#, python-format
@@ -957,6 +957,26 @@ msgstr ""
msgid "Unable to delete virtual machine %(name)s. Details: %(err)s"
msgstr ""
+msgid "User names list must be an array"
+msgstr ""
+
+msgid "User name must be a string"
+msgstr ""
+
+msgid "Group names list must be an array"
+msgstr ""
+
+msgid "Group name must be a string"
+msgstr ""
+
+#, python-format
+msgid "User %(user)s does not exist"
+msgstr ""
+
+#, python-format
+msgid "Group %(group)s does not exist"
+msgstr ""
+
#, python-format
msgid "Interface %(iface)s does not exist in virtual machine %(name)s"
msgstr ""
diff --git a/po/kimchi.pot b/po/kimchi.pot
index 92d5401..1da141b 100755
--- a/po/kimchi.pot
+++ b/po/kimchi.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-04-09 17:11-0300\n"
+"POT-Creation-Date: 2014-04-10 14:32-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL(a)li.org>\n"
@@ -902,7 +902,7 @@ msgid "Unable to create virtual machine %(name)s. Details:
%(err)s"
msgstr ""
#, python-format
-msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
+msgid "Unable to update virtual machine %(name)s. Details: %(err)s"
msgstr ""
#, python-format
@@ -945,6 +945,26 @@ msgstr ""
msgid "Unable to delete virtual machine %(name)s. Details: %(err)s"
msgstr ""
+msgid "User names list must be an array"
+msgstr ""
+
+msgid "User name must be a string"
+msgstr ""
+
+msgid "Group names list must be an array"
+msgstr ""
+
+msgid "Group name must be a string"
+msgstr ""
+
+#, python-format
+msgid "User %(user)s does not exist"
+msgstr ""
+
+#, python-format
+msgid "Group %(group)s does not exist"
+msgstr ""
+
#, python-format
msgid "Interface %(iface)s does not exist in virtual machine %(name)s"
msgstr ""
diff --git a/po/pt_BR.po b/po/pt_BR.po
index cbb2b31..098ca8e 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-04-09 17:11-0300\n"
+"POT-Creation-Date: 2014-04-10 14:32-0300\n"
"PO-Revision-Date: 2013-06-27 10:48+0000\n"
"Last-Translator: Crístian Viana <vianac(a)linux.vnet.ibm.com>\n"
"Language-Team: Aline Manera <alinefm(a)br.ibm.com>\n"
@@ -407,13 +407,10 @@ msgstr ""
msgid "Create a network"
msgstr "Criar uma rede"
-#, fuzzy
msgid ""
"This network is not persistent. Instead of stop, this action will "
"permanently delete it. Would you like to continue?"
msgstr ""
-"O storage pool não é persistente. Ao invés de desativar, essa ação vai "
-"removê-lo permanentemente. Deseja continuar?"
msgid ""
"This will permanently delete the storage pool. Would you like to continue?"
@@ -840,13 +837,13 @@ msgstr "Essa API suporta apenas JSON"
msgid "Datastore is not initiated in the model object."
msgstr "Datastore não é inicializado no objeto modelo."
-#, fuzzy, python-format
+#, python-format
msgid "Unable to start task due error: %(err)s"
-msgstr "Não foi possível iniciar a máquina virtual %(name)s. Detalhes:
%(err)s"
+msgstr ""
-#, fuzzy, python-format
+#, python-format
msgid "Authentication failed for user '%(username)s'. [Error code:
%(code)s]"
-msgstr "O usuário '%(userid)s' falhou na autenticação. [Error code:
%(code)s]"
+msgstr ""
msgid "You are not authorized to access Kimchi"
msgstr "Você não está autorizado para acessar o Kimchi"
@@ -965,8 +962,8 @@ msgid "Unable to create virtual machine %(name)s. Details:
%(err)s"
msgstr "Não é possível criar a máquina virtual %(name)s. Detalhes: %(err)s"
#, python-format
-msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
-msgstr "Não é possível renomear a máquina virtual %(name)s. Detalhes:
%(err)s"
+msgid "Unable to update virtual machine %(name)s. Details: %(err)s"
+msgstr ""
#, python-format
msgid "Unable to retrieve virtual machine %(name)s. Details: %(err)s"
@@ -1008,6 +1005,26 @@ msgstr "Não é possível parar a máquina virtual %(name)s.
Detalhes: %(err)s"
msgid "Unable to delete virtual machine %(name)s. Details: %(err)s"
msgstr "Não é possível apagar a máquina virtual %(name)s. Detalhes: %(err)s"
+msgid "User names list must be an array"
+msgstr ""
+
+msgid "User name must be a string"
+msgstr ""
+
+msgid "Group names list must be an array"
+msgstr ""
+
+msgid "Group name must be a string"
+msgstr ""
+
+#, python-format
+msgid "User %(user)s does not exist"
+msgstr ""
+
+#, python-format
+msgid "Group %(group)s does not exist"
+msgstr ""
+
#, python-format
msgid "Interface %(iface)s does not exist in virtual machine %(name)s"
msgstr "Interface %(iface)s não existe na máquina virtual %(name)s"
@@ -1107,13 +1124,13 @@ msgstr ""
msgid "The volume: %(volume)s in not in storage pool %(pool)s"
msgstr "O volume de storage: %(volume)s não existe no storage pool %(pool)s"
-#, fuzzy, python-format
+#, python-format
msgid "Unable to create template due error: %(err)s"
-msgstr "Não é possível criar o storage pool %(name)s.Detalhes: %(err)s"
+msgstr ""
-#, fuzzy, python-format
+#, python-format
msgid "Unable to delete template due error: %(err)s"
-msgstr "Incapaz de deletar a rede ativa %(name)s"
+msgstr ""
#, python-format
msgid "Storage pool %(name)s already exists"
@@ -1277,9 +1294,9 @@ msgstr ""
"Um grupo de volume chamado '%(name)s' já existe. Por favor, escolher
outro "
"nome para criar o pool lógico."
-#, fuzzy, python-format
+#, python-format
msgid "Unable to update database with deep scan information due error:
%(err)s"
-msgstr "Incapaz de carregar as informações do repositório. Detalhes: %(err)s"
+msgstr ""
#, python-format
msgid "Storage volume %(name)s already exists"
@@ -1356,10 +1373,10 @@ msgstr "Formato do volume não suportado"
msgid "Storage volume requires a volume name"
msgstr "Volume requer um nome"
-#, fuzzy, python-format
+#, python-format
msgid ""
"Unable to update database with storage volume information due error:
%(err)s"
-msgstr "Incapaz de deletar o volume %(name)s. Detalhes: %(err)s"
+msgstr ""
#, python-format
msgid "Interface %(name)s does not exist"
@@ -1686,3 +1703,7 @@ msgstr "Não é possível adicionar o repositório. Detalhes:
%(err)s"
#, python-format
msgid "Unable to remove repository. Details: '%(err)s'"
msgstr "Incapaz de remover o repositório. Detalhes: %(err)s"
+
+#~ msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
+#~ msgstr ""
+#~ "Não é possível renomear a máquina virtual %(name)s. Detalhes: %(err)s"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 3b3754c..a1a72b9 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-04-09 17:11-0300\n"
+"POT-Creation-Date: 2014-04-10 14:32-0300\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"
@@ -389,11 +389,10 @@ msgstr "此操作将中断依赖此网络的虚拟机的网络连接。"
msgid "Create a network"
msgstr "创建一个网络"
-#, fuzzy
msgid ""
"This network is not persistent. Instead of stop, this action will "
"permanently delete it. Would you like to continue?"
-msgstr "对于非持久存储池,这个操作将会永久删除存储池而不是停用。是否继续?"
+msgstr ""
msgid ""
"This will permanently delete the storage pool. Would you like to continue?"
@@ -812,13 +811,13 @@ msgstr "这个API仅支持JSON"
msgid "Datastore is not initiated in the model object."
msgstr "尚未为model对象初始化数据存储。"
-#, fuzzy, python-format
+#, python-format
msgid "Unable to start task due error: %(err)s"
-msgstr "不能启动虚拟机 %(name)s. 详情:%(err)s"
+msgstr ""
-#, fuzzy, python-format
+#, python-format
msgid "Authentication failed for user '%(username)s'. [Error code:
%(code)s]"
-msgstr "用户 '%(userid)s' 验证失败。[错误代码: %(code)s]"
+msgstr ""
msgid "You are not authorized to access Kimchi"
msgstr "您没有被授权访问Kimchi"
@@ -926,8 +925,8 @@ msgid "Unable to create virtual machine %(name)s. Details:
%(err)s"
msgstr "不能创建虚拟机%(name)s。详情:%(err)s"
#, python-format
-msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
-msgstr "不能重命名虚拟机%(name)s。详情:%(err)s"
+msgid "Unable to update virtual machine %(name)s. Details: %(err)s"
+msgstr ""
#, python-format
msgid "Unable to retrieve virtual machine %(name)s. Details: %(err)s"
@@ -969,6 +968,26 @@ msgstr "不能停止虚拟机 %(name)s. 详情:%(err)s"
msgid "Unable to delete virtual machine %(name)s. Details: %(err)s"
msgstr "不能删除虚拟机 %(name)s. 详情:%(err)s"
+msgid "User names list must be an array"
+msgstr ""
+
+msgid "User name must be a string"
+msgstr ""
+
+msgid "Group names list must be an array"
+msgstr ""
+
+msgid "Group name must be a string"
+msgstr ""
+
+#, python-format
+msgid "User %(user)s does not exist"
+msgstr ""
+
+#, python-format
+msgid "Group %(group)s does not exist"
+msgstr ""
+
#, python-format
msgid "Interface %(iface)s does not exist in virtual machine %(name)s"
msgstr "虚拟机 %(name)s 中没有接口 %(iface)s"
@@ -1061,13 +1080,13 @@ msgstr "当模板的存储池是iscsi或scsi时,必须为模板指定一个卷
msgid "The volume: %(volume)s in not in storage pool %(pool)s"
msgstr "存储池%(pool)s中没有存储卷%(volume)s"
-#, fuzzy, python-format
+#, python-format
msgid "Unable to create template due error: %(err)s"
-msgstr "不能创建存储池 %(name)s。详情: %(err)s"
+msgstr ""
-#, fuzzy, python-format
+#, python-format
msgid "Unable to delete template due error: %(err)s"
-msgstr "不能删除激活的网络%(name)s"
+msgstr ""
#, python-format
msgid "Storage pool %(name)s already exists"
@@ -1210,9 +1229,9 @@ msgid ""
"to create the logical pool."
msgstr "卷组'%(name)s'已经存在,请选择其它的名字来创建逻辑存储池。"
-#, fuzzy, python-format
+#, python-format
msgid "Unable to update database with deep scan information due error:
%(err)s"
-msgstr "不能获取软件仓库的信息。详情:'%(err)s'"
+msgstr ""
#, python-format
msgid "Storage volume %(name)s already exists"
@@ -1282,10 +1301,10 @@ msgstr "存储卷格式不支持"
msgid "Storage volume requires a volume name"
msgstr "存储卷需要名字"
-#, fuzzy, python-format
+#, python-format
msgid ""
"Unable to update database with storage volume information due error:
%(err)s"
-msgstr "不能删除存储卷%(name)s。详情:%(err)s"
+msgstr ""
#, python-format
msgid "Interface %(name)s does not exist"
@@ -1581,3 +1600,6 @@ msgstr "不能增加软件仓库。详情:'%(err)s'"
#, python-format
msgid "Unable to remove repository. Details: '%(err)s'"
msgstr "不能移除软件仓库。详情:'%(err)s'"
+
+#~ msgid "Unable to rename virtual machine %(name)s. Details: %(err)s"
+#~ msgstr "不能重命名虚拟机%(name)s。详情:%(err)s"
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 63c1e24..a44764b 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -32,7 +32,7 @@ class VMs(Collection):
class VM(Resource):
def __init__(self, model, ident):
super(VM, self).__init__(model, ident)
- self.update_params = ["name"]
+ self.update_params = ["name", "users", "groups"]
self.screenshot = VMScreenShot(model, ident)
self.uri_fmt = '/vms/%s'
for ident, node in sub_nodes.items():
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 20f9716..ef46505 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -68,7 +68,7 @@ messages = {
"KCHVM0005E": _("Remote ISO image is not supported by this
server."),
"KCHVM0006E": _("Screenshot not supported for virtual machine
%(name)s"),
"KCHVM0007E": _("Unable to create virtual machine %(name)s. Details:
%(err)s"),
- "KCHVM0008E": _("Unable to rename virtual machine %(name)s. Details:
%(err)s"),
+ "KCHVM0008E": _("Unable to update virtual machine %(name)s. Details:
%(err)s"),
"KCHVM0009E": _("Unable to retrieve virtual machine %(name)s.
Details: %(err)s"),
"KCHVM0010E": _("Unable to connect to powered off machine
%(name)s."),
"KCHVM0011E": _("Virtual machine name must be a string"),
@@ -84,6 +84,8 @@ messages = {
"KCHVM0023E": _("User name must be a string"),
"KCHVM0024E": _("Group names list must be an array"),
"KCHVM0025E": _("Group name must be a string"),
+ "KCHVM0026E": _("User %(user)s does not exist"),
+ "KCHVM0027E": _("Group %(group)s does not exist"),
"KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual
machine %(name)s"),
"KCHVMIF0002E": _("Network %(network)s specified for virtual machine
%(name)s does not exist"),
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
index 87ca21c..4fb28c6 100644
--- a/src/kimchi/mockmodel.py
+++ b/src/kimchi/mockmodel.py
@@ -49,7 +49,6 @@ from kimchi.exception import MissingParameter, NotFoundError,
OperationFailed
from kimchi.model.storagepools import ISO_POOL_NAME
from kimchi.model.storageservers import STORAGE_SERVERS
from kimchi.model.utils import get_vm_name
-from kimchi.model.vms import VM_STATIC_UPDATE_PARAMS
from kimchi.objectstore import ObjectStore
from kimchi.screenshot import VMScreenshot
from kimchi.utils import pool_name_from_uri
@@ -98,7 +97,7 @@ class MockModel(object):
self._mock_vms[dom.name] = dom
for key, val in params.items():
- if key in VM_STATIC_UPDATE_PARAMS and key in dom.info:
+ if key in dom.info:
dom.info[key] = val
def _live_vm_update(self, dom, params):
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index ba091e5..f567390 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -17,6 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import lxml.etree as ET
import os
import time
import uuid
@@ -24,9 +25,11 @@ from xml.etree import ElementTree
import libvirt
from cherrypy.process.plugins import BackgroundTask
+from lxml.builder import E
from kimchi import vnc
from kimchi import xmlutils
+from kimchi.auth import Group, User
from kimchi.config import READONLY_POOL_TYPE
from kimchi.exception import InvalidOperation, InvalidParameter
from kimchi.exception import NotFoundError, OperationFailed
@@ -240,25 +243,63 @@ class VMModel(object):
self._live_vm_update(dom, params)
return dom.name().decode('utf-8')
+ def _get_metadata_node(self, users, groups):
+ access = E.access()
+ for user in users:
+ access.append(E.user(user))
+
+ for group in groups:
+ access.append(E.group(group))
+
+ return E.metadata(E.kimchi(access))
+
def _static_vm_update(self, dom, params):
state = DOM_STATE_MAP[dom.info()[0]]
old_xml = new_xml = dom.XMLDesc(0)
+ metadata_xpath = "/domain/metadata/kimchi/access/%s"
+ users = xpath_get_text(old_xml, metadata_xpath % "user")
+ groups = xpath_get_text(old_xml, metadata_xpath % "group")
+
for key, val in params.items():
- if key in VM_STATIC_UPDATE_PARAMS:
- xpath = VM_STATIC_UPDATE_PARAMS[key]
- new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
+ if key == 'users':
+ for user in val:
+ if not User(user).exists():
+ raise OperationFailed("KCHVM0026E", {'user':
user})
+ users = val
+ elif key == 'groups':
+ for group in val:
+ if not Group(group).exists():
+ raise OperationFailed("KCHVM0027E", {'group':
group})
+ groups = val
+ else:
+ if key in VM_STATIC_UPDATE_PARAMS:
+ xpath = VM_STATIC_UPDATE_PARAMS[key]
+ new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
+ conn = self.conn.get()
try:
if 'name' in params:
if state == 'running':
msg_args = {'name': dom.name(), 'new_name':
params['name']}
raise InvalidParameter("KCHVM0003E", msg_args)
- else:
- dom.undefine()
- conn = self.conn.get()
- dom = conn.defineXML(new_xml)
+
+ # Undefine old vm and create a new one with updated values
+ dom.undefine()
+ conn = self.conn.get()
+ dom = conn.defineXML(new_xml)
+
+ # Update metadata element
+ root = ET.fromstring(new_xml)
+ current_metadata = root.find('metadata')
+ new_metadata = self._get_metadata_node(users, groups)
+ if current_metadata is not None:
+ root.replace(current_metadata, new_metadata)
+ else:
+ root.append(new_metadata)
+ dom = conn.defineXML(ET.tostring(root))
+
except libvirt.libvirtError as e:
dom = conn.defineXML(old_xml)
raise OperationFailed("KCHVM0008E", {'name': dom.name(),
diff --git a/tests/test_model.py b/tests/test_model.py
index 00c02d3..357d969 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -18,9 +18,11 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import grp
import os
import platform
import psutil
+import pwd
import shutil
import tempfile
import threading
@@ -608,6 +610,44 @@ class ModelTests(unittest.TestCase):
rollback.prependDefer(self._rollback_wrapper, inst.vm_delete,
u'пeω-∨м')
+ # change only VM users - groups are not changed (default is empty)
+ users = ['root']
+ inst.vm_update(u'пeω-∨м', {'users': users})
+ self.assertEquals(users,
inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['groups'])
+
+ # change only VM groups - users are not changed (default is empty)
+ groups = ['root']
+ inst.vm_update(u'пeω-∨м', {'groups': groups})
+ self.assertEquals(users,
inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals(groups,
inst.vm_lookup(u'пeω-∨м')['groups'])
+
+ # change VM users and groups by adding a new element to each one
+ users.append(pwd.getpwuid(os.getuid()).pw_name)
+ groups.append(grp.getgrgid(os.getgid()).gr_name)
+ inst.vm_update(u'пeω-∨м', {'users': users, 'groups':
groups})
+ self.assertEquals(users,
inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals(groups,
inst.vm_lookup(u'пeω-∨м')['groups'])
+
+ # change VM users (wrong value) and groups
+ # when an error occurs, everything fails and nothing is changed
+ self.assertRaises(OperationFailed, inst.vm_update, u'пeω-∨м',
+ {'users': ['userdoesnotexist'],
'groups': []})
+ self.assertEquals(users,
inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals(groups,
inst.vm_lookup(u'пeω-∨м')['groups'])
+
+ # change VM users and groups (wrong value)
+ # when an error occurs, everything fails and nothing is changed
+ self.assertRaises(OperationFailed, inst.vm_update, u'пeω-∨м',
+ {'users': [], 'groups':
['groupdoesnotexist']})
+ self.assertEquals(users,
inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals(groups,
inst.vm_lookup(u'пeω-∨м')['groups'])
+
+ # change VM users and groups by removing all elements
+ inst.vm_update(u'пeω-∨м', {'users': [], 'groups':
[]})
+ self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['users'])
+ self.assertEquals([], inst.vm_lookup(u'пeω-∨м')['groups'])
+
@unittest.skipUnless(utils.running_as_root(), 'Must be run as root')
def test_network(self):
inst = model.Model('qemu:///system', self.tmp_store)