First cut of Network client and positive tests.
Fixes bug 946675.
1. Created Network (Quantum ) client for Tempest.
2. Added positive tests for Quantum.
3. Addressed review comments.
Change-Id: If3e27d3aadc70fbf6db722b6ae554a1188fe56e8
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index c7c403c..bfb7e59 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -126,3 +126,9 @@
password = {$ADMIN_PASSWORD}
# The above administrative user's tenant name
tenant_name = {$ADMIN_TENANT_NAME}
+
+[network]
+# This section contains configuration options used when executing tests
+# against the OpenStack Network API.
+api_version = v1.1
+catalog_type = network
diff --git a/tempest/config.py b/tempest/config.py
index 0a76ce5..08e90fd 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -260,6 +260,24 @@
return self.get("tenant_name", "demo")
+class NetworkConfig(BaseConfig):
+ """Provides configuration information for connecting to an OpenStack
+ Network Service.
+ """
+
+ SECTION_NAME = "network"
+
+ @property
+ def catalog_type(self):
+ """Catalog type of the Quantum service."""
+ return self.get("catalog_type", 'network')
+
+ @property
+ def api_version(self):
+ """Version of Quantum API"""
+ return self.get("api_version", "v1.1")
+
+
# TODO(jaypipes): Move this to a common utils (not data_utils...)
def singleton(cls):
"""Simple wrapper for classes that should only have a single instance"""
@@ -306,6 +324,7 @@
self.compute_admin = ComputeAdminConfig(self._conf)
self.identity = IdentityConfig(self._conf)
self.images = ImagesConfig(self._conf)
+ self.network = NetworkConfig(self._conf)
def load_config(self, path):
"""Read configuration from given path and return a config object."""
diff --git a/tempest/openstack.py b/tempest/openstack.py
index 6e58272..18af647 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -20,6 +20,7 @@
import tempest.config
from tempest import exceptions
from tempest.services.image import service as image_service
+from tempest.services.network.json.network_client import NetworkClient
from tempest.services.nova.json.images_client import ImagesClient
from tempest.services.nova.json.flavors_client import FlavorsClient
from tempest.services.nova.json.servers_client import ServersClient
@@ -83,6 +84,7 @@
self.volumes_client = VolumesClient(*client_args)
self.admin_client = AdminClient(*client_args)
self.token_client = TokenClient(self.config)
+ self.network_client = NetworkClient(*client_args)
class AltManager(Manager):
diff --git a/tempest/services/network/__init__.py b/tempest/services/network/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/network/__init__.py
diff --git a/tempest/services/network/json/__init__.py b/tempest/services/network/json/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/services/network/json/__init__.py
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
new file mode 100644
index 0000000..49b83fe
--- /dev/null
+++ b/tempest/services/network/json/network_client.py
@@ -0,0 +1,103 @@
+import json
+from tempest.common.rest_client import RestClient
+
+
+class NetworkClient(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(NetworkClient, self).__init__(config, username, password,
+ auth_url, tenant_name)
+ self.service = self.config.network.catalog_type
+
+ def list_networks(self):
+ resp, body = self.get('networks')
+ body = json.loads(body)
+ return resp, body
+
+ def create_network(self, name, key="network"):
+ post_body = {
+ key: {
+ 'name': name
+ }
+ }
+ headers = {'Content-Type': 'application/json'}
+ body = json.dumps(post_body)
+ resp, body = self.post('networks',
+ headers=headers,
+ body=body)
+ body = json.loads(body)
+ return resp, body
+
+ def list_networks_details(self):
+ resp, body = self.get('networks/detail')
+ body = json.loads(body)
+ return resp, body
+
+ def get_network(self, uuid):
+ resp, body = self.get('networks/%s' % uuid)
+ body = json.loads(body)
+ return resp, body
+
+ def get_network_details(self, uuid):
+ resp, body = self.get('networks/%s/detail' % uuid)
+ body = json.loads(body)
+ return resp, body
+
+ def delete_network(self, uuid):
+ resp, body = self.delete('networks/%s' % uuid)
+ return resp, body
+
+ def create_port(self, network_id, zone, state=None, key='port'):
+ if not state:
+ state = 'ACTIVE'
+ post_body = {
+ key: {
+ 'state': state,
+ 'nova_id': zone
+ }
+ }
+ headers = {'Content-Type': 'application/json'}
+ body = json.dumps(post_body)
+ resp, body = self.post('networks/%s/ports.json' % network_id,
+ headers=headers, body=body)
+ body = json.loads(body)
+ return resp, body
+
+ def delete_port(self, network_id, port_id):
+ resp, body = self.delete('networks/%s/ports/%s.json' %
+ (network_id, port_id))
+ return resp, body
+
+ def list_ports(self, network_id):
+ resp, body = self.get('networks/%s/ports.json' % network_id)
+ body = json.loads(body)
+ return resp, body
+
+ def list_port_details(self, network_id):
+ url = 'networks/%s/ports/detail.json' % network_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
+
+ def attach_port(self, network_id, port_id, interface_id):
+ post_body = {
+ 'attachment': {
+ 'id': interface_id
+ }
+ }
+ headers = {'Content-Type': 'application/json'}
+ body = json.dumps(post_body)
+ url = 'networks/%s/ports/%s/attachment.json' % (network_id, port_id)
+ resp, body = self.put(url, headers=headers, body=body)
+ return resp, body
+
+ def detach_port(self, network_id, port_id):
+ url = 'networks/%s/ports/%s/attachment.json' % (network_id, port_id)
+ resp, body = self.delete(url)
+ return resp, body
+
+ def list_port_attachment(self, network_id, port_id):
+ url = 'networks/%s/ports/%s/attachment.json' % (network_id, port_id)
+ resp, body = self.get(url)
+ body = json.loads(body)
+ return resp, body
diff --git a/tempest/tests/network/__init__.py b/tempest/tests/network/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/network/__init__.py
diff --git a/tempest/tests/network/test_networks.py b/tempest/tests/network/test_networks.py
new file mode 100644
index 0000000..6adbb6b
--- /dev/null
+++ b/tempest/tests/network/test_networks.py
@@ -0,0 +1,66 @@
+from nose.plugins.attrib import attr
+from tempest import openstack
+from tempest.common.utils.data_utils import rand_name
+import unittest2 as unittest
+
+
+class NetworksTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.os = openstack.Manager()
+ cls.client = cls.os.network_client
+ cls.config = cls.os.config
+ cls.name = rand_name('network')
+ resp, body = cls.client.create_network(cls.name)
+ cls.network = body['network']
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.client.delete_network(cls.network['id'])
+
+ @attr(type='positive')
+ def test_create_delete_network(self):
+ """Creates and deletes a network for a tenant"""
+ name = rand_name('network')
+ resp, body = self.client.create_network(name)
+ self.assertEqual('202', resp['status'])
+ network = body['network']
+ self.assertTrue(network['id'] is not None)
+ resp, body = self.client.delete_network(network['id'])
+ self.assertEqual('204', resp['status'])
+
+ @attr(type='positive')
+ def test_show_network(self):
+ """Verifies the details of a network"""
+ resp, body = self.client.get_network(self.network['id'])
+ self.assertEqual('200', resp['status'])
+ network = body['network']
+ self.assertEqual(self.network['id'], network['id'])
+ self.assertEqual(self.name, network['name'])
+
+ @attr(type='positive')
+ def test_show_network_details(self):
+ """Verifies the full details of a network"""
+ resp, body = self.client.get_network_details(self.network['id'])
+ self.assertEqual('200', resp['status'])
+ network = body['network']
+ self.assertEqual(self.network['id'], network['id'])
+ self.assertEqual(self.name, network['name'])
+ self.assertEqual(len(network['ports']), 0)
+
+ @attr(type='positive')
+ def test_list_networks(self):
+ """Verify the network exists in the list of all networks"""
+ resp, body = self.client.list_networks()
+ networks = body['networks']
+ found = any(n for n in networks if n['id'] == self.network['id'])
+ self.assertTrue(found)
+
+ @attr(type='positive')
+ def test_list_networks_with_detail(self):
+ """Verify the network exists in the detailed list of all networks"""
+ resp, body = self.client.list_networks_details()
+ networks = body['networks']
+ found = any(n for n in networks if n['id'] == self.network['id'])
+ self.assertTrue(found)