Merge "Simplify xml-json inheritance in compute"
diff --git a/cli/simple_read_only/test_compute.py b/cli/simple_read_only/test_compute.py
index 742b5a4..073fde1 100644
--- a/cli/simple_read_only/test_compute.py
+++ b/cli/simple_read_only/test_compute.py
@@ -55,6 +55,7 @@
         cls.identity = config.TempestConfig().identity
         super(SimpleReadOnlyNovaCLientTest, cls).setUpClass()
 
+    #NOTE(jogo): This should eventually be moved into a base class
     def nova(self, action, flags='', params='', admin=True, fail_ok=False):
         """Executes nova command for the given action."""
         #TODO(jogo) make admin=False work
@@ -66,39 +67,144 @@
         cmd = ' '.join([CONF.cli.cli_dir + 'nova', flags, action, params])
         LOG.info("running: '%s'" % cmd)
         cmd = shlex.split(cmd)
-        result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+        try:
+            result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+        except subprocess.CalledProcessError, e:
+            LOG.error("command output:\n%s" % e.output)
+            raise
         return result
 
-    def test_admin_version(self):
-        self.nova('', flags='--version')
-
-    def test_admin_timing(self):
-        self.nova('list', flags='--timing')
-
-    def test_admin_timeout(self):
-        self.nova('list', flags='--timeout 2')
-
-    def test_admin_debug_list(self):
-        self.nova('list', flags='--debug')
-
     def test_admin_fake_action(self):
         self.assertRaises(subprocess.CalledProcessError,
                           self.nova,
                           'this-does-nova-exist')
 
+    #NOTE(jogo): Commands in order listed in 'nova help'
+
+    # Positional arguments:
+
+    def test_admin_absolute_limites(self):
+        self.nova('absolute-limits')
+
     def test_admin_aggregate_list(self):
         self.nova('aggregate-list')
 
+    def test_admin_availability_zone_list(self):
+        self.nova('availability-zone-list')
+
     def test_admin_cloudpipe_list(self):
         self.nova('cloudpipe-list')
 
-    def test_admin_image_list(self):
-        self.nova('image-list')
+    def test_admin_credentials(self):
+        self.nova('credentials')
 
     def test_admin_dns_domains(self):
         self.nova('dns-domains')
 
+    @testtools.skip("needs parameters")
+    def test_admin_dns_list(self):
+        self.nova('dns-list')
+
+    def test_admin_endpoints(self):
+        self.nova('endpoints')
+
+    def test_admin_flavor_acces_list(self):
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.nova,
+                          'flavor-access-list')
+        # Failed to get access list for public flavor type
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.nova,
+                          'flavor-access-list',
+                          params='--flavor m1.tiny')
+
     def test_admin_flavor_list(self):
         self.nova('flavor-list')
 
