[PATCH 0/5] Bug fixes and network tests

Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py -- 2.1.0

Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index e3a051c..e8693da 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -223,6 +223,7 @@ messages = { "KCHNET0007E": _("Interface should be bare NIC, bonding or bridge device."), "KCHNET0008E": _("Unable to create network %(name)s. Details: %(err)s"), "KCHNET0009E": _("Unable to find a free IP address for network '%(name)s'"), + "KCHNET0010E": _("The interface %(iface)s already exist."), "KCHNET0011E": _("Network name must be a string"), "KCHNET0012E": _("Supported network types are isolated, NAT and bridge"), "KCHNET0013E": _("Network subnet must be a string with IP address and prefix or netmask"), diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 1e94fd2..44f1297 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -211,9 +211,7 @@ class NetworksModel(object): conn = self.conn.get() if br_name in [net.bridgeName() for net in conn.listAllNetworks()]: - error_msg = 'The interface %s already exist' % br_name - raise InvalidOperation("KCHNET0010E", {'iface': br_name, - 'err': error_msg}) + raise InvalidOperation("KCHNET0010E", {'iface': br_name}) with RollbackContext() as rollback: try: -- 2.1.0

When trying to delete a VLAN tagging bridged network I got the following error because of the VLAN bridge was not active. [26/Dec/2014:16:28:42] HTTP Traceback (most recent call last): File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 158, in index 'PUT': self.update}[method]() File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 135, in delete fn(*self.model_args) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 327, in delete self._remove_vlan_tagged_bridge(network) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 372, in _remove_vlan_tagged_bridge iface.destroy(0) File "/home/alinefm/kimchi/src/kimchi/model/libvirtconnection.py", line 66, in wrapper ret = f(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/libvirt.py", line 2894, in destroy if ret == -1: raise libvirtError ('virInterfaceDestroy() failed', net=self) libvirtError: Requested operation is not valid: interface is not running Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 44f1297..dad31c7 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -369,5 +369,5 @@ class NetworkModel(object): if bridge.startswith(KIMCHI_BRIDGE_PREFIX): conn = self.conn.get() iface = conn.interfaceLookupByName(bridge) - iface.destroy(0) + iface.isActive() and iface.destroy(0) iface.undefine() -- 2.1.0

On 12/29/2014 09:53 AM, Aline Manera wrote:
When trying to delete a VLAN tagging bridged network I got the following error because of the VLAN bridge was not active.
[26/Dec/2014:16:28:42] HTTP Traceback (most recent call last): File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 158, in index 'PUT': self.update}[method]() File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 135, in delete fn(*self.model_args) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 327, in delete self._remove_vlan_tagged_bridge(network) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 372, in _remove_vlan_tagged_bridge iface.destroy(0) File "/home/alinefm/kimchi/src/kimchi/model/libvirtconnection.py", line 66, in wrapper ret = f(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/libvirt.py", line 2894, in destroy if ret == -1: raise libvirtError ('virInterfaceDestroy() failed', net=self) libvirtError: Requested operation is not valid: interface is not running I was not able to reproduce this, is this for a specific distro? virsh # iface-list --all Name State MAC Address
kbem1-4 inactive lo active 00:00:00:00:00:00 virsh # net-list Name State Autostart Persistent ---------------------------------------------------------- abc active yes yes default active yes yes virsh # net-dumpxml abc <network> <name>abc</name> <uuid>1dfcefe2-de3f-41e0-a74d-1c5769f1d05e</uuid> <forward mode='bridge'/> <bridge name='kbem1-4'/> </network> When delete the bridge: 9.115.122.223 - - [30/Dec/2014:03:01:37] "GET /networks HTTP/1.0" 200 564 "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0" 9.115.122.223 - - [30/Dec/2014:03:01:40] "GET /networks/abc HTTP/1.0" 200 221 "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0" 9.115.122.223 - - [30/Dec/2014:03:01:44] "DELETE /networks/abc HTTP/1.0" 204 - "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0"
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 44f1297..dad31c7 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -369,5 +369,5 @@ class NetworkModel(object): if bridge.startswith(KIMCHI_BRIDGE_PREFIX): conn = self.conn.get() iface = conn.interfaceLookupByName(bridge) - iface.destroy(0) + iface.isActive() and iface.destroy(0) iface.undefine()

I verified it on Ubuntu 14.10. You can try to verify it by running the last patch related to network tests. I identified the problem when creating them. On 30/12/2014 06:19, Royce Lv wrote:
On 12/29/2014 09:53 AM, Aline Manera wrote:
When trying to delete a VLAN tagging bridged network I got the following error because of the VLAN bridge was not active.
[26/Dec/2014:16:28:42] HTTP Traceback (most recent call last): File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 670, in respond response.body = self.handler() File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 217, in __call__ self.body = self.oldhandler(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 61, in __call__ return self.callable(*self.args, **self.kwargs) File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 158, in index 'PUT': self.update}[method]() File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 135, in delete fn(*self.model_args) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 327, in delete self._remove_vlan_tagged_bridge(network) File "/home/alinefm/kimchi/src/kimchi/model/networks.py", line 372, in _remove_vlan_tagged_bridge iface.destroy(0) File "/home/alinefm/kimchi/src/kimchi/model/libvirtconnection.py", line 66, in wrapper ret = f(*args, **kwargs) File "/usr/lib/python2.7/dist-packages/libvirt.py", line 2894, in destroy if ret == -1: raise libvirtError ('virInterfaceDestroy() failed', net=self) libvirtError: Requested operation is not valid: interface is not running I was not able to reproduce this, is this for a specific distro? virsh # iface-list --all Name State MAC Address
kbem1-4 inactive lo active 00:00:00:00:00:00
virsh # net-list Name State Autostart Persistent ---------------------------------------------------------- abc active yes yes default active yes yes
virsh # net-dumpxml abc <network> <name>abc</name> <uuid>1dfcefe2-de3f-41e0-a74d-1c5769f1d05e</uuid> <forward mode='bridge'/> <bridge name='kbem1-4'/> </network>
When delete the bridge: 9.115.122.223 - - [30/Dec/2014:03:01:37] "GET /networks HTTP/1.0" 200 564 "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0" 9.115.122.223 - - [30/Dec/2014:03:01:40] "GET /networks/abc HTTP/1.0" 200 221 "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0" 9.115.122.223 - - [30/Dec/2014:03:01:44] "DELETE /networks/abc HTTP/1.0" 204 - "https://9.115.122.184:8001/" "Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0"
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/model/networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 44f1297..dad31c7 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -369,5 +369,5 @@ class NetworkModel(object): if bridge.startswith(KIMCHI_BRIDGE_PREFIX): conn = self.conn.get() iface = conn.interfaceLookupByName(bridge) - iface.destroy(0) + iface.isActive() and iface.destroy(0) iface.undefine()
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/API.md b/docs/API.md index 210a036..94a1001 100644 --- a/docs/API.md +++ b/docs/API.md @@ -565,19 +565,18 @@ A interface represents available interface on host. * **GET**: Retrieve a summarized list of all defined Networks * **POST**: Create a new Network * name: The name of the Network - * subnet *(optional)*: Network segment in slash-separated format with ip address and - prefix or netmask. It is always ignored for bridge network. * connection: Specifies how this network should be connected to the other networks visible to this host. * isolated: Create a private, isolated virtual network. * nat: Outgoing traffic will be routed through the host. * bridge: All traffic on this network will be bridged through the indicated interface. - * interface: The name of a network interface on the host. + * subnet *(optional)*: Network segment in slash-separated format with ip address and + prefix or netmask used to create nat network. + * interface *(optional)*: The name of a network interface on the host. For bridge network, the interface can be a bridge or nic/bonding - device. For isolated or NAT network, the interface is ignored. - * in_use: True if network is in use by a template or virtual machine; - False, otherwise. + device. + * vlan_id *(optional)*: VLAN tagging ID for the bridge network. ### Resource: Network -- 2.1.0

On 12/29/2014 09:53 AM, Aline Manera wrote:
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 210a036..94a1001 100644 --- a/docs/API.md +++ b/docs/API.md @@ -565,19 +565,18 @@ A interface represents available interface on host. * **GET**: Retrieve a summarized list of all defined Networks * **POST**: Create a new Network * name: The name of the Network - * subnet *(optional)*: Network segment in slash-separated format with ip address and - prefix or netmask. It is always ignored for bridge network. * connection: Specifies how this network should be connected to the other networks visible to this host. * isolated: Create a private, isolated virtual network. * nat: Outgoing traffic will be routed through the host. * bridge: All traffic on this network will be bridged through the indicated interface. - * interface: The name of a network interface on the host. + * subnet *(optional)*: Network segment in slash-separated format with ip address and + prefix or netmask used to create nat network. + * interface *(optional)*: The name of a network interface on the host. For bridge network, the interface can be a bridge or nic/bonding - device. For isolated or NAT network, the interface is ignored. - * in_use: True if network is in use by a template or virtual machine; - False, otherwise. + device. I think this is the gap between GET response and the POST request, we include "vlan_id" in POST request, but no "vlan_id" in lookup response. and we have "in_use" in GET response but not this param in POST request. + * vlan_id *(optional)*: VLAN tagging ID for the bridge network.
### Resource: Network

On 30/12/2014 06:18, Royce Lv wrote:
On 12/29/2014 09:53 AM, Aline Manera wrote:
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- docs/API.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/docs/API.md b/docs/API.md index 210a036..94a1001 100644 --- a/docs/API.md +++ b/docs/API.md @@ -565,19 +565,18 @@ A interface represents available interface on host. * **GET**: Retrieve a summarized list of all defined Networks * **POST**: Create a new Network * name: The name of the Network - * subnet *(optional)*: Network segment in slash-separated format with ip address and - prefix or netmask. It is always ignored for bridge network. * connection: Specifies how this network should be connected to the other networks visible to this host. * isolated: Create a private, isolated virtual network. * nat: Outgoing traffic will be routed through the host. * bridge: All traffic on this network will be bridged through the indicated interface. - * interface: The name of a network interface on the host. + * subnet *(optional)*: Network segment in slash-separated format with ip address and + prefix or netmask used to create nat network. + * interface *(optional)*: The name of a network interface on the host. For bridge network, the interface can be a bridge or nic/bonding - device. For isolated or NAT network, the interface is ignored. - * in_use: True if network is in use by a template or virtual machine; - False, otherwise. + device. I think this is the gap between GET response and the POST request, we include "vlan_id" in POST request, but no "vlan_id" in lookup response. and we have "in_use" in GET response but not this param in POST request.
The parameters returned by GET response is not necessarily the same of the POST request. For example, when the network is created, it is not in use in the first time so there is no need to add it to POST.
+ * vlan_id *(optional)*: VLAN tagging ID for the bridge network.
### Resource: Network
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

The rollback_wrapper function is used to avoid a NotFoundError when the tests finishes correctly. Move it to a common place to allow reuse. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/test_model.py | 23 +++++++---------------- tests/utils.py | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/test_model.py b/tests/test_model.py index 5984906..a8d35dc 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -964,10 +964,10 @@ class ModelTests(unittest.TestCase): params_1 = {'name': 'kimchi-vm1', 'template': '/templates/test'} params_2 = {'name': 'kimchi-vm2', 'template': '/templates/test'} inst.vms_create(params_1) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm1') inst.vms_create(params_2) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm2') vms = inst.vms_get_list() @@ -978,7 +978,7 @@ class ModelTests(unittest.TestCase): {"graphics": {"passwd": "123456"}}) inst.vm_start('kimchi-vm1') - rollback.prependDefer(self._rollback_wrapper, inst.vm_poweroff, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, 'kimchi-vm1') vm_info = inst.vm_lookup(u'kimchi-vm1') @@ -1019,7 +1019,7 @@ class ModelTests(unittest.TestCase): params = {'name': u'пeω-∨м', 'cpus': 4, 'memory': 2048} inst.vm_update('kimchi-vm1', params) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'пeω-∨м') self.assertEquals(info['uuid'], inst.vm_lookup(u'пeω-∨м')['uuid']) info = inst.vm_lookup(u'пeω-∨м') @@ -1296,16 +1296,7 @@ class ModelTests(unittest.TestCase): inst.task_wait(taskid, timeout=10) self.assertEquals('finished', inst.task_lookup(taskid)['status']) - # This wrapper function is needed due to the new backend messaging in - # vm model. vm_poweroff and vm_delete raise exception if vm is not found. - # These functions are called after vm has been deleted if test finishes - # correctly, then NofFoundError exception is raised and rollback breaks - def _rollback_wrapper(self, func, vmname): - try: - func(vmname) - except NotFoundError: - # VM has been deleted already - return + @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): @@ -1318,11 +1309,11 @@ class ModelTests(unittest.TestCase): params = {'name': u'kīмсhī-∨м', 'template': u'/templates/test'} inst.vms_create(params) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'kīмсhī-∨м') inst.vm_start(u'kīмсhī-∨м') - rollback.prependDefer(self._rollback_wrapper, inst.vm_poweroff, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, u'kīмсhī-∨м') inst.vm_delete(u'kīмсhī-∨м') diff --git a/tests/utils.py b/tests/utils.py index c692041..3677851 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -38,7 +38,7 @@ import kimchi.mockmodel import kimchi.server from kimchi.config import config, paths from kimchi.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs -from kimchi.exception import OperationFailed +from kimchi.exception import NotFoundError, OperationFailed from kimchi.utils import kimchi_log _ports = {} @@ -226,3 +226,16 @@ def wait_task(task_lookup, taskid, timeout=10): return kimchi_log.error("Timeout while process long-run task, " "try to increase timeout value.") + + +# The action functions in model backend raise NotFoundError exception if the +# element is not found. But in some tests, these functions are called after +# the element has been deleted if test finishes correctly, then NofFoundError +# exception is raised and rollback breaks. To avoid it, this wrapper ignores +# the NotFoundError. +def rollback_wrapper(func, resource): + try: + func(resource) + except NotFoundError: + # VM has been deleted already + return -- 2.1.0

