[V6 0/5] bug fix: get user and group when VM is running

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V5 -> V6: rebase V4 -> V5: it is wrong to call dom.isPersistent in V4, fix it. V3 -> V4: work around if libvirt do not support metadata API well. V2 -> V3: move the virDomain.metadata and virDomain.setMetadata to model/utils.py add testcase V1 -> V2: libvirt also support virDomain.metadata and virDomain.setMetadata two api. use virDomain.metadata to get the user and group. use virDomain.setMetadata to set the user and group. ShaoHe Feng (5): Add two function to set and get domain xml metadata bug fix: get user and group when vm is living. update test case to set/get user and group when VM is running write the template OS info to vm metadata manually manage the metadata element src/kimchi/i18n.py | 1 + src/kimchi/model/utils.py | 73 +++++++++++++++++++++++++++++++++ src/kimchi/model/vms.py | 102 ++++++++++++++++++++++++++++------------------ tests/test_model.py | 13 ++++++ 4 files changed, 150 insertions(+), 39 deletions(-) -- 1.9.0

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Libvirt support two API to set and get domain xml metadata. These two function wrap them. Put them in model/utils.py, so vm_storage and vm_iface can make use of them. For set domain xml metadata, we set live and config xml by default. For get domain xml metadata, we get the current xml by default. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/utils.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index a1af1f7..e28f689 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -87,6 +87,7 @@ messages = { "KCHVM0027E": _("User(s) '%(users)s' do not exist"), "KCHVM0028E": _("Group(s) '%(groups)s' do not exist"), "KCHVM0029E": _("Unable to shutdown virtual machine %(name)s. Details: %(err)s"), + "KCHVM0030E": _("Unable to get access metadata of virtual machine %(name)s. Details: %(err)s"), "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/model/utils.py b/src/kimchi/model/utils.py index 80e1801..be5e526 100644 --- a/src/kimchi/model/utils.py +++ b/src/kimchi/model/utils.py @@ -18,6 +18,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.exception import OperationFailed +import libvirt +from lxml import etree + + +KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi/metadata/" def get_vm_name(vm_name, t_name, name_list): @@ -28,3 +33,39 @@ def get_vm_name(vm_name, t_name, name_list): if vm_name not in name_list: return vm_name raise OperationFailed("KCHUTILS0003E") + + +def get_vm_config_flag(dom, mode="persistent"): + # libvirt.VIR_DOMAIN_AFFECT_CURRENT is 0 + # VIR_DOMAIN_AFFECT_LIVE is 1, VIR_DOMAIN_AFFECT_CONFIG is 2 + flag = {"live": libvirt.VIR_DOMAIN_AFFECT_LIVE, + "persistent": libvirt.VIR_DOMAIN_AFFECT_CONFIG, + "current": libvirt.VIR_DOMAIN_AFFECT_CURRENT, + "all": libvirt.VIR_DOMAIN_AFFECT_CONFIG + + libvirt.VIR_DOMAIN_AFFECT_LIVE if dom.isActive() and + dom.isPersistent() else libvirt.VIR_DOMAIN_AFFECT_CURRENT} + + return flag[mode] + + +def set_vm_metadata_element(dom, meta_xml, mode="all"): + element = etree.fromstring(meta_xml) + # From libvirt doc, Passing None for @metadata says to remove that element + # from the domain XML (passing the empty string leaves the element present) + # Do not support remove the old metadata + dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, + "kimchi", KIMCHI_META_URL + element.tag, + flags=get_vm_config_flag(dom, mode)) + + +def get_vm_metadata_element(dom, element, mode="current"): + try: + return dom.metadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, + KIMCHI_META_URL + element, + flags=get_vm_config_flag(dom, mode)) + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_METADATA: + return "" + else: + raise OperationFailed("KCHVM0030E", {'name': dom.name(), + 'err': e.message}) -- 1.9.0

Reviewed-by: Royce Lv<lvroyce@linux.vnet.ibm.com> On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Libvirt support two API to set and get domain xml metadata. These two function wrap them. Put them in model/utils.py, so vm_storage and vm_iface can make use of them.
For set domain xml metadata, we set live and config xml by default. For get domain xml metadata, we get the current xml by default.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/utils.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index a1af1f7..e28f689 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -87,6 +87,7 @@ messages = { "KCHVM0027E": _("User(s) '%(users)s' do not exist"), "KCHVM0028E": _("Group(s) '%(groups)s' do not exist"), "KCHVM0029E": _("Unable to shutdown virtual machine %(name)s. Details: %(err)s"), + "KCHVM0030E": _("Unable to get access metadata of virtual machine %(name)s. Details: %(err)s"),
"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/model/utils.py b/src/kimchi/model/utils.py index 80e1801..be5e526 100644 --- a/src/kimchi/model/utils.py +++ b/src/kimchi/model/utils.py @@ -18,6 +18,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from kimchi.exception import OperationFailed +import libvirt +from lxml import etree + + +KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi/metadata/"
def get_vm_name(vm_name, t_name, name_list): @@ -28,3 +33,39 @@ def get_vm_name(vm_name, t_name, name_list): if vm_name not in name_list: return vm_name raise OperationFailed("KCHUTILS0003E") + + +def get_vm_config_flag(dom, mode="persistent"): + # libvirt.VIR_DOMAIN_AFFECT_CURRENT is 0 + # VIR_DOMAIN_AFFECT_LIVE is 1, VIR_DOMAIN_AFFECT_CONFIG is 2 + flag = {"live": libvirt.VIR_DOMAIN_AFFECT_LIVE, + "persistent": libvirt.VIR_DOMAIN_AFFECT_CONFIG, + "current": libvirt.VIR_DOMAIN_AFFECT_CURRENT, + "all": libvirt.VIR_DOMAIN_AFFECT_CONFIG + + libvirt.VIR_DOMAIN_AFFECT_LIVE if dom.isActive() and + dom.isPersistent() else libvirt.VIR_DOMAIN_AFFECT_CURRENT} + + return flag[mode] + + +def set_vm_metadata_element(dom, meta_xml, mode="all"): + element = etree.fromstring(meta_xml) + # From libvirt doc, Passing None for @metadata says to remove that element + # from the domain XML (passing the empty string leaves the element present) + # Do not support remove the old metadata + dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, + "kimchi", KIMCHI_META_URL + element.tag, + flags=get_vm_config_flag(dom, mode)) + + +def get_vm_metadata_element(dom, element, mode="current"): + try: + return dom.metadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, + KIMCHI_META_URL + element, + flags=get_vm_config_flag(dom, mode)) + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_METADATA: + return "" + else: + raise OperationFailed("KCHVM0030E", {'name': dom.name(), + 'err': e.message})

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> define a domain just edit the persistent xml. So we should get user and group from persistent xml instead of live xml when domain is living. Refacor the code by libvirt metadata API and fix the bug. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/vms.py | 84 ++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 8a79f0f..7077900 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -35,6 +35,8 @@ from kimchi.exception import NotFoundError, OperationFailed from kimchi.model.config import CapabilitiesModel from kimchi.model.templates import TemplateModel from kimchi.model.utils import get_vm_name +from kimchi.model.utils import get_vm_metadata_element +from kimchi.model.utils import set_vm_metadata_element from kimchi.screenshot import VMScreenshot from kimchi.utils import import_class, kimchi_log, run_setfacl_set_attr from kimchi.utils import template_name_from_uri @@ -255,40 +257,50 @@ class VMModel(object): for group in groups: access.append(E.group(group)) - return E.metadata(E.kimchi(access)) + return access + + def _vm_updata_access_metadata(self, dom, params): + users = groups = None + if "users" in params: + users = params["users"] + invalid_users = set(users) - set(self.users.get_list()) + if len(invalid_users) != 0: + raise InvalidParameter("KCHVM0027E", + {'users': ", ".join(invalid_users)}) + if "groups" in params: + groups = params["groups"] + invalid_groups = set(groups) - set(self.groups.get_list()) + if len(invalid_groups) != 0: + raise InvalidParameter("KCHVM0028E", + {'groups': ", ".join(invalid_groups)}) + + if users is None and groups is None: + return + + access_xml = (get_vm_metadata_element(dom, "access") or + """<access></access>""") + old_users = xpath_get_text(access_xml, "/access/user") + old_groups = xpath_get_text(access_xml, "/access/group") + users = old_users if users is None else users + groups = old_groups if groups is None else groups + + xml = self._get_metadata_node(users, groups) + set_vm_metadata_element(dom, ET.tostring(xml)) 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 == 'users': - invalid_users = set(val) - set(self.users.get_list()) - if len(invalid_users) != 0: - raise InvalidParameter("KCHVM0027E", - {'users': ", ".join(invalid_users)}) - users = val - elif key == 'groups': - invalid_groups = set(val) - set(self.groups.get_list()) - if len(invalid_groups) != 0: - raise InvalidParameter("KCHVM0028E", - {'groups': - ", ".join(invalid_groups)}) - groups = val - else: - if key in VM_STATIC_UPDATE_PARAMS: - if key == 'memory': - # Libvirt saves memory in KiB. Retrieved xml has memory - # in KiB too, so new valeu must be in KiB here - val = val * 1024 - if type(val) == int: - val = str(val) - xpath = VM_STATIC_UPDATE_PARAMS[key] - new_xml = xmlutils.xml_item_update(new_xml, xpath, val) + if key in VM_STATIC_UPDATE_PARAMS: + if key == 'memory': + # Libvirt saves memory in KiB. Retrieved xml has memory + # in KiB too, so new valeu must be in KiB here + val = val * 1024 + if type(val) == int: + val = str(val) + xpath = VM_STATIC_UPDATE_PARAMS[key] + new_xml = xmlutils.xml_item_update(new_xml, xpath, val) conn = self.conn.get() try: @@ -302,13 +314,6 @@ class VMModel(object): root = ET.fromstring(new_xml) root.remove(root.find('.currentMemory')) - # Update metadata element - 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, encoding="utf-8")) except libvirt.libvirtError as e: dom = conn.defineXML(old_xml) @@ -317,7 +322,7 @@ class VMModel(object): return dom def _live_vm_update(self, dom, params): - pass + self._vm_updata_access_metadata(dom, params) def _has_video(self, dom): dom = ElementTree.fromstring(dom.XMLDesc(0)) @@ -356,9 +361,10 @@ class VMModel(object): res['io_throughput'] = vm_stats.get('disk_io', 0) res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100) - xml = dom.XMLDesc(0) - users = xpath_get_text(xml, "/domain/metadata/kimchi/access/user") - groups = xpath_get_text(xml, "/domain/metadata/kimchi/access/group") + access_xml = (get_vm_metadata_element(dom, "access") or + """<access></access>""") + users = xpath_get_text(access_xml, "/access/user") + groups = xpath_get_text(access_xml, "/access/group") return {'name': name, 'state': state, -- 1.9.0

Reviewed-by: Royce Lv<lvroyce@linux.vnet.ibm.com> On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
define a domain just edit the persistent xml. So we should get user and group from persistent xml instead of live xml when domain is living. Refacor the code by libvirt metadata API and fix the bug.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/vms.py | 84 ++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 39 deletions(-)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 8a79f0f..7077900 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -35,6 +35,8 @@ from kimchi.exception import NotFoundError, OperationFailed from kimchi.model.config import CapabilitiesModel from kimchi.model.templates import TemplateModel from kimchi.model.utils import get_vm_name +from kimchi.model.utils import get_vm_metadata_element +from kimchi.model.utils import set_vm_metadata_element from kimchi.screenshot import VMScreenshot from kimchi.utils import import_class, kimchi_log, run_setfacl_set_attr from kimchi.utils import template_name_from_uri @@ -255,40 +257,50 @@ class VMModel(object): for group in groups: access.append(E.group(group))
- return E.metadata(E.kimchi(access)) + return access + + def _vm_updata_access_metadata(self, dom, params): + users = groups = None + if "users" in params: + users = params["users"] + invalid_users = set(users) - set(self.users.get_list()) + if len(invalid_users) != 0: + raise InvalidParameter("KCHVM0027E", + {'users': ", ".join(invalid_users)}) + if "groups" in params: + groups = params["groups"] + invalid_groups = set(groups) - set(self.groups.get_list()) + if len(invalid_groups) != 0: + raise InvalidParameter("KCHVM0028E", + {'groups': ", ".join(invalid_groups)}) + + if users is None and groups is None: + return + + access_xml = (get_vm_metadata_element(dom, "access") or + """<access></access>""") + old_users = xpath_get_text(access_xml, "/access/user") + old_groups = xpath_get_text(access_xml, "/access/group") + users = old_users if users is None else users + groups = old_groups if groups is None else groups + + xml = self._get_metadata_node(users, groups) + set_vm_metadata_element(dom, ET.tostring(xml))
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 == 'users': - invalid_users = set(val) - set(self.users.get_list()) - if len(invalid_users) != 0: - raise InvalidParameter("KCHVM0027E", - {'users': ", ".join(invalid_users)}) - users = val - elif key == 'groups': - invalid_groups = set(val) - set(self.groups.get_list()) - if len(invalid_groups) != 0: - raise InvalidParameter("KCHVM0028E", - {'groups': - ", ".join(invalid_groups)}) - groups = val - else: - if key in VM_STATIC_UPDATE_PARAMS: - if key == 'memory': - # Libvirt saves memory in KiB. Retrieved xml has memory - # in KiB too, so new valeu must be in KiB here - val = val * 1024 - if type(val) == int: - val = str(val) - xpath = VM_STATIC_UPDATE_PARAMS[key] - new_xml = xmlutils.xml_item_update(new_xml, xpath, val) + if key in VM_STATIC_UPDATE_PARAMS: + if key == 'memory': + # Libvirt saves memory in KiB. Retrieved xml has memory + # in KiB too, so new valeu must be in KiB here + val = val * 1024 + if type(val) == int: + val = str(val) + xpath = VM_STATIC_UPDATE_PARAMS[key] + new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
conn = self.conn.get() try: @@ -302,13 +314,6 @@ class VMModel(object):
root = ET.fromstring(new_xml) root.remove(root.find('.currentMemory')) - # Update metadata element - 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, encoding="utf-8")) except libvirt.libvirtError as e: dom = conn.defineXML(old_xml) @@ -317,7 +322,7 @@ class VMModel(object): return dom
def _live_vm_update(self, dom, params): - pass + self._vm_updata_access_metadata(dom, params)
def _has_video(self, dom): dom = ElementTree.fromstring(dom.XMLDesc(0)) @@ -356,9 +361,10 @@ class VMModel(object): res['io_throughput'] = vm_stats.get('disk_io', 0) res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
- xml = dom.XMLDesc(0) - users = xpath_get_text(xml, "/domain/metadata/kimchi/access/user") - groups = xpath_get_text(xml, "/domain/metadata/kimchi/access/group") + access_xml = (get_vm_metadata_element(dom, "access") or + """<access></access>""") + users = xpath_get_text(access_xml, "/access/user") + groups = xpath_get_text(access_xml, "/access/group")
return {'name': name, 'state': state,

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> When vm is live, we should also get user and group correctly. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_model.py b/tests/test_model.py index b17ce00..ab22012 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -603,6 +603,19 @@ class ModelTests(unittest.TestCase): self.assertRaises(InvalidParameter, inst.vm_update, 'kimchi-vm1', params) + # change VM users and groups, when wm is running. + inst.vm_update(u'kimchi-vm1', + {'users': ['root'], 'groups': ['root']}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals(['root'], vm_info['users']) + self.assertEquals(['root'], vm_info['groups']) + # change VM users and groups by removing all elements, + # when wm is running. + inst.vm_update(u'kimchi-vm1', {'users': [], 'groups': []}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals([], vm_info['users']) + self.assertEquals([], vm_info['groups']) + inst.vm_poweroff('kimchi-vm1') self.assertRaises(OperationFailed, inst.vm_update, 'kimchi-vm1', {'name': 'kimchi-vm2'}) -- 1.9.0

Reviewed-by: Royce Lv<lvroyce@linux.vnet.ibm.com> On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
When vm is live, we should also get user and group correctly.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/tests/test_model.py b/tests/test_model.py index b17ce00..ab22012 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -603,6 +603,19 @@ class ModelTests(unittest.TestCase): self.assertRaises(InvalidParameter, inst.vm_update, 'kimchi-vm1', params)
+ # change VM users and groups, when wm is running. + inst.vm_update(u'kimchi-vm1', + {'users': ['root'], 'groups': ['root']}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals(['root'], vm_info['users']) + self.assertEquals(['root'], vm_info['groups']) + # change VM users and groups by removing all elements, + # when wm is running. + inst.vm_update(u'kimchi-vm1', {'users': [], 'groups': []}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals([], vm_info['users']) + self.assertEquals([], vm_info['groups']) + inst.vm_poweroff('kimchi-vm1') self.assertRaises(OperationFailed, inst.vm_update, 'kimchi-vm1', {'name': 'kimchi-vm2'})

Test cases fails when metadata added: royce@royce-ThinkPad-T420:~/lvroyce/kimchi/kimchi/tests$ sudo ./run_tests.sh test_model.ModelTests.test_use_test_host Loading AptUpdate features. libvirt: error : this function is not supported by the connection driver: virDomainSetMetadata libvirt: error : this function is not supported by the connection driver: virDomainGetMetadata no_domain_meta error 80 royce 3 E ====================================================================== ERROR: test_use_test_host (test_model.ModelTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_model.py", line 1008, in test_use_test_host self.assertTrue('kimchi-vm' in vms) File "/home/royce/lvroyce/kimchi/kimchi/src/kimchi/rollbackcontext.py", line 53, in __exit__ undo(*args, **kwargs) File "/home/royce/lvroyce/kimchi/kimchi/src/kimchi/model/vms.py", line 425, in delete info = self.lookup(name) File "/home/royce/lvroyce/kimchi/kimchi/src/kimchi/model/vms.py", line 382, in lookup access_xml = (get_vm_metadata_element(dom, "access") or File "/home/royce/lvroyce/kimchi/kimchi/src/kimchi/model/utils.py", line 104, in get_vm_metadata_element raise e libvirtError: this function is not supported by the connection driver: virDomainGetMetadata ---------------------------------------------------------------------- Ran 1 test in 2.092s FAILED (errors=1) This is because test:///default does not support virDomainGetMetadata at my version. We need to change this to be qemu:///system or we ignore this error when lookup vm information. On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
When vm is live, we should also get user and group correctly.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/test_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/tests/test_model.py b/tests/test_model.py index b17ce00..ab22012 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -603,6 +603,19 @@ class ModelTests(unittest.TestCase): self.assertRaises(InvalidParameter, inst.vm_update, 'kimchi-vm1', params)
+ # change VM users and groups, when wm is running. + inst.vm_update(u'kimchi-vm1', + {'users': ['root'], 'groups': ['root']}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals(['root'], vm_info['users']) + self.assertEquals(['root'], vm_info['groups']) + # change VM users and groups by removing all elements, + # when wm is running. + inst.vm_update(u'kimchi-vm1', {'users': [], 'groups': []}) + vm_info = inst.vm_lookup(u'kimchi-vm1') + self.assertEquals([], vm_info['users']) + self.assertEquals([], vm_info['groups']) + inst.vm_poweroff('kimchi-vm1') self.assertRaises(OperationFailed, inst.vm_update, 'kimchi-vm1', {'name': 'kimchi-vm2'})

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Then kimchi can choose the divice mode depend on these info. Note, the info is the initial OS when install OS on a blank guest. OS may change after the install. So other device should still keep their model info in the vm metadata. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/vms.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 7077900..1957c3a 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -223,6 +223,8 @@ class VMsModel(object): raise OperationFailed("KCHVM0007E", {'name': name, 'err': e.get_error_message()}) + VMModel.vm_updata_os_metadata(VMModel.get_vm(name, self.conn), t.info) + return name def get_list(self): @@ -287,6 +289,22 @@ class VMModel(object): xml = self._get_metadata_node(users, groups) set_vm_metadata_element(dom, ET.tostring(xml)) + @staticmethod + def vm_get_os_metadata(dom): + os_xml = (get_vm_metadata_element(dom, "OS") or + """<OS></OS>""") + OS = ET.fromstring(os_xml) + return (OS.attrib.get("version"), OS.attrib.get("distro")) + + @staticmethod + def vm_updata_os_metadata(dom, params): + distro = params.get("os_distro") + version = params.get("os_version") + if distro is None: + return + OS = E.os({"distro": distro, "version": version}) + set_vm_metadata_element(dom, ET.tostring(OS)) + def _static_vm_update(self, dom, params): state = DOM_STATE_MAP[dom.info()[0]] old_xml = new_xml = dom.XMLDesc(0) -- 1.9.0

On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Then kimchi can choose the divice mode depend on these info.
Note, the info is the initial OS when install OS on a blank guest.
OS may change after the install.
So other device should still keep their model info in the vm metadata.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/vms.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 7077900..1957c3a 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -223,6 +223,8 @@ class VMsModel(object): raise OperationFailed("KCHVM0007E", {'name': name, 'err': e.get_error_message()})
+ VMModel.vm_updata_os_metadata(VMModel.get_vm(name, self.conn), t.info) + return name
def get_list(self): @@ -287,6 +289,22 @@ class VMModel(object): xml = self._get_metadata_node(users, groups) set_vm_metadata_element(dom, ET.tostring(xml))
+ @staticmethod + def vm_get_os_metadata(dom): I feel a little bit wired to take a param of 'dom' for vm_get_os_metadata. According to me, our model api takes argument such as 'name', and hide libvirt details inside, but param 'dom' expose libvirt detail, I kind of think our model api should not take argument like this. + os_xml = (get_vm_metadata_element(dom, "OS") or + """<OS></OS>""") + OS = ET.fromstring(os_xml) + return (OS.attrib.get("version"), OS.attrib.get("distro")) + + @staticmethod + def vm_updata_os_metadata(dom, params): + distro = params.get("os_distro") + version = params.get("os_version") + if distro is None: + return + OS = E.os({"distro": distro, "version": version}) + set_vm_metadata_element(dom, ET.tostring(OS)) + def _static_vm_update(self, dom, params): state = DOM_STATE_MAP[dom.info()[0]] old_xml = new_xml = dom.XMLDesc(0)

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> virDomain.metadata and virDomain.setMetadata does not work in all livirt versions used in the supported distros. So if libvirt do not support these two API. let kimchi manually manage the metadata element Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/utils.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/kimchi/model/utils.py b/src/kimchi/model/utils.py index be5e526..574ce9d 100644 --- a/src/kimchi/model/utils.py +++ b/src/kimchi/model/utils.py @@ -20,6 +20,7 @@ from kimchi.exception import OperationFailed import libvirt from lxml import etree +from lxml.builder import E KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi/metadata/" @@ -53,9 +54,30 @@ def set_vm_metadata_element(dom, meta_xml, mode="all"): # From libvirt doc, Passing None for @metadata says to remove that element # from the domain XML (passing the empty string leaves the element present) # Do not support remove the old metadata - dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, - "kimchi", KIMCHI_META_URL + element.tag, - flags=get_vm_config_flag(dom, mode)) + try: + dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, + "kimchi", KIMCHI_META_URL + element.tag, + flags=get_vm_config_flag(dom, mode)) + except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports setMetadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, raise exception. + if not dom.isPersistent(): + msg = 'The VM has not a persistent configuration' + raise OperationFailed("KCHVM0029E", + {'name': dom.name(), "err": msg}) + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + metadata = root.find("metadata") + if metadata is None: + metadata = E.metadata() + root.append(metadata) + old_elem = metadata.find(element.tag) + (metadata.replace(old_elem, element) if old_elem is not None + else metadata.append(element)) + + dom.connect().defineXML(etree.tostring(root)) def get_vm_metadata_element(dom, element, mode="current"): @@ -64,6 +86,16 @@ def get_vm_metadata_element(dom, element, mode="current"): KIMCHI_META_URL + element, flags=get_vm_config_flag(dom, mode)) except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports metadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, just return empty + if not dom.isPersistent(): + return "" + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + elements = root.xpath("metadata/%s" % element) + return etree.tostring(elements[0]) if elements else "" if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_METADATA: return "" else: -- 1.9.0

Would you ignore or redirect the below error: 127.0.0.1 - - [24/Apr/2014:15:26:47] "GET /data/screenshots/233e8d1b-81a1-41af-97ec-2d3f2a886618-7bc8d298-a91c-4319-81a2-96f257a4d672.png HTTP/1.0" 200 62801 "http://localhost:8000/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0" libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element I got this all of my log. On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
virDomain.metadata and virDomain.setMetadata does not work in all livirt versions used in the supported distros.
So if libvirt do not support these two API.
let kimchi manually manage the metadata element
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/utils.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-)
diff --git a/src/kimchi/model/utils.py b/src/kimchi/model/utils.py index be5e526..574ce9d 100644 --- a/src/kimchi/model/utils.py +++ b/src/kimchi/model/utils.py @@ -20,6 +20,7 @@ from kimchi.exception import OperationFailed import libvirt from lxml import etree +from lxml.builder import E
KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi/metadata/" @@ -53,9 +54,30 @@ def set_vm_metadata_element(dom, meta_xml, mode="all"): # From libvirt doc, Passing None for @metadata says to remove that element # from the domain XML (passing the empty string leaves the element present) # Do not support remove the old metadata - dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, - "kimchi", KIMCHI_META_URL + element.tag, - flags=get_vm_config_flag(dom, mode)) + try: + dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, + "kimchi", KIMCHI_META_URL + element.tag, + flags=get_vm_config_flag(dom, mode)) + except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports setMetadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, raise exception. + if not dom.isPersistent(): + msg = 'The VM has not a persistent configuration' + raise OperationFailed("KCHVM0029E", + {'name': dom.name(), "err": msg}) + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + metadata = root.find("metadata") + if metadata is None: + metadata = E.metadata() + root.append(metadata) + old_elem = metadata.find(element.tag) + (metadata.replace(old_elem, element) if old_elem is not None + else metadata.append(element)) + + dom.connect().defineXML(etree.tostring(root))
def get_vm_metadata_element(dom, element, mode="current"): @@ -64,6 +86,16 @@ def get_vm_metadata_element(dom, element, mode="current"): KIMCHI_META_URL + element, flags=get_vm_config_flag(dom, mode)) except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports metadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, just return empty + if not dom.isPersistent(): + return "" + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + elements = root.xpath("metadata/%s" % element) + return etree.tostring(elements[0]) if elements else "" if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_METADATA: return "" else:

On 04/24/2014 03:28 PM, Royce Lv wrote:
Would you ignore or redirect the below error: let me improve it.
127.0.0.1 - - [24/Apr/2014:15:26:47] "GET /data/screenshots/233e8d1b-81a1-41af-97ec-2d3f2a886618-7bc8d298-a91c-4319-81a2-96f257a4d672.png HTTP/1.0" 200 62801 "http://localhost:8000/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0" libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element libvirt: QEMU Driver error : argument unsupported: QEMU driver does not support <metadata> element
I got this all of my log. On 2014年04月24日 13:16, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
virDomain.metadata and virDomain.setMetadata does not work in all livirt versions used in the supported distros.
So if libvirt do not support these two API.
let kimchi manually manage the metadata element
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/model/utils.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-)
diff --git a/src/kimchi/model/utils.py b/src/kimchi/model/utils.py index be5e526..574ce9d 100644 --- a/src/kimchi/model/utils.py +++ b/src/kimchi/model/utils.py @@ -20,6 +20,7 @@ from kimchi.exception import OperationFailed import libvirt from lxml import etree +from lxml.builder import E
KIMCHI_META_URL = "https://github.com/kimchi-project/kimchi/metadata/" @@ -53,9 +54,30 @@ def set_vm_metadata_element(dom, meta_xml, mode="all"): # From libvirt doc, Passing None for @metadata says to remove that element # from the domain XML (passing the empty string leaves the element present) # Do not support remove the old metadata - dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, - "kimchi", KIMCHI_META_URL + element.tag, - flags=get_vm_config_flag(dom, mode)) + try: + dom.setMetadata(libvirt.VIR_DOMAIN_METADATA_ELEMENT, meta_xml, + "kimchi", KIMCHI_META_URL + element.tag, + flags=get_vm_config_flag(dom, mode)) + except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports setMetadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, raise exception. + if not dom.isPersistent(): + msg = 'The VM has not a persistent configuration' + raise OperationFailed("KCHVM0029E", + {'name': dom.name(), "err": msg}) + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + metadata = root.find("metadata") + if metadata is None: + metadata = E.metadata() + root.append(metadata) + old_elem = metadata.find(element.tag) + (metadata.replace(old_elem, element) if old_elem is not None + else metadata.append(element)) + + dom.connect().defineXML(etree.tostring(root))
def get_vm_metadata_element(dom, element, mode="current"): @@ -64,6 +86,16 @@ def get_vm_metadata_element(dom, element, mode="current"): KIMCHI_META_URL + element, flags=get_vm_config_flag(dom, mode)) except libvirt.libvirtError as e: + # FIXME remove this code when all distro libvirt supports metadata + if e.get_error_code() == libvirt.VIR_ERR_ARGUMENT_UNSUPPORTED: + # some other tools will not let libvirt create a persistent + # configuration, just return empty + if not dom.isPersistent(): + return "" + xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE) + root = etree.fromstring(xml) + elements = root.xpath("metadata/%s" % element) + return etree.tostring(elements[0]) if elements else "" if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN_METADATA: return "" else:
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center
participants (3)
-
Royce Lv
-
shaohef@linux.vnet.ibm.com
-
Sheldon