-    #TODO(jogo) add more tests
+    def test_admin_floating_ip_bulk_list(self):
+        self.nova('floating-ip-bulk-list')
+
+    def test_admin_floating_ip_list(self):
+        self.nova('floating-ip-list')
+
+    def test_admin_floating_ip_pool_list(self):
+        self.nova('floating-ip-pool-list')
+
+    def test_admin_host_list(self):
+        self.nova('host-list')
+
+    def test_admin_hypervisor_list(self):
+        self.nova('hypervisor-list')
+
+    def test_admin_image_list(self):
+        self.nova('image-list')
+
+    @testtools.skip("needs parameters")
+    def test_admin_interface_list(self):
+        self.nova('interface-list')
+
+    def test_admin_keypair_list(self):
+        self.nova('keypair-list')
+
+    def test_admin_list(self):
+        self.nova('list')
+        self.nova('list', params='--all-tenants 1')
+        self.nova('list', params='--all-tenants 0')
+        self.assertRaises(subprocess.CalledProcessError,
+                          self.nova,
+                          'list',
+                          params='--all-tenants bad')
+
+    def test_admin_network_list(self):
+        self.nova('network-list')
+
+    def test_admin_rate_limits(self):
+        self.nova('rate-limits')
+
+    def test_admin_secgroup_list(self):
+        self.nova('secgroup-list')
+
+    @testtools.skip("needs parameters")
+    def test_admin_secgroup_list_rules(self):
+        self.nova('secgroup-list-rules')
+
+    def test_admin_servce_list(self):
+        self.nova('service-list')
+
+    def test_admin_usage(self):
+        self.nova('usage')
+
+    def test_admin_usage_list(self):
+        self.nova('usage-list')
+
+    def test_admin_volume_list(self):
+        self.nova('volume-list')
+
+    def test_admin_volume_snapshot_list(self):
+        self.nova('volume-snapshot-list')
+
+    def test_admin_volume_type_list(self):
+        self.nova('volume-type-list')
+
+    def test_admin_help(self):
+        self.nova('help')
+
+    def test_admin_list_extensions(self):
+        self.nova('list-extensions')
+
+    def test_admin_net_list(self):
+        self.nova('net-list')
+
+    # Optional arguments:
+
+    def test_admin_version(self):
+        self.nova('', flags='--version')
+
+    def test_admin_debug_list(self):
+        self.nova('list', flags='--debug')
+
+    def test_admin_timeout(self):
+        self.nova('list', flags='--timeout 2')
+
+    def test_admin_timing(self):
+        self.nova('list', flags='--timing')
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 45737be..36a9abd 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -16,9 +16,11 @@
 # Originally copied from python-glanceclient
 
 import copy
+import hashlib
 import httplib
 import logging
 import posixpath
+import re
 import socket
 import StringIO
 import struct
@@ -42,6 +44,7 @@
 LOG = logging.getLogger(__name__)
 USER_AGENT = 'tempest'
 CHUNKSIZE = 1024 * 64  # 64kB
+TOKEN_CHARS_RE = re.compile('^[-A-Za-z0-9+/=]*$')
 
 
 class HTTPClient(object):
@@ -92,42 +95,6 @@
         except httplib.InvalidURL:
             raise exc.EndpointNotFound
 