Reviewed-by: Royce Lv<lvroyce@linux.vnet.ibm.com> On 12/29/2014 09:53 AM, Aline Manera wrote:
The rollback_wrapper function is used to avoid a NotFoundError when the tests finishes correctly. Move it to a common place to allow reuse.
Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/test_model.py | 23 +++++++---------------- tests/utils.py | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/tests/test_model.py b/tests/test_model.py index 5984906..a8d35dc 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -964,10 +964,10 @@ class ModelTests(unittest.TestCase): params_1 = {'name': 'kimchi-vm1', 'template': '/templates/test'} params_2 = {'name': 'kimchi-vm2', 'template': '/templates/test'} inst.vms_create(params_1) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm1') inst.vms_create(params_2) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, 'kimchi-vm2')
vms = inst.vms_get_list() @@ -978,7 +978,7 @@ class ModelTests(unittest.TestCase): {"graphics": {"passwd": "123456"}})
inst.vm_start('kimchi-vm1') - rollback.prependDefer(self._rollback_wrapper, inst.vm_poweroff, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, 'kimchi-vm1')
vm_info = inst.vm_lookup(u'kimchi-vm1') @@ -1019,7 +1019,7 @@ class ModelTests(unittest.TestCase):
params = {'name': u'пeω-∨м', 'cpus': 4, 'memory': 2048} inst.vm_update('kimchi-vm1', params) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'пeω-∨м') self.assertEquals(info['uuid'], inst.vm_lookup(u'пeω-∨м')['uuid']) info = inst.vm_lookup(u'пeω-∨м') @@ -1296,16 +1296,7 @@ class ModelTests(unittest.TestCase): inst.task_wait(taskid, timeout=10) self.assertEquals('finished', inst.task_lookup(taskid)['status'])
- # This wrapper function is needed due to the new backend messaging in - # vm model. vm_poweroff and vm_delete raise exception if vm is not found. - # These functions are called after vm has been deleted if test finishes - # correctly, then NofFoundError exception is raised and rollback breaks - def _rollback_wrapper(self, func, vmname): - try: - func(vmname) - except NotFoundError: - # VM has been deleted already - return +
@unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): @@ -1318,11 +1309,11 @@ class ModelTests(unittest.TestCase):
params = {'name': u'kīмсhī-∨м', 'template': u'/templates/test'} inst.vms_create(params) - rollback.prependDefer(self._rollback_wrapper, inst.vm_delete, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_delete, u'kīмсhī-∨м')
inst.vm_start(u'kīмсhī-∨м') - rollback.prependDefer(self._rollback_wrapper, inst.vm_poweroff, + rollback.prependDefer(utils.rollback_wrapper, inst.vm_poweroff, u'kīмсhī-∨м')
inst.vm_delete(u'kīмсhī-∨м') diff --git a/tests/utils.py b/tests/utils.py index c692041..3677851 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -38,7 +38,7 @@ import kimchi.mockmodel import kimchi.server from kimchi.config import config, paths from kimchi.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs -from kimchi.exception import OperationFailed +from kimchi.exception import NotFoundError, OperationFailed from kimchi.utils import kimchi_log
_ports = {} @@ -226,3 +226,16 @@ def wait_task(task_lookup, taskid, timeout=10): return kimchi_log.error("Timeout while process long-run task, " "try to increase timeout value.") + + +# The action functions in model backend raise NotFoundError exception if the +# element is not found. But in some tests, these functions are called after +# the element has been deleted if test finishes correctly, then NofFoundError +# exception is raised and rollback breaks. To avoid it, this wrapper ignores +# the NotFoundError. +def rollback_wrapper(func, resource): + try: + func(resource) + except NotFoundError: + # VM has been deleted already + return

It creates a new file (test_network.py) with the network test cases. As MockModel does not override any Model function related to network, only Model tests were added using the REST API. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- tests/test_model.py | 65 ---------------------- tests/test_network.py | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ---------------------- 3 files changed, 147 insertions(+), 129 deletions(-) create mode 100644 tests/test_network.py diff --git a/tests/test_model.py b/tests/test_model.py index a8d35dc..7e28aed 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1064,69 +1064,6 @@ class ModelTests(unittest.TestCase): 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(None, self.tmp_store) - - with RollbackContext() as rollback: - - # Regression test: - # Kimchi fails creating new network #318 - name = 'test-network-no-subnet' - - networks = inst.networks_get_list() - num = len(networks) + 1 - args = {'name': name, - 'connection': 'nat', - 'subnet': ''} - inst.networks_create(args) - rollback.prependDefer(inst.network_delete, name) - - networks = inst.networks_get_list() - self.assertEquals(num, len(networks)) - networkinfo = inst.network_lookup(name) - self.assertNotEqual(args['subnet'], networkinfo['subnet']) - self.assertEqual(args['connection'], networkinfo['connection']) - self.assertEquals('inactive', networkinfo['state']) - self.assertEquals([], networkinfo['vms']) - self.assertTrue(networkinfo['autostart']) - self.assertTrue(networkinfo['persistent']) - - inst.network_activate(name) - rollback.prependDefer(inst.network_deactivate, name) - - networkinfo = inst.network_lookup(name) - self.assertEquals('active', networkinfo['state']) - - # test network creation with subnet passed - name = 'test-network-subnet' - - networks = inst.networks_get_list() - num = len(networks) + 1 - args = {'name': name, - 'connection': 'nat', - 'subnet': '127.0.100.0/24'} - inst.networks_create(args) - rollback.prependDefer(inst.network_delete, name) - - networks = inst.networks_get_list() - self.assertEquals(num, len(networks)) - networkinfo = inst.network_lookup(name) - self.assertEqual(args['subnet'], networkinfo['subnet']) - self.assertEqual(args['connection'], networkinfo['connection']) - self.assertEquals('inactive', networkinfo['state']) - self.assertEquals([], networkinfo['vms']) - self.assertTrue(networkinfo['autostart']) - - inst.network_activate(name) - rollback.prependDefer(inst.network_deactivate, name) - - networkinfo = inst.network_lookup(name) - self.assertEquals('active', networkinfo['state']) - - networks = inst.networks_get_list() - self.assertEquals((num - 2), len(networks)) - def test_multithreaded_connection(self): def worker(): for i in xrange(100): @@ -1296,8 +1233,6 @@ class ModelTests(unittest.TestCase): inst.task_wait(taskid, timeout=10) self.assertEquals('finished', inst.task_lookup(taskid)['status']) - - @unittest.skipUnless(utils.running_as_root(), 'Must be run as root') def test_delete_running_vm(self): inst = model.Model(objstore_loc=self.tmp_store) diff --git a/tests/test_network.py b/tests/test_network.py new file mode 100644 index 0000000..8949789 --- /dev/null +++ b/tests/test_network.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# 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 json +import os +import unittest + +from functools import partial + +from kimchi.model.model import Model +from kimchi.rollbackcontext import RollbackContext +from utils import get_free_port, patch_auth, request, rollback_wrapper +from utils import run_server + + +model = None +test_server = None +host = None +port = None +ssl_port = None +cherrypy_port = None + + +def setUpModule(): + global test_server, model, host, port, ssl_port, cherrypy_port + + patch_auth() + model = Model(None, '/tmp/obj-store-test') + host = '127.0.0.1' + port = get_free_port('http') + ssl_port = get_free_port('https') + cherrypy_port = get_free_port('cherrypy_port') + test_server = run_server(host, port, ssl_port, test_mode=True, + cherrypy_port=cherrypy_port, model=model) + + +def tearDownModule(): + test_server.stop() + os.unlink('/tmp/obj-store-test') + + +class NetworkTests(unittest.TestCase): + def setUp(self): + self.request = partial(request, host, ssl_port) + + def test_get_networks(self): + networks = json.loads(self.request('/networks').read()) + self.assertIn('default', [net['name'] for net in networks]) + + with RollbackContext() as rollback: + # Now add a couple of Networks to the mock model + for i in xrange(5): + name = 'network-%i' % i + req = json.dumps({'name': name, + 'connection': 'nat', + 'subnet': '127.0.10%i.0/24' % i}) + + resp = self.request('/networks', req, 'POST') + rollback.prependDefer(model.network_delete, name) + self.assertEquals(201, resp.status) + network = json.loads(resp.read()) + self.assertEquals([], network["vms"]) + + nets = json.loads(self.request('/networks').read()) + self.assertEquals(len(networks) + 5, len(nets)) + + network = json.loads(self.request('/networks/network-1').read()) + keys = [u'name', u'connection', u'interface', u'subnet', u'dhcp', + u'vms', u'in_use', u'autostart', u'state', u'persistent'] + self.assertEquals(sorted(keys), sorted(network.keys())) + + def test_network_lifecycle(self): + def _do_test(params): + with RollbackContext() as rollback: + net_name = params['name'] + uri = '/networks/%s' % net_name.encode('utf-8') + + # Create a network + req = json.dumps(params) + resp = self.request('/networks', req, 'POST') + rollback.prependDefer(rollback_wrapper, model.network_delete, + net_name) + self.assertEquals(201, resp.status) + + # Verify the network + resp = self.request(uri) + network = json.loads(resp.read()) + self.assertEquals('inactive', network['state']) + self.assertTrue(network['persistent']) + + # activate the network + resp = self.request(uri + '/activate', '{}', 'POST') + rollback.prependDefer(rollback_wrapper, + model.network_deactivate, net_name) + self.assertEquals(200, resp.status) + + resp = self.request(uri) + network = json.loads(resp.read()) + self.assertEquals('active', network['state']) + + # Deactivate the network + resp = self.request(uri + '/deactivate', '{}', 'POST') + self.assertEquals(200, resp.status) + + resp = self.request(uri) + network = json.loads(resp.read()) + self.assertEquals('inactive', network['state']) + + # Delete the network + resp = self.request(uri, '{}', 'DELETE') + self.assertEquals(204, resp.status) + + # Verify all the supported network type + networks = [{'name': u'kīмсhī-пet', 'connection': 'isolated'}, + {'name': u'nat-network', 'connection': 'nat'}, + {'name': u'subnet-network', 'connection': 'nat', + 'subnet': '127.0.100.0/24'}] + + # Verify the current system has at least one interface to create a + # bridged network + interfaces = json.loads(self.request('/interfaces').read()) + if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'vlan-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 999}) + + for net in networks: + _do_test(net) diff --git a/tests/test_rest.py b/tests/test_rest.py index fa15ef6..8eac0f9 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -1606,70 +1606,6 @@ class RestTests(unittest.TestCase): self.assertEquals(interface['netmask'], "255.255.255.0") self.assertEquals(interface['status'], "active") - def test_get_networks(self): - networks = json.loads(request(host, ssl_port, '/networks').read()) - self.assertEquals(1, len(networks)) - self.assertEquals('default', networks[0]['name']) - self.assertEquals([], networks[0]['vms']) - - # Now add a couple of Networks to the mock model - for i in xrange(5): - name = 'network-%i' % i - req = json.dumps({'name': name, - 'connection': 'nat', - 'subnet': '127.0.10%i.0/24' % i}) - - resp = request(host, ssl_port, '/networks', req, 'POST') - self.assertEquals(201, resp.status) - network = json.loads(resp.read()) - self.assertEquals([], network["vms"]) - - networks = json.loads(request(host, ssl_port, '/networks').read()) - self.assertEquals(6, len(networks)) - - network = json.loads(request(host, ssl_port, - '/networks/network-1').read()) - self.assertEquals('network-1', network['name']) - self.assertEquals('inactive', network['state']) - # Delete the network - for i in xrange(5): - resp = request(host, ssl_port, '/networks/network-%i' % i, - '{}', 'DELETE') - self.assertEquals(204, resp.status) - - def test_network_action(self): - # Create a network - req = json.dumps({'name': 'test-network', - 'connection': 'nat', - 'net': '127.0.1.0/24'}) - resp = request(host, ssl_port, '/networks', req, 'POST') - self.assertEquals(201, resp.status) - - # Verify the network - network = json.loads(request(host, ssl_port, - '/networks/test-network').read()) - self.assertEquals('inactive', network['state']) - self.assertTrue(network['persistent']) - - # activate the network - resp = request(host, ssl_port, - '/networks/test-network/activate', '{}', 'POST') - network = json.loads(request(host, ssl_port, - '/networks/test-network').read()) - self.assertEquals('active', network['state']) - - # Deactivate the network - resp = request(host, ssl_port, - '/networks/test-network/deactivate', '{}', 'POST') - network = json.loads(request(host, ssl_port, - '/networks/test-network').read()) - self.assertEquals('inactive', network['state']) - - # Delete the network - resp = request(host, ssl_port, '/networks/test-network', '{}', - 'DELETE') - self.assertEquals(204, resp.status) - def _task_lookup(self, taskid): return json.loads(self.request('/tasks/%s' % taskid).read()) -- 2.1.0

Running the ./run-tests.sh script with these patches in my machine (Lenovo T410 with Fedora 20) gives the following error: ====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 400 ---------------------------------------------------------------------- I made sure to run make clean // autogen.sh --system // make after applying the patches. Removing the patches fixes this behavior. Hope it helps! Daniel On 12/29/2014 12:53 PM, Aline Manera wrote:
Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests
docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py

On 06/01/2015 10:03, Daniel Henrique Barboza wrote:
Running the ./run-tests.sh script with these patches in my machine (Lenovo T410 with Fedora 20) gives the following error:
====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 400
----------------------------------------------------------------------
I made sure to run make clean // autogen.sh --system // make after applying the patches. Removing the patches fixes this behavior.
Hope it helps!
Thanks for checking it, Daniel! I verified the test case and it is failing because I assumed in the test case any interface would be good to create a bridged network which is not always true. So I will remove the below code and send the V2 + # Verify the current system has at least one interface to create a + # bridged network + interfaces = json.loads(self.request('/interfaces').read()) + if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'vlan-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 999})
Daniel
On 12/29/2014 12:53 PM, Aline Manera wrote:
Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests
docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 06/01/2015 13:40, Aline Manera wrote:
On 06/01/2015 10:03, Daniel Henrique Barboza wrote:
Running the ./run-tests.sh script with these patches in my machine (Lenovo T410 with Fedora 20) gives the following error:
====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 400
----------------------------------------------------------------------
I made sure to run make clean // autogen.sh --system // make after applying the patches. Removing the patches fixes this behavior.
Hope it helps!
Thanks for checking it, Daniel!
I verified the test case and it is failing because I assumed in the test case any interface would be good to create a bridged network which is not always true.
So I will remove the below code and send the V2
+ # Verify the current system has at least one interface to create a + # bridged network + interfaces = json.loads(self.request('/interfaces').read()) + if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'vlan-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 999})
I will change MockModel to allow creating a bridged network without verifying the interface and make the above test case use MockModel.
Daniel
On 12/29/2014 12:53 PM, Aline Manera wrote:
Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests
docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 06/01/2015 13:40, Aline Manera wrote:
On 06/01/2015 10:03, Daniel Henrique Barboza wrote:
Running the ./run-tests.sh script with these patches in my machine (Lenovo T410 with Fedora 20) gives the following error:
====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 400
----------------------------------------------------------------------
I made sure to run make clean // autogen.sh --system // make after applying the patches. Removing the patches fixes this behavior.
Hope it helps!
Thanks for checking it, Daniel!
I verified the test case and it is failing because I assumed in the test case any interface would be good to create a bridged network which is not always true.
So I will remove the below code and send the V2
+ # Verify the current system has at least one interface to create a + # bridged network
+ interfaces = json.loads(self.request('/interfaces').read())
Daniel, could you please check if changing change the above line to interfaces = json.loads(self.request('/interfaces*?type=nic*').read()) fix the issue you had? Thanks Aline Manera
+ if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'vlan-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 999})
Daniel
On 12/29/2014 12:53 PM, Aline Manera wrote:
Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests
docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 01/06/2015 06:16 PM, Aline Manera wrote:
On 06/01/2015 13:40, Aline Manera wrote:
On 06/01/2015 10:03, Daniel Henrique Barboza wrote:
Running the ./run-tests.sh script with these patches in my machine (Lenovo T410 with Fedora 20) gives the following error:
====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 400
----------------------------------------------------------------------
I made sure to run make clean // autogen.sh --system // make after applying the patches. Removing the patches fixes this behavior.
Hope it helps!
Thanks for checking it, Daniel!
I verified the test case and it is failing because I assumed in the test case any interface would be good to create a bridged network which is not always true.
So I will remove the below code and send the V2
+ # Verify the current system has at least one interface to create a + # bridged network
+ interfaces = json.loads(self.request('/interfaces').read())
Daniel, could you please check if changing change the above line to
interfaces = json.loads(self.request('/interfaces*?type=nic*').read())
fix the issue you had?
Thanks Aline Manera
It didn't. However the error code changed: ====================================================================== FAIL: test_network_lifecycle (test_network.NetworkTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_network.py", line 148, in test_network_lifecycle _do_test(net) File "test_network.py", line 101, in _do_test self.assertEquals(201, resp.status) AssertionError: 201 != 500 The original error was an error code = "400". Daniel
+ if len(interfaces) > 0: + iface = interfaces[0]['name'] + networks.append({'name': u'bridge-network', 'connection': 'bridge', + 'interface': iface}) + networks.append({'name': u'vlan-network', 'connection': 'bridge', + 'interface': iface, 'vlan_id': 999})
Daniel
On 12/29/2014 12:53 PM, Aline Manera wrote:
Aline Manera (5): Add message to KCHNET0010E code Bug fix: Allow deleting VLAN tagging bridged network Network API: Update docs/API.md Move rollback_wrapper function to a common place Reorganize the network tests
docs/API.md | 11 ++-- src/kimchi/i18n.py | 1 + src/kimchi/model/networks.py | 6 +- tests/test_model.py | 86 ++----------------------- tests/test_network.py | 147 +++++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 64 ------------------- tests/utils.py | 15 ++++- 7 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 tests/test_network.py
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel
participants (3)
-
Aline Manera
-
Daniel Henrique Barboza
-
Royce Lv