Merge "API test for Get Image Member(s) Schema"
diff --git a/tempest/api/compute/images/test_images.py b/tempest/api/compute/images/test_images.py
index ed316ef..5de2436 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -32,22 +32,6 @@
cls.client = cls.images_client
cls.servers_client = cls.servers_client
- cls.image_ids = []
-
- def tearDown(self):
- """Terminate test instances created after a test is executed."""
- for image_id in self.image_ids:
- self.client.delete_image(image_id)
- self.image_ids.remove(image_id)
- super(ImagesTestJSON, self).tearDown()
-
- def __create_image__(self, server_id, name, meta=None):
- resp, body = self.client.create_image(server_id, name, meta)
- image_id = data_utils.parse_image_id(resp['location'])
- self.client.wait_for_image_status(image_id, 'ACTIVE')
- self.image_ids.append(image_id)
- return resp, body
-
@test.attr(type=['negative', 'gate'])
def test_create_image_from_deleted_server(self):
# An image should not be created if the server instance is removed
@@ -60,8 +44,8 @@
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
self.assertRaises(exceptions.NotFound,
- self.__create_image__,
- server['id'], name, meta)
+ self.create_image_from_server,
+ server['id'], name=name, meta=meta)
@test.attr(type=['negative', 'gate'])
def test_create_image_from_invalid_server(self):
@@ -71,8 +55,9 @@
meta = {'image_type': 'test'}
resp = {}
resp['status'] = None
- self.assertRaises(exceptions.NotFound, self.__create_image__,
- '!@#$%^&*()', name, meta)
+ self.assertRaises(exceptions.NotFound,
+ self.create_image_from_server,
+ '!@#$%^&*()', name=name, meta=meta)
@test.attr(type=['negative', 'gate'])
def test_create_image_from_stopped_server(self):
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index 7c02787..eb397ba 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -26,6 +26,7 @@
List routers that the given L3 agent is hosting.
List L3 agents hosting the given router.
+ Add and Remove Router to L3 agent
v2.0 of the Neutron API is assumed. It is also assumed that the following
options are defined in the [network] section of etc/tempest.conf:
@@ -37,28 +38,49 @@
if not test.is_extension_enabled('l3_agent_scheduler', 'network'):
msg = "L3 Agent Scheduler Extension not enabled."
raise cls.skipException(msg)
+ # Trying to get agent details for L3 Agent
+ resp, body = cls.admin_client.list_agents()
+ agents = body['agents']
+ for agent in agents:
+ if agent['agent_type'] == 'L3 agent':
+ cls.agent = agent
+ break
+ else:
+ msg = "L3 Agent not found"
+ raise cls.skipException(msg)
@test.attr(type='smoke')
def test_list_routers_on_l3_agent(self):
- resp, body = self.admin_client.list_agents()
- agents = body['agents']
- for a in agents:
- if a['agent_type'] == 'L3 agent':
- agent = a
resp, body = self.admin_client.list_routers_on_l3_agent(
- agent['id'])
+ self.agent['id'])
self.assertEqual('200', resp['status'])
@test.attr(type='smoke')
- def test_list_l3_agents_hosting_router(self):
- name = data_utils.rand_name('router-')
+ def test_add_list_remove_router_on_l3_agent(self):
+ l3_agent_ids = list()
+ name = data_utils.rand_name('router1-')
resp, router = self.client.create_router(name)
+ self.addCleanup(self.client.delete_router, router['router']['id'])
+ resp, body = self.admin_client.add_router_to_l3_agent(
+ self.agent['id'], router['router']['id'])
self.assertEqual('201', resp['status'])
resp, body = self.admin_client.list_l3_agents_hosting_router(
router['router']['id'])
self.assertEqual('200', resp['status'])
- resp, _ = self.client.delete_router(router['router']['id'])
- self.assertEqual(204, resp.status)
+ for agent in body['agents']:
+ l3_agent_ids.append(agent['id'])
+ self.assertIn('agent_type', agent)
+ self.assertEqual('L3 agent', agent['agent_type'])
+ self.assertIn(self.agent['id'], l3_agent_ids)
+ del l3_agent_ids[:]
+ resp, body = self.admin_client.remove_router_from_l3_agent(
+ self.agent['id'], router['router']['id'])
+ self.assertEqual('204', resp['status'])
+ resp, body = self.admin_client.list_l3_agents_hosting_router(
+ router['router']['id'])
+ for agent in body['agents']:
+ l3_agent_ids.append(agent['id'])
+ self.assertNotIn(self.agent['id'], l3_agent_ids)
class L3AgentSchedulerTestXML(L3AgentSchedulerTestJSON):
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 66b6fe7..36ddb40 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -409,7 +409,7 @@
elif ctype.lower() in TXT_ENC:
parse_resp = False
else:
- raise exceptions.RestClientException(str(resp.status))
+ raise exceptions.InvalidContentType(str(resp.status))
if resp.status == 401 or resp.status == 403:
raise exceptions.Unauthorized()
@@ -451,25 +451,26 @@
# exception.
raise exceptions.InvalidHTTPResponseBody(message)
else:
- # I'm seeing both computeFault
- # and cloudServersFault come back.
- # Will file a bug to fix, but leave as is for now.
- if 'cloudServersFault' in resp_body:
- message = resp_body['cloudServersFault']['message']
- elif 'computeFault' in resp_body:
- message = resp_body['computeFault']['message']
- elif 'error' in resp_body: # Keystone errors
- message = resp_body['error']['message']
- raise exceptions.IdentityError(message)
- elif 'message' in resp_body:
- message = resp_body['message']
+ if isinstance(resp_body, dict):
+ # I'm seeing both computeFault
+ # and cloudServersFault come back.
+ # Will file a bug to fix, but leave as is for now.
+ if 'cloudServersFault' in resp_body:
+ message = resp_body['cloudServersFault']['message']
+ elif 'computeFault' in resp_body:
+ message = resp_body['computeFault']['message']
+ elif 'error' in resp_body: # Keystone errors
+ message = resp_body['error']['message']
+ raise exceptions.IdentityError(message)
+ elif 'message' in resp_body:
+ message = resp_body['message']
+ else:
+ message = resp_body
raise exceptions.ServerFault(message)
if resp.status >= 400:
- if parse_resp:
- resp_body = self._parse_resp(resp_body)
- raise exceptions.RestClientException(str(resp.status))
+ raise exceptions.UnexpectedResponseCode(str(resp.status))
def is_absolute_limit(self, resp, resp_body):
if (not isinstance(resp_body, collections.Mapping) or
diff --git a/tempest/common/utils/data_utils.py b/tempest/common/utils/data_utils.py
index cd32720..a0a88dd 100644
--- a/tempest/common/utils/data_utils.py
+++ b/tempest/common/utils/data_utils.py
@@ -15,12 +15,8 @@
import itertools
import random
-import re
-import urllib
import uuid
-from tempest import exceptions
-
def rand_uuid():
return str(uuid.uuid4())
@@ -57,37 +53,6 @@
return ':'.join(["%02x" % x for x in mac])
-def build_url(host, port, api_version=None, path=None,
- params=None, use_ssl=False):
- """Build the request URL from given host, port, path and parameters."""
-
- pattern = 'v\d\.\d'
- if re.match(pattern, path):
- message = 'Version should not be included in path.'
- raise exceptions.InvalidConfiguration(message=message)
-
- if use_ssl:
- url = "https://" + host
- else:
- url = "http://" + host
-
- if port is not None:
- url += ":" + port
- url += "/"
-
- if api_version is not None:
- url += api_version + "/"
-
- if path is not None:
- url += path
-
- if params is not None:
- url += "?"
- url += urllib.urlencode(params)
-
- return url
-
-
def parse_image_id(image_ref):
"""Return the image id from a given image ref."""
return image_ref.rsplit('/')[-1]
diff --git a/tempest/exceptions/__init__.py b/tempest/exceptions/__init__.py
index c95f94f..06dee71 100644
--- a/tempest/exceptions/__init__.py
+++ b/tempest/exceptions/__init__.py
@@ -146,3 +146,11 @@
class InvalidHTTPResponseBody(base.RestClientException):
message = "HTTP response body is invalid json or xml"
+
+
+class InvalidContentType(base.RestClientException):
+ message = "Invalid content type provided"
+
+
+class UnexpectedResponseCode(base.RestClientException):
+ message = "Unexpected response code received"
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index 5786ae7..d8c0979 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -280,6 +280,20 @@
body = json.loads(body)
return resp, body
+ def add_router_to_l3_agent(self, agent_id, router_id):
+ uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+ post_body = {"router_id": router_id}
+ body = json.dumps(post_body)
+ resp, body = self.post(uri, body)
+ body = json.loads(body)
+ return resp, body
+
+ def remove_router_from_l3_agent(self, agent_id, router_id):
+ uri = '%s/agents/%s/l3-routers/%s' % (
+ self.uri_prefix, agent_id, router_id)
+ resp, body = self.delete(uri)
+ return resp, body
+
def list_dhcp_agent_hosting_network(self, network_id):
uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
resp, body = self.get(uri)
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index 8152d71..daa60da 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -248,15 +248,30 @@
def list_routers_on_l3_agent(self, agent_id):
uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
resp, body = self.get(uri)
- body = _root_tag_fetcher_and_xml_to_json_parse(body)
+ routers = common.parse_array(etree.fromstring(body))
+ body = {'routers': routers}
return resp, body
def list_l3_agents_hosting_router(self, router_id):
uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
resp, body = self.get(uri)
+ agents = common.parse_array(etree.fromstring(body))
+ body = {'agents': agents}
+ return resp, body
+
+ def add_router_to_l3_agent(self, agent_id, router_id):
+ uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+ router = (common.Element("router_id", router_id))
+ resp, body = self.post(uri, str(common.Document(router)))
body = _root_tag_fetcher_and_xml_to_json_parse(body)
return resp, body
+ def remove_router_from_l3_agent(self, agent_id, router_id):
+ uri = '%s/agents/%s/l3-routers/%s' % (
+ self.uri_prefix, agent_id, router_id)
+ resp, body = self.delete(uri)
+ return resp, body
+
def list_dhcp_agent_hosting_network(self, network_id):
uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
resp, body = self.get(uri)
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index 9f07972..827b5c9 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -230,3 +230,114 @@
data = {"one_top_key": "not_list_or_dict_value"}
body = self.rest_client._parse_resp(json.dumps(data))
self.assertEqual(data, body)
+
+
+class TestRestClientErrorCheckerJSON(base.TestCase):
+ c_type = "application/json"
+
+ def set_data(self, r_code, enc=None, r_body=None):
+ if enc is None:
+ enc = self.c_type
+ resp_dict = {'status': r_code, 'content-type': enc}
+ resp = httplib2.Response(resp_dict)
+ data = {
+ "method": "fake_method",
+ "url": "fake_url",
+ "headers": "fake_headers",
+ "body": "fake_body",
+ "resp": resp,
+ "resp_body": '{"resp_body": "fake_resp_body"}',
+ }
+ if r_body is not None:
+ data.update({"resp_body": r_body})
+ return data
+
+ def setUp(self):
+ super(TestRestClientErrorCheckerJSON, self).setUp()
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakeConfig)
+ self.rest_client = rest_client.RestClient(
+ fake_auth_provider.FakeAuthProvider())
+
+ def test_response_less_than_400(self):
+ self.rest_client._error_checker(**self.set_data("399"))
+
+ def test_response_400(self):
+ self.assertRaises(exceptions.BadRequest,
+ self.rest_client._error_checker,
+ **self.set_data("400"))
+
+ def test_response_401(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.rest_client._error_checker,
+ **self.set_data("401"))
+
+ def test_response_403(self):
+ self.assertRaises(exceptions.Unauthorized,
+ self.rest_client._error_checker,
+ **self.set_data("403"))
+
+ def test_response_404(self):
+ self.assertRaises(exceptions.NotFound,
+ self.rest_client._error_checker,
+ **self.set_data("404"))
+
+ def test_response_409(self):
+ self.assertRaises(exceptions.Conflict,
+ self.rest_client._error_checker,
+ **self.set_data("409"))
+
+ def test_response_413(self):
+ self.assertRaises(exceptions.OverLimit,
+ self.rest_client._error_checker,
+ **self.set_data("413"))
+
+ def test_response_422(self):
+ self.assertRaises(exceptions.UnprocessableEntity,
+ self.rest_client._error_checker,
+ **self.set_data("422"))
+
+ def test_response_500_with_text(self):
+ # _parse_resp is expected to return 'str'
+ self.assertRaises(exceptions.ServerFault,
+ self.rest_client._error_checker,
+ **self.set_data("500"))
+
+ def test_response_501_with_text(self):
+ self.assertRaises(exceptions.ServerFault,
+ self.rest_client._error_checker,
+ **self.set_data("501"))
+
+ def test_response_500_with_dict(self):
+ r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+ self.assertRaises(exceptions.ServerFault,
+ self.rest_client._error_checker,
+ **self.set_data("500", r_body=r_body))
+
+ def test_response_501_with_dict(self):
+ r_body = '{"resp_body": {"err": "fake_resp_body"}}'
+ self.assertRaises(exceptions.ServerFault,
+ self.rest_client._error_checker,
+ **self.set_data("501", r_body=r_body))
+
+ def test_response_bigger_than_400(self):
+ # Any response code, that bigger than 400, and not in
+ # (401, 403, 404, 409, 413, 422, 500, 501)
+ self.assertRaises(exceptions.UnexpectedResponseCode,
+ self.rest_client._error_checker,
+ **self.set_data("402"))
+
+
+class TestRestClientErrorCheckerXML(TestRestClientErrorCheckerJSON):
+ c_type = "application/xml"
+
+
+class TestRestClientErrorCheckerTEXT(TestRestClientErrorCheckerJSON):
+ c_type = "text/plain"
+
+ def test_fake_content_type(self):
+ # This test is required only in one exemplar
+ # Any response code, that bigger than 400, and not in
+ # (401, 403, 404, 409, 413, 422, 500, 501)
+ self.assertRaises(exceptions.InvalidContentType,
+ self.rest_client._error_checker,
+ **self.set_data("405", enc="fake_enc"))
diff --git a/tools/check_logs.py b/tools/check_logs.py
index 98e079a..edf95a1 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -25,20 +25,24 @@
import yaml
-is_neutron = os.environ.get('DEVSTACK_GATE_NEUTRON', "0") == "1"
is_grenade = (os.environ.get('DEVSTACK_GATE_GRENADE', "0") == "1" or
os.environ.get('DEVSTACK_GATE_GRENADE_FORWARD', "0") == "1")
dump_all_errors = True
+# As logs are made clean, add to this set
+must_be_clean = set(['c-sch', 'g-reg', 'ceilometer-alarm-notifier',
+ 'ceilometer-collector', 'horizon', 'n-crt', 'n-obj',
+ 'q-vpn'])
+
def process_files(file_specs, url_specs, whitelists):
regexp = re.compile(r"^.* (ERROR|CRITICAL|TRACE) .*\[.*\-.*\]")
- had_errors = False
+ logs_with_errors = []
for (name, filename) in file_specs:
whitelist = whitelists.get(name, [])
with open(filename) as content:
if scan_content(name, content, regexp, whitelist):
- had_errors = True
+ logs_with_errors.append(name)
for (name, url) in url_specs:
whitelist = whitelists.get(name, [])
req = urllib2.Request(url)
@@ -47,8 +51,8 @@
buf = StringIO.StringIO(page.read())
f = gzip.GzipFile(fileobj=buf)
if scan_content(name, f.read().splitlines(), regexp, whitelist):
- had_errors = True
- return had_errors
+ logs_with_errors.append(name)
+ return logs_with_errors
def scan_content(name, content, regexp, whitelist):
@@ -122,19 +126,22 @@
assert 'module' in w, 'no module in %s' % name
assert 'message' in w, 'no message in %s' % name
whitelists = loaded
- if process_files(files_to_process, urls_to_process, whitelists):
+ logs_with_errors = process_files(files_to_process, urls_to_process,
+ whitelists)
+ if logs_with_errors:
print("Logs have errors")
- if is_neutron:
- print("Currently not failing neutron builds with errors")
- return 0
- if is_grenade:
- print("Currently not failing grenade runs with errors")
- return 0
- print("FAILED")
- return 1
- else:
- print("ok")
+ if is_grenade:
+ print("Currently not failing grenade runs with errors")
return 0
+ failed = False
+ for log in logs_with_errors:
+ if log in must_be_clean:
+ print("FAILED: %s" % log)
+ failed = True
+ if failed:
+ return 1
+ print("ok")
+ return 0
usage = """
Find non-white-listed log errors in log files from a devstack-gate run.