-    def log_curl_request(self, method, url, kwargs):
-        curl = ['curl -i -X %s' % method]
-
-        for (key, value) in kwargs['headers'].items():
-            header = '-H \'%s: %s\'' % (key, value)
-            curl.append(header)
-
-        conn_params_fmt = [
-            ('key_file', '--key %s'),
-            ('cert_file', '--cert %s'),
-            ('cacert', '--cacert %s'),
-        ]
-        for (key, fmt) in conn_params_fmt:
-            value = self.connection_kwargs.get(key)
-            if value:
-                curl.append(fmt % value)
-
-        if self.connection_kwargs.get('insecure'):
-            curl.append('-k')
-
-        if 'body' in kwargs:
-            curl.append('-d \'%s\'' % kwargs['body'])
-
-        curl.append('%s%s' % (self.endpoint, url))
-        LOG.debug(' '.join(curl))
-
-    @staticmethod
-    def log_http_response(resp, body=None):
-        status = (resp.version / 10.0, resp.status, resp.reason)
-        dump = ['\nHTTP/%.1f %s %s' % status]
-        dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()])
-        dump.append('')
-        if body:
-            dump.extend([body, ''])
-        LOG.debug('\n'.join(dump))
-
     def _http_request(self, url, method, **kwargs):
         """Send an http request with the specified characteristics.
 
@@ -140,7 +107,8 @@
         if self.auth_token:
             kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
 
-        self.log_curl_request(method, url, kwargs)
+        self._log_request(method, url, kwargs['headers'])
+
         conn = self.get_connection()
 
         try:
@@ -172,13 +140,37 @@
         # Read body into string if it isn't obviously image data
         if resp.getheader('content-type', None) != 'application/octet-stream':
             body_str = ''.join([chunk for chunk in body_iter])
-            self.log_http_response(resp, body_str)
             body_iter = StringIO.StringIO(body_str)
+            self._log_response(resp, None)
         else:
-            self.log_http_response(resp)
+            self._log_response(resp, body_iter)
 
         return resp, body_iter
 
+    def _log_request(self, method, url, headers):
+        LOG.info('Request: ' + method + ' ' + url)
+        if headers:
+            headers_out = headers
+            if 'X-Auth-Token' in headers and headers['X-Auth-Token']:
+                token = headers['X-Auth-Token']
+                if len(token) > 64 and TOKEN_CHARS_RE.match(token):
+                    headers_out = headers.copy()
+                    headers_out['X-Auth-Token'] = "<Token omitted>"
+                LOG.info('Request Headers: ' + str(headers_out))
+
+    def _log_response(self, resp, body):
+        status = str(resp.status)
+        LOG.info("Response Status: " + status)
+        if resp.getheaders():
+            LOG.info('Response Headers: ' + str(resp.getheaders()))
+        if body:
+            str_body = str(body)
+            length = len(body)
+            LOG.info('Response Body: ' + str_body[:2048])
+            if length >= 2048:
+                self.LOG.debug("Large body (%d) md5 summary: %s", length,
+                               hashlib.md5(str_body).hexdigest())
+
     def json_request(self, method, url, **kwargs):
         kwargs.setdefault('headers', {})
         kwargs['headers'].setdefault('Content-Type', 'application/json')
diff --git a/tempest/tests/compute/servers/test_server_metadata.py b/tempest/tests/compute/servers/test_server_metadata.py
index 2905a6b..6958a7d 100644
--- a/tempest/tests/compute/servers/test_server_metadata.py
+++ b/tempest/tests/compute/servers/test_server_metadata.py
@@ -163,6 +163,21 @@
                       'not succeed')
 
     @attr(type='negative')
+    def test_set_server_metadata_item_incorrect_uri_key(self):
+        #Raise BadRequest if key in uri does not match
+        #the key passed in body.
+
+        meta = {'testkey': 'testvalue'}
+        try:
+            resp, metadata = self.client.set_server_metadata_item(
+                                        self.server_id, 'key', meta)
+        except exceptions.BadRequest:
+            pass
+        else:
+            self.fail('Should raise BadRequest if URI key does not match key'
+                      'passed in the body')
+
+    @attr(type='negative')
     def test_set_nonexistant_server_metadata(self):
         # Negative test: Set metadata on a non existant server should not
         # succeed
diff --git a/tempest/tests/compute/servers/test_servers_negative.py b/tempest/tests/compute/servers/test_servers_negative.py
index 9b528f6..553af78 100644
--- a/tempest/tests/compute/servers/test_servers_negative.py
+++ b/tempest/tests/compute/servers/test_servers_negative.py
@@ -287,3 +287,9 @@
             pass
         else:
             self.fail('Server was created with nonexistent security group')
+
+    @attr(type='negative')
+    def test_get_non_existent_server(self):
+        # Get a non existent server details
+        self.assertRaises(exceptions.NotFound, self.client.get_server,
+                          '999erra43')
diff --git a/tempest/tests/identity/admin/test_services.py b/tempest/tests/identity/admin/test_services.py
index 9ac102a..77c8e83 100644
--- a/tempest/tests/identity/admin/test_services.py
+++ b/tempest/tests/identity/admin/test_services.py
@@ -56,12 +56,13 @@
             self.assertEqual(fetched_service['description'],
                              service_data['description'])
         finally:
-            #Deleting the service created in this method
-            resp, _ = self.client.delete_service(service_data['id'])
-            self.assertTrue(resp['status'].startswith('2'))
-            #Checking whether service is deleted successfully
-            self.assertRaises(exceptions.NotFound, self.client.get_service,
-                              service_data['id'])
+            if 'service_data' in locals():
+                # Deleting the service created in this method
+                resp, _ = self.client.delete_service(service_data['id'])
+                self.assertEqual(resp['status'], '204')
+                # Checking whether service is deleted successfully
+                self.assertRaises(exceptions.NotFound, self.client.get_service,
+                                  service_data['id'])
 
     def test_list_services(self):
         # Create, List, Verify and Delete Services