[Kimchi-devel] [PATCH 3/4] Return users and groups when fetching VM info

Rodrigo Trujillo rodrigo.trujillo at linux.vnet.ibm.com
Thu Feb 27 14:37:14 UTC 2014


I was wondering whether would be better to store the user id in vm xml 
instead of the user name (login).
Although it is possible to change both by the system admin, the login 
seems to be more likely to change.

More comments below.

On 02/26/2014 03:09 PM, Crístian Viana wrote:
> In order to have more control over what users can use a virtual machine
> under Kimchi, a VM should have users and groups associated with it.
>
> Kimchi uses metadata information
> (http://libvirt.org/formatdomain.html#elementsMetadata) to store users
> and groups to the virtual machines. The data is stored as string
> representations of Python lists.
> Keep in mind that these security restrictions will not apply when using
> other virtual machine managers (e.g. virsh, virt-manager, etc).
>
> Users and groups stored in the VM are now returned when the user queries the
> VM data. Currently, there is no way to associate users and groups to a VM.
>
> The REST command below now returns the users and groups of every
> virtual machine:
>
> /vms/:name GET: It now lists the users and groups associated with that VM.
>
> {
>    "users":[
>      "user1",
>      "user2"
>    ],
>    "groups":[
>      "group1",
>      "group2"
>    ],
> ...
> }
>
> If there is no metadata information about users and groups, empty lists
> will be displayed
>
> Signed-off-by: Crístian Viana <vianac at linux.vnet.ibm.com>
> ---
>   docs/API.md               |  4 ++++
>   src/kimchi/API.json       | 22 ++++++++++++++++++++++
>   src/kimchi/control/vms.py |  4 +++-
>   src/kimchi/i18n.py        |  4 ++++
>   src/kimchi/mockmodel.py   |  4 +++-
>   src/kimchi/model/vms.py   | 32 +++++++++++++++++++++++++++++++-
>   tests/test_mockmodel.py   |  2 +-
>   tests/test_model.py       |  4 +++-
>   tests/test_rest.py        |  2 ++
>   9 files changed, 73 insertions(+), 5 deletions(-)
>
> diff --git a/docs/API.md b/docs/API.md
> index e97eace..e79a38b 100644
> --- a/docs/API.md
> +++ b/docs/API.md
> @@ -96,6 +96,10 @@ the following general conventions:
>           * port: The real port number of the graphics, vnc or spice. Users
>                   can use this port to connect to the vm with general vnc/spice
>                   clients.
> +    * users: A list of system users who have permission to access the VM.
> +      Default is: empty.
> +    * groups: A list of system groups whose users have permission to access
> +      the VM. Default is: empty.
>   * **DELETE**: Remove the Virtual Machine
>   * **PUT**: update the parameters of existed VM
>       * name: New name for this VM (only applied for shutoff VM)
> diff --git a/src/kimchi/API.json b/src/kimchi/API.json
> index f595bbf..9d19351 100644
> --- a/src/kimchi/API.json
> +++ b/src/kimchi/API.json
> @@ -183,6 +183,28 @@
>                       "type": "string",
>                       "minLength": 1,
>                       "error": "KCHVM0011E"
> +                },
> +                "users": {
> +                    "description": "Array of users who have permission to the VM",
> +                    "type": "array",
> +                    "uniqueItems": true,
> +                    "error": "KCHVM0020E",
> +                    "items": {
> +                        "description": "User name",
> +                        "type": "string",
> +                        "error": "KCHVM0021E"
> +                    }
> +                },
> +                "groups": {
> +                    "description": "Array of groups who have permission to the VM",
> +                    "type": "array",
> +                    "uniqueItems": true,
> +                    "error": "KCHVM0022E",
> +                    "items": {
> +                        "description": "Group name",
> +                        "type": "string",
> +                        "error": "KCHVM0023E"
> +                    }
>                   }
>               }
>           },
> diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
> index a74ce27..89e9f45 100644
> --- a/src/kimchi/control/vms.py
> +++ b/src/kimchi/control/vms.py
> @@ -58,7 +58,9 @@ class VM(Resource):
>                   'icon': self.info['icon'],
>                   'graphics': {'type': self.info['graphics']['type'],
>                                'listen': self.info['graphics']['listen'],
> -                             'port': self.info['graphics']['port']}
> +                             'port': self.info['graphics']['port']},
> +                'users': self.info['users'],
> +                'groups': self.info['groups']
>                   }
>   
>   
> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
> index ca72a80..179b38b 100644
> --- a/src/kimchi/i18n.py
> +++ b/src/kimchi/i18n.py
> @@ -77,6 +77,10 @@ messages = {
>       "KCHVM0017E": _("Volume list (LUNs names) not given."),
>       "KCHVM0018E": _("Virtual machine volumes must be a list of strings with distinct LUNs names."),
>       "KCHVM0019E": _("Unable to start virtual machine %(name)s. Details: %(err)s"),
> +    "KCHVM0020E": _("User names list must be an array"),
> +    "KCHVM0021E": _("User name must be a string"),
> +    "KCHVM0022E": _("Group names list must be an array"),
> +    "KCHVM0023E": _("Group name must be a string"),
>   
>       "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 b23a024..5cdbc01 100644
> --- a/src/kimchi/mockmodel.py
> +++ b/src/kimchi/mockmodel.py
> @@ -937,7 +937,9 @@ class MockVM(object):
>                        'cpus': template_info['cpus'],
>                        'icon': None,
>                        'graphics': {'type': 'vnc', 'listen': '0.0.0.0',
> -                                  'port': None}
> +                                  'port': None},
> +                     'users': ['user1', 'user2', 'root'],
> +                     'groups': ['group1', 'group2', 'admin']
>                        }
>           self.info['graphics'].update(template_info['graphics'])
>   
> diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
> index b6a42e6..dcb352c 100644
> --- a/src/kimchi/model/vms.py
> +++ b/src/kimchi/model/vms.py
> @@ -20,6 +20,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 ast
>   import os
>   import time
>   import uuid
> @@ -27,6 +28,7 @@ from xml.etree import ElementTree
>   
>   import libvirt
>   from cherrypy.process.plugins import BackgroundTask
> +from lxml import etree
>   
>   from kimchi import vnc
>   from kimchi import xmlutils
> @@ -53,6 +55,17 @@ VM_LIVE_UPDATE_PARAMS = {}
>   
>   stats = {}
>   
> +KIMCHI_NS_TAG = 'kimchi'
> +KIMCHI_NS_URI = 'http://github.com/kimchi-project/kimchi'
> +
> +
> +def _get_vm_metadata(xml, tag):
> +    et = etree.fromstring(xml)
> +    val = et.xpath("/domain/metadata/%s:access/%s:%s"
> +                   % (KIMCHI_NS_TAG, KIMCHI_NS_TAG, tag),
> +                   namespaces={KIMCHI_NS_TAG: KIMCHI_NS_URI})
> +    return ast.literal_eval(val[0].text) if val else []
> +
>   
>   class VMsModel(object):
>       def __init__(self, **kargs):
> @@ -305,6 +318,21 @@ 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)
> +        try:
> +            users = _get_vm_metadata(xml, "users")
> +        except:
You could log the error here. I think its good for debugging and general 
information.
> +            # Wrong XML tree structure, wrong array syntax, or empty value:
> +            #     ignore error and build an empty list
> +            users = []
> +
> +        try:
> +            groups = _get_vm_metadata(xml, "groups")
> +        except:
Same here
> +            # Wrong XML tree structure, wrong array syntax, or empty value:
> +            #     ignore error and build an empty list
> +            groups = []
> +
>           return {'state': state,
>                   'stats': res,
>                   'uuid': dom.UUIDString(),
> @@ -314,7 +342,9 @@ class VMModel(object):
>                   'icon': icon,
>                   'graphics': {"type": graphics_type,
>                                "listen": graphics_listen,
> -                             "port": graphics_port}
> +                             "port": graphics_port},
> +                'users': users,
> +                'groups': groups
>                   }
>   
>       def _vm_get_disk_paths(self, dom):
> diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py
> index ab5eb59..863c740 100644
> --- a/tests/test_mockmodel.py
> +++ b/tests/test_mockmodel.py
> @@ -138,7 +138,7 @@ class MockModelTests(unittest.TestCase):
>           self.assertEquals(u'test', vms[0])
>   
>           keys = set(('state', 'stats', 'uuid', 'memory', 'cpus', 'screenshot',
> -                    'icon', 'graphics'))
> +                    'icon', 'graphics', 'users', 'groups'))
>           stats_keys = set(('cpu_utilization',
>                             'net_throughput', 'net_throughput_peak',
>                             'io_throughput', 'io_throughput_peak'))
> diff --git a/tests/test_model.py b/tests/test_model.py
> index 74e2424..474da57 100644
> --- a/tests/test_model.py
> +++ b/tests/test_model.py
> @@ -58,7 +58,7 @@ class ModelTests(unittest.TestCase):
>           self.assertEquals('test', vms[0])
>   
>           keys = set(('state', 'stats', 'uuid', 'memory', 'cpus', 'screenshot',
> -                    'icon', 'graphics'))
> +                    'icon', 'graphics', 'users', 'groups'))
>           stats_keys = set(('cpu_utilization',
>                             'net_throughput', 'net_throughput_peak',
>                             'io_throughput', 'io_throughput_peak'))
> @@ -70,6 +70,8 @@ class ModelTests(unittest.TestCase):
>           self.assertEquals(None, info['icon'])
>           self.assertEquals(stats_keys, set(info['stats'].keys()))
>           self.assertRaises(NotFoundError, inst.vm_lookup, 'nosuchvm')
> +        self.assertEquals([], info['users'])
> +        self.assertEquals([], info['groups'])
>   
>       @unittest.skipUnless(utils.running_as_root(), 'Must be run as root')
>       def test_vm_lifecycle(self):
> diff --git a/tests/test_rest.py b/tests/test_rest.py
> index ca96dc0..f8a87e6 100644
> --- a/tests/test_rest.py
> +++ b/tests/test_rest.py
> @@ -182,6 +182,8 @@ class RestTests(unittest.TestCase):
>           vm = json.loads(self.request('/vms/vm-1').read())
>           self.assertEquals('vm-1', vm['name'])
>           self.assertEquals('shutoff', vm['state'])
> +        self.assertEquals(['user1', 'user2', 'root'], vm['users'])
> +        self.assertEquals(['group1', 'group2', 'admin'], vm['groups'])
>   
>       def test_edit_vm(self):
>           req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'})




More information about the Kimchi-devel mailing list