[PATCH V2 0/4] improve controller

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V1 -> V2 fix typo. remove print use url_auth instead of _url_sub_node_auth improve the commit description add a method to load root sub collections/resouces automatically ShaoHe Feng (4): improve controller: add a method to load root sub collections/resouces automatically improve controller: tag the collections/resouces of root with @urlSubNode improve controller: Root loads collections/resouces automatically improve controller: set authentication automatically src/kimchi/control/__init__.py | 8 ++++++++ src/kimchi/control/config.py | 2 ++ src/kimchi/control/debugreports.py | 2 ++ src/kimchi/control/host.py | 2 ++ src/kimchi/control/interfaces.py | 2 ++ src/kimchi/control/networks.py | 2 ++ src/kimchi/control/plugins.py | 2 ++ src/kimchi/control/storagepools.py | 2 ++ src/kimchi/control/tasks.py | 2 ++ src/kimchi/control/templates.py | 2 ++ src/kimchi/control/vms.py | 2 ++ src/kimchi/root.py | 26 +++++-------------------- src/kimchi/server.py | 12 +++++------- src/kimchi/utils.py | 39 ++++++++++++++++++++++++++++++++++++++ 14 files changed, 77 insertions(+), 28 deletions(-) -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> load_url_sub_node can load collections/resouces automatically when they are tagged with @urlSubNode Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/utils.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py index 331da91..89eaba2 100644 --- a/src/kimchi/utils.py +++ b/src/kimchi/utils.py @@ -22,6 +22,7 @@ # import cherrypy +import imp import os import subprocess import urllib2 @@ -154,3 +155,41 @@ def run_command(cmd, timeout=None): finally: if timer and not timeout_flag[0]: timer.cancel() + + +class urlSubNode(object): + def __init__(self, name, auth=False): + self.name = name + self.auth = auth + + def __call__(self, fun): + fun._url_sub_node_name = {"name": self.name} + fun.url_auth = self.auth + return fun + + +def listPathModules(path): + modules = set() + for f in os.listdir(path): + base, ext = os.path.splitext(f) + if ext in ('.py', '.pyc', '.pyo'): + modules.add(base) + return sorted(modules) + + +def load_url_sub_node(path, expect_attr="_url_sub_node_name"): + sub_nodes = {} + + for mod_name in listPathModules(path): + if mod_name.startswith("_"): + continue + mod_fobj, mod_absp, mod_desc = imp.find_module(mod_name, [path]) + module = imp.load_module(mod_name, mod_fobj, + mod_absp, mod_desc) + for node in [getattr(module, x) for x in dir(module)]: + if not hasattr(node, expect_attr): + continue + name = getattr(node, expect_attr)["name"] + sub_nodes.update({name: node}) + + return sub_nodes -- 1.8.4.2

Am 21-01-2014 12:03, schrieb shaohef@linux.vnet.ibm.com:
@@ -154,3 +155,41 @@ def run_command(cmd, timeout=None): finally: if timer and not timeout_flag[0]: timer.cancel() + + +class urlSubNode(object): According to the PEP8, class names should normally use the CapWords convention. Please use something like "UrlSubNode".

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> we can difine a /host as follow: @urlSubNode("host", True) class Host(Resource): def __init__(self, model, id=None): super(Host, self).__init__(model, id) @urlSubNode("host", True) means: the URL "/host" is root.Host, and it need authentication Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/control/__init__.py | 8 ++++++++ src/kimchi/control/config.py | 2 ++ src/kimchi/control/debugreports.py | 2 ++ src/kimchi/control/host.py | 2 ++ src/kimchi/control/interfaces.py | 2 ++ src/kimchi/control/networks.py | 2 ++ src/kimchi/control/plugins.py | 2 ++ src/kimchi/control/storagepools.py | 2 ++ src/kimchi/control/tasks.py | 2 ++ src/kimchi/control/templates.py | 2 ++ src/kimchi/control/vms.py | 2 ++ 11 files changed, 28 insertions(+) diff --git a/src/kimchi/control/__init__.py b/src/kimchi/control/__init__.py index 8a37cc4..3d64099 100644 --- a/src/kimchi/control/__init__.py +++ b/src/kimchi/control/__init__.py @@ -19,3 +19,11 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os + + +from kimchi.utils import load_url_sub_node + + +sub_nodes = load_url_sub_node(os.path.dirname(__file__)) diff --git a/src/kimchi/control/config.py b/src/kimchi/control/config.py index 603f51d..9813b8d 100644 --- a/src/kimchi/control/config.py +++ b/src/kimchi/control/config.py @@ -27,8 +27,10 @@ import cherrypy from kimchi.config import config from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("config") class Config(Resource): def __init__(self, model, id=None): super(Config, self).__init__(model, id) diff --git a/src/kimchi/control/debugreports.py b/src/kimchi/control/debugreports.py index a55ba38..7c594ae 100644 --- a/src/kimchi/control/debugreports.py +++ b/src/kimchi/control/debugreports.py @@ -23,8 +23,10 @@ from kimchi.control.base import AsyncCollection, Resource from kimchi.control.utils import internal_redirect +from kimchi.utils import urlSubNode +@urlSubNode("debugreports", True) class DebugReports(AsyncCollection): def __init__(self, model): super(DebugReports, self).__init__(model) diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py index 9b19577..2fc64be 100644 --- a/src/kimchi/control/host.py +++ b/src/kimchi/control/host.py @@ -24,8 +24,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("host", True) class Host(Resource): def __init__(self, model, id=None): super(Host, self).__init__(model, id) diff --git a/src/kimchi/control/interfaces.py b/src/kimchi/control/interfaces.py index 28be26e..e6a1e77 100644 --- a/src/kimchi/control/interfaces.py +++ b/src/kimchi/control/interfaces.py @@ -23,8 +23,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("interfaces") class Interfaces(Collection): def __init__(self, model): super(Interfaces, self).__init__(model) diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py index f3f0b41..ac1aaed 100644 --- a/src/kimchi/control/networks.py +++ b/src/kimchi/control/networks.py @@ -22,8 +22,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("networks", True) class Networks(Collection): def __init__(self, model): super(Networks, self).__init__(model) diff --git a/src/kimchi/control/plugins.py b/src/kimchi/control/plugins.py index af32709..ff448bd 100644 --- a/src/kimchi/control/plugins.py +++ b/src/kimchi/control/plugins.py @@ -25,8 +25,10 @@ import kimchi.template from kimchi.control.base import Collection, Resource from kimchi.control.utils import get_class_name, model_fn +from kimchi.utils import urlSubNode +@urlSubNode("plugins") class Plugins(Collection): def __init__(self, model): super(Plugins, self).__init__(model) diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py index 782f5a6..88d2aa3 100644 --- a/src/kimchi/control/storagepools.py +++ b/src/kimchi/control/storagepools.py @@ -31,8 +31,10 @@ from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes from kimchi.control.utils import get_class_name, model_fn, parse_request from kimchi.control.utils import validate_params from kimchi.model import ISO_POOL_NAME +from kimchi.utils import urlSubNode +@urlSubNode("storagepools", True) class StoragePools(Collection): def __init__(self, model): super(StoragePools, self).__init__(model) diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py index b799422..869cecf 100644 --- a/src/kimchi/control/tasks.py +++ b/src/kimchi/control/tasks.py @@ -22,8 +22,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("tasks", True) class Tasks(Collection): def __init__(self, model): super(Tasks, self).__init__(model) diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py index a77936e..8f59152 100644 --- a/src/kimchi/control/templates.py +++ b/src/kimchi/control/templates.py @@ -22,8 +22,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode +@urlSubNode("templates", True) class Templates(Collection): def __init__(self, model): super(Templates, self).__init__(model) diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py index 7843be7..25474fc 100644 --- a/src/kimchi/control/vms.py +++ b/src/kimchi/control/vms.py @@ -24,8 +24,10 @@ from kimchi.control.base import Collection, Resource from kimchi.control.utils import internal_redirect +from kimchi.utils import urlSubNode +@urlSubNode("vms", True) class VMs(Collection): def __init__(self, model): super(VMs, self).__init__(model) -- 1.8.4.2

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 21-01-2014 12:03, schrieb shaohef@linux.vnet.ibm.com:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
we can difine a /host as follow:
@urlSubNode("host", True) class Host(Resource): def __init__(self, model, id=None): super(Host, self).__init__(model, id)
@urlSubNode("host", True) means: the URL "/host" is root.Host, and it need authentication
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com>

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Remove hard code from root.py. We do not need to touch root.py any more when we add a new sub collection/resouce. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/root.py | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/kimchi/root.py b/src/kimchi/root.py index 3cc6321..83a777c 100644 --- a/src/kimchi/root.py +++ b/src/kimchi/root.py @@ -28,23 +28,15 @@ import json from kimchi import auth from kimchi import template from kimchi.config import get_api_schema_file +from kimchi.control import sub_nodes from kimchi.control.base import Resource -from kimchi.control.config import Config -from kimchi.control.debugreports import DebugReports -from kimchi.control.host import Host -from kimchi.control.interfaces import Interfaces -from kimchi.control.networks import Networks -from kimchi.control.plugins import Plugins -from kimchi.control.storagepools import StoragePools -from kimchi.control.tasks import Tasks -from kimchi.control.templates import Templates from kimchi.control.utils import parse_request -from kimchi.control.vms import VMs from kimchi.exception import OperationFailed class Root(Resource): def __init__(self, model, dev_env): + super(Root, self).__init__(model) self._handled_error = ['error_page.400', 'error_page.404', 'error_page.405', 'error_page.406', 'error_page.415', 'error_page.500'] @@ -56,17 +48,9 @@ class Root(Resource): self._cp_config = dict([(key, self.error_development_handler) for key in self._handled_error]) - Resource.__init__(self, model) - self.vms = VMs(model) - self.templates = Templates(model) - self.storagepools = StoragePools(model) - self.interfaces = Interfaces(model) - self.networks = Networks(model) - self.tasks = Tasks(model) - self.config = Config(model) - self.host = Host(model) - self.debugreports = DebugReports(model) - self.plugins = Plugins(model) + for ident, node in sub_nodes.items(): + setattr(self, ident, node(model)) + self.api_schema = json.load(open(get_api_schema_file())) def error_production_handler(self, status, message, traceback, version): -- 1.8.4.2

Reviewed-by: Crístian Viana <vianac@linux.vnet.ibm.com> Am 21-01-2014 12:03, schrieb shaohef@linux.vnet.ibm.com:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Remove hard code from root.py. We do not need to touch root.py any more when we add a new sub collection/resouce.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com>

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Remove hard code from server.py. We do not need to touch root.py any more when we add a new sub collection/resouce. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/server.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/kimchi/server.py b/src/kimchi/server.py index b820263..6acf2f4 100644 --- a/src/kimchi/server.py +++ b/src/kimchi/server.py @@ -35,6 +35,7 @@ from kimchi import mockmodel from kimchi import vnc from kimchi.root import Root from kimchi.utils import get_enabled_plugins, import_class +from kimchi.control import sub_nodes LOGGING_LEVEL = {"debug": logging.DEBUG, @@ -74,13 +75,6 @@ class Server(object): 'tools.sessions.storage_type': 'file', 'tools.sessions.storage_path': config.get_session_path(), 'tools.kimchiauth.on': False}, - '/host': {'tools.kimchiauth.on': True}, - '/vms': {'tools.kimchiauth.on': True}, - '/templates': {'tools.kimchiauth.on': True}, - '/networks': {'tools.kimchiauth.on': True}, - '/storagepools': {'tools.kimchiauth.on': True}, - '/tasks': {'tools.kimchiauth.on': True}, - '/debugreports': {'tools.kimchiauth.on': True}, '/css': { 'tools.staticdir.on': True, 'tools.staticdir.dir': 'ui/css', @@ -193,6 +187,10 @@ class Server(object): vnc_ws_proxy = vnc.new_ws_proxy() cherrypy.engine.subscribe('exit', vnc_ws_proxy.kill) + for ident, node in sub_nodes.items(): + if node.url_auth: + self.configObj["/%s" % ident] = {'tools.kimchiauth.on': True} + self.app = cherrypy.tree.mount(Root(model_instance, dev_env), config=self.configObj) self._load_plugins() -- 1.8.4.2

Am 21-01-2014 12:03, schrieb shaohef@linux.vnet.ibm.com:
@@ -35,6 +35,7 @@ from kimchi import mockmodel from kimchi import vnc from kimchi.root import Root from kimchi.utils import get_enabled_plugins, import_class +from kimchi.control import sub_nodes Please add the import statement in alphabetical order. "kimchi.control" comes before "kimchi.root".

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> V1 -> V2 use the "mac" to identify the iface and remove "name" parameter $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' \ http://localhost:800/vms/test-vm-8/ifaces/ [ { "mac":"52:54:00:00:00:08", "model":"virtio", "type":"network", "network":"default", "name":"52:54:00:00:00:08" } ] $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' \ http://localhost:800/vms/test-vm-8/ifaces/52:54:00:00:00:08 { "mac":"52:54:00:00:00:08", "model":"virtio", "type":"network", "network":"default", "name":"52:54:00:00:00:08" } ShaoHe Feng (6): Add a control.vm module VM supports interfaces: update API VM supports interfaces: update model VM supports interfaces: update controller VM supports interfaces: update mockmodel VM supports interfaces: update testcase Makefile.am | 1 + configure.ac | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + docs/API.md | 46 +++++++++++++++++++++++++++++++++++++ src/kimchi/control/Makefile.am | 2 ++ src/kimchi/control/vm/Makefile.am | 32 ++++++++++++++++++++++++++ src/kimchi/control/vm/__init__.py | 30 ++++++++++++++++++++++++ src/kimchi/control/vm/ifaces.py | 48 +++++++++++++++++++++++++++++++++++++++ src/kimchi/control/vms.py | 3 +++ src/kimchi/mockmodel.py | 30 ++++++++++++++++++++++++ src/kimchi/model.py | 36 +++++++++++++++++++++++++++++ tests/test_model.py | 19 ++++++++++++++++ tests/test_rest.py | 31 +++++++++++++++++++++++++ 14 files changed, 281 insertions(+) create mode 100644 src/kimchi/control/vm/Makefile.am create mode 100644 src/kimchi/control/vm/__init__.py create mode 100644 src/kimchi/control/vm/ifaces.py -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> All vm devices should be in this module Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- Makefile.am | 1 + configure.ac | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/control/Makefile.am | 2 ++ src/kimchi/control/vm/Makefile.am | 32 ++++++++++++++++++++++++++++++++ src/kimchi/control/vm/__init__.py | 30 ++++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+) create mode 100644 src/kimchi/control/vm/Makefile.am create mode 100644 src/kimchi/control/vm/__init__.py diff --git a/Makefile.am b/Makefile.am index 9ccac0e..68cec3b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,7 @@ PEP8_WHITELIST = \ src/kimchi/cachebust.py \ src/kimchi/config.py.in \ src/kimchi/control/*.py \ + src/kimchi/control/vm/*.py \ src/kimchi/disks.py \ src/kimchi/distroloader.py \ src/kimchi/exception.py \ diff --git a/configure.ac b/configure.ac index aaed9de..27c8840 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,7 @@ AC_CONFIG_FILES([ src/distros.d/Makefile src/kimchi/Makefile src/kimchi/control/Makefile + src/kimchi/control/vm/Makefile plugins/Makefile plugins/sample/Makefile plugins/sample/ui/Makefile diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 77c64e9..982ee91 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -138,6 +138,7 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/kimchid %{python_sitelib}/kimchi/*.py* %{python_sitelib}/kimchi/control/*.py* +%{python_sitelib}/kimchi/control/vm/*.py* %{python_sitelib}/kimchi/API.json %{_datadir}/kimchi/doc/API.md %{_datadir}/kimchi/doc/README.md diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 889b704..ea86aca 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -60,6 +60,7 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/kimchid %{python_sitelib}/kimchi/*.py* %{python_sitelib}/kimchi/control/*.py* +%{python_sitelib}/kimchi/control/vm/*.py* %{python_sitelib}/kimchi/API.json %{_datadir}/kimchi/doc/API.md %{_datadir}/kimchi/doc/README.md diff --git a/src/kimchi/control/Makefile.am b/src/kimchi/control/Makefile.am index 925c593..42f62c3 100644 --- a/src/kimchi/control/Makefile.am +++ b/src/kimchi/control/Makefile.am @@ -20,6 +20,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +SUBDIRS = vm + control_PYTHON = *.py controldir = $(pythondir)/kimchi/control diff --git a/src/kimchi/control/vm/Makefile.am b/src/kimchi/control/vm/Makefile.am new file mode 100644 index 0000000..17340af --- /dev/null +++ b/src/kimchi/control/vm/Makefile.am @@ -0,0 +1,32 @@ +# +# Kimchi +# +# Copyright IBM Corp, 2013 +# +# Authors: +# ShaoHe Feng <shaohef@linux.vnet.ibm.com> +# Royce Lv <lvroyce@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +vm_PYTHON = \ + __init__.py + + +vmdir = $(pythondir)/kimchi/control/vm + +install-data-local: + $(MKDIR_P) $(DESTDIR)$(vmdir) diff --git a/src/kimchi/control/vm/__init__.py b/src/kimchi/control/vm/__init__.py new file mode 100644 index 0000000..e30d450 --- /dev/null +++ b/src/kimchi/control/vm/__init__.py @@ -0,0 +1,30 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# ShaoHe Feng <shaohef@linux.vnet.ibm.com> +# Royce Lv <lvroyce@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os + + +from kimchi.utils import load_url_sub_node + + +sub_nodes = load_url_sub_node(os.path.dirname(__file__)) -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Now kimchi supports network get/create/delete/activate/deactivate REST API. We need to support the attach/detach/ a Network to a VM. https://github.com/kimchi-project/kimchi/wiki/customize-VM#organization-of-s... And get the info of Network attached to a VM. more libvirt network interfaces info: http://libvirt.org/formatdomain.html#elementsNICS Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- docs/API.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/docs/API.md b/docs/API.md index f872eab..c727cf1 100644 --- a/docs/API.md +++ b/docs/API.md @@ -149,6 +149,52 @@ Represents a snapshot of the Virtual Machine's primary monitor. * null: Graphics is disabled or type not supported * listen: The network which the vnc/spice server listens on. +### Sub-Collection: Virtual Machine Network Interfaces + +**URI:** /vms/*:name*/ifaces + +Represents all network interfaces attached to a Virtual Machine. + +**Methods:** + +* **GET**: Retrieve a summarized list of all network interfaces attached to a Virtual Machine. + +* **POST**: attach a network interface to VM + * model *(optional)*: model of emulated network interface card. It can be one of these models: + ne2k_pci, i82551, i82557b, i82559er, rtl8139, e1000, pcnet and virtio. + When model is missing, libvirt will set 'rtl8139' as default value. + * network *(optional)*: the name of resource network, it is required when the + interface type is network. + * type: The type of VM network interface that libvirt supports. + Now kimchi just supports 'network' type. + +### Sub-Resource: Virtual Machine Network Interface + +**URI:** /vms/*:name*/ifaces/*:mac* + +A interface represents available network interface on VM. + +**Methods:** + +* **GET**: Retrieve the full description of the VM network interface + * bridge *(optional)*: the name of resource bridge, only be available when the + interface type is bridge. + * mac: Media Access Control Address of the VM interface. + * model: model of emulated network interface card. It will be one of these models: + ne2k_pci, i82551, i82557b, i82559er, rtl8139, e1000, pcnet and virtio. + * network *(optional)*: the name of resource network, only be available when the + interface type is network. + * type: The type of VM network interface that libvirt supports. + It will be one of these types: 'network', 'bridge', 'user','ethernet', + 'direct', 'hostdev', 'mcast', 'server' and 'client'. + +* **DELETE**: detach the network interface from VM + +**Actions (POST):** + +*No actions defined* + + ### Resource: Template **URI:** /templates/*:name* -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Support vm ifaces collection GET method $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:800/vms/vm-name/ifaces/ Support vm iface resource GET method $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:800/vms/vm-name/ifaces/mac Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/model.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/kimchi/model.py b/src/kimchi/model.py index 2c6d3a1..7e129b7 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -45,6 +45,7 @@ import uuid from cherrypy.process.plugins import BackgroundTask from cherrypy.process.plugins import SimplePlugin from collections import defaultdict +from lxml import objectify from xml.etree import ElementTree @@ -967,6 +968,41 @@ class Model(object): iface.destroy() iface.undefine() + def _get_vmifaces(self, vm): + dom = self._get_vm(vm) + xml = dom.XMLDesc(0) + root = objectify.fromstring(xml) + + return root.devices.findall("interface") + + def _get_vmiface(self, vm, mac): + ifaces = self._get_vmifaces(vm) + + for iface in ifaces: + if iface.mac.get('address') == mac: + return iface + return None + + def vmifaces_get_list(self, vm): + return [iface.mac.get('address') for iface in self._get_vmifaces(vm)] + + def vmiface_lookup(self, vm, mac): + info = {} + + iface = self._get_vmiface(vm, mac) + if not iface: + raise NotFoundError('iface: "%s"' % mac) + + info['type'] = iface.attrib['type'] + info['mac'] = iface.mac.get('address') + info['model'] = iface.model.get('type') + if info['type'] == 'network': + info['network'] = iface.source.get('network') + if info['type'] == 'bridge': + info['bridge'] = iface.source.get('bridge') + + return info + def add_task(self, target_uri, fn, opaque=None): id = self.next_taskid self.next_taskid = self.next_taskid + 1 -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> add VmIfaces collection and VmIface resource Update VM resource. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/control/vm/ifaces.py | 48 +++++++++++++++++++++++++++++++++++++++++ src/kimchi/control/vms.py | 3 +++ 2 files changed, 51 insertions(+) create mode 100644 src/kimchi/control/vm/ifaces.py diff --git a/src/kimchi/control/vm/ifaces.py b/src/kimchi/control/vm/ifaces.py new file mode 100644 index 0000000..a333616 --- /dev/null +++ b/src/kimchi/control/vm/ifaces.py @@ -0,0 +1,48 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2013 +# +# Authors: +# ShaoHe Feng <shaohef@linux.vnet.ibm.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from kimchi.control.base import Collection, Resource +from kimchi.utils import urlSubNode + + +@urlSubNode("ifaces") +class VMIfaces(Collection): + def __init__(self, model, vm): + super(VMIfaces, self).__init__(model) + self.resource = VMIface + self.vm = vm + self.resource_args = [self.vm, ] + self.model_args = [self.vm, ] + + +class VMIface(Resource): + def __init__(self, model, vm, ident): + super(VMIface, self).__init__(model, ident) + self.vm = vm + self.ident = ident + self.info = {} + self.model_args = [self.vm, self.ident] + self.uri_fmt = '/vms/%s/iface/%s' + + @property + def data(self): + return self.info diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py index 25474fc..70ab826 100644 --- a/src/kimchi/control/vms.py +++ b/src/kimchi/control/vms.py @@ -24,6 +24,7 @@ from kimchi.control.base import Collection, Resource from kimchi.control.utils import internal_redirect +from kimchi.control.vm import sub_nodes from kimchi.utils import urlSubNode @@ -40,6 +41,8 @@ class VM(Resource): self.update_params = ["name"] self.screenshot = VMScreenShot(model, ident) self.uri_fmt = '/vms/%s' + for ident, node in sub_nodes.items(): + setattr(self, ident, node(model, self.ident.decode("utf-8"))) self.start = self.generate_action_handler('start') self.stop = self.generate_action_handler('stop') self.connect = self.generate_action_handler('connect') -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Support vm ifaces collection GET method $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:800/vms/vm-name/ifaces/ Support vm iface resource GET method $ curl -u user -H 'Accept: application/json' -H 'Content-type: application/json' http://localhost:800/vms/vm-name/ifaces/mac Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/kimchi/mockmodel.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 4ef3fa6..20cc0a7 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -488,6 +488,16 @@ class MockModel(object): def networks_get_list(self): return sorted(self._mock_networks.keys()) + def vmifaces_get_list(self, vm): + dom = self._get_vm(vm) + macs = dom.ifaces.keys() + return macs + + def vmiface_lookup(self, vm, mac): + dom = self._get_vm(vm) + info = dom.ifaces[mac].info + return info + def tasks_get_list(self): with self.objstore as session: return session.get_list('task') @@ -626,12 +636,32 @@ class MockVMTemplate(VMTemplate): return disk_paths +class MockVMIface(object): + counter = 0 + + def __init__(self, network=None): + self.__class__.counter += 1 + self.info = {'type': 'network', + 'model': 'virtio', + 'network': network if network else "net-%s" % self.counter, + 'mac': self.get_mac() + } + + @classmethod + def get_mac(cls): + mac = ":".join(["52", "54"] + ["%02x" % (cls.counter/(256**i) % 256) + for i in range(3, -1, -1)]) + return mac + + class MockVM(object): def __init__(self, uuid, name, template_info): self.uuid = uuid self.name = name self.disk_paths = [] self.networks = template_info['networks'] + ifaces = [MockVMIface(net) for net in self.networks] + self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces]) self.info = {'state': 'shutoff', 'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \ 'net_throughput_peak': 100, 'io_throughput': 45, \ -- 1.8.4.2

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> update test_rest and test_model Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- tests/test_model.py | 19 +++++++++++++++++++ tests/test_rest.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/test_model.py b/tests/test_model.py index 8077f4d..c6c5d0e 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -121,6 +121,25 @@ class ModelTests(unittest.TestCase): inst.template_delete('test') @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') + def test_vm_ifaces(self): + inst = kimchi.model.Model(objstore_loc=self.tmp_store) + with RollbackContext() as rollback: + params = {'name': 'test', 'disks': []} + inst.templates_create(params) + rollback.prependDefer(inst.template_delete, 'test') + params = {'name': 'kimchi-ifaces', 'template': '/templates/test'} + inst.vms_create(params) + rollback.prependDefer(inst.vm_delete, 'kimchi-ifaces') + + ifaces = inst.vmifaces_get_list('kimchi-ifaces') + self.assertEquals(1, len(ifaces)) + + iface = inst.vmiface_lookup('kimchi-ifaces', ifaces[0]) + self.assertEquals(17, len(iface['mac'])) + self.assertEquals("default", iface['network']) + self.assertIn("model", iface) + + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_vm_storage_provisioning(self): inst = kimchi.model.Model(objstore_loc=self.tmp_store) diff --git a/tests/test_rest.py b/tests/test_rest.py index a8e5842..5543cb5 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -340,6 +340,37 @@ class RestTests(unittest.TestCase): resp = self.request('/templates/test', '{}', 'DELETE') self.assertEquals(204, resp.status) + def test_vm_iface(self): + + with RollbackContext() as rollback: + # Create a template as a base for our VMs + req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso'}) + resp = self.request('/templates', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the template + rollback.prependDefer(self.request, + '/templates/test', '{}', 'DELETE') + + # Create a VM with default args + req = json.dumps({'name': 'test-vm', + 'template': '/templates/test'}) + resp = self.request('/vms', req, 'POST') + self.assertEquals(201, resp.status) + # Delete the VM + rollback.prependDefer(self.request, + '/vms/test-vm', '{}', 'DELETE') + + ifaces = json.loads(self.request('/vms/test-vm/ifaces').read()) + self.assertEquals(1, len(ifaces)) + + for iface in ifaces: + res = json.loads(self.request('/vms/test-vm/ifaces/%s' % + iface['mac']).read()) + self.assertEquals('default', res['network']) + self.assertEquals(17, len(res['mac'])) + self.assertEquals('virtio', res['model']) + + def test_vm_customise_storage(self): # Create a Template req = json.dumps({'name': 'test', 'cdrom': '/nonexistent.iso', -- 1.8.4.2
participants (2)
-
Crístian Viana
-
shaohef@linux.vnet.ibm.com