Merge "Adding port_admin_state_change option to config"
diff --git a/etc/accounts.yaml.sample b/etc/accounts.yaml.sample
index 64ff8a7..31ceb33 100644
--- a/etc/accounts.yaml.sample
+++ b/etc/accounts.yaml.sample
@@ -33,3 +33,5 @@
   password: 'test_password'
   types:
      - 'admin'
+  resources:
+    network: 'public'
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9007e51..2a72635 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -253,10 +253,6 @@
 # image. (string value)
 #image_alt_ssh_user = root
 
-# Password used to authenticate to an instance using the alternate
-# image. (string value)
-#image_alt_ssh_password = password
-
 # Time in seconds between build status checks. (integer value)
 #build_interval = 1
 
@@ -269,16 +265,16 @@
 #run_ssh = false
 
 # Auth method used for authenticate to the instance. Valid choices
-# are: keypair, configured, adminpass. keypair: start the servers with
-# an ssh keypair. configured: use the configured user and password.
-# adminpass: use the injected adminPass. disabled: avoid using ssh
-# when it is an option. (string value)
+# are: keypair, configured, adminpass and disabled. Keypair: start the
+# servers with a ssh keypair. Configured: use the configured user and
+# password. Adminpass: use the injected adminPass. Disabled: avoid
+# using ssh when it is an option. (string value)
 #ssh_auth_method = keypair
 
 # How to connect to the instance? fixed: using the first ip belongs
-# the fixed network floating: creating and using a floating ip (string
-# value)
-#ssh_connect_method = fixed
+# the fixed network floating: creating and using a floating ip.
+# (string value)
+#ssh_connect_method = floating
 
 # User name used to authenticate to an instance. (string value)
 #ssh_user = root
@@ -286,6 +282,14 @@
 # Timeout in seconds to wait for ping to succeed. (integer value)
 #ping_timeout = 120
 
+# The packet size for ping packets originating from remote linux hosts
+# (integer value)
+#ping_size = 56
+
+# The number of ping packets originating from remote linux hosts
+# (integer value)
+#ping_count = 1
+
 # Timeout in seconds to wait for authentication to succeed. (integer
 # value)
 #ssh_timeout = 300
@@ -301,7 +305,8 @@
 # Name of the fixed network that is visible to all test tenants. If
 # multiple networks are available for a tenant this is the network
 # which will be used for creating servers if tempest does not create a
-# network or a network is not specified elsewhere (string value)
+# network or a network is not specified elsewhere. It may be used for
+# ssh validation only if floating IPs are disabled. (string value)
 #fixed_network_name = <None>
 
 # Network used for SSH connections. Ignored if
@@ -326,10 +331,6 @@
 # Allowed values: public, admin, internal, publicURL, adminURL, internalURL
 #endpoint_type = publicURL
 
-# Path to a private key file for SSH access to remote hosts (string
-# value)
-#path_to_private_key = <None>
-
 # Expected device name when a volume is attached to an instance
 # (string value)
 #volume_device_name = vdb
@@ -746,14 +747,19 @@
 # The mask bits for tenant ipv6 subnets (integer value)
 #tenant_network_v6_mask_bits = 64
 
-# Whether tenant network connectivity should be evaluated directly
-# (boolean value)
+# Whether tenant networks can be reached directly from the test
+# client. This must be set to True when the 'fixed' ssh_connect_method
+# is selected. (boolean value)
 #tenant_networks_reachable = false
 
 # Id of the public network that provides external connectivity (string
 # value)
 #public_network_id =
 
+# Default floating network name. Used to allocate floating IPs when
+# neutron is enabled. (string value)
+#floating_network_name = <None>
+
 # Id of the public router that provides external connectivity. This
 # should only be used when Neutron's 'allow_overlapping_ips' is set to
 # 'False' in neutron.conf. usually not needed past 'Grizzly' release
@@ -1075,6 +1081,38 @@
 #too_slow_to_test = true
 
 
+[validation]
+
+#
+# From tempest.config
+#
+
+# Default IP type used for validation: -fixed: uses the first IP
+# belonging to the fixed network -floating: creates and uses a
+# floating IP (string value)
+# Allowed values: fixed, floating
+#connect_method = floating
+
+# Default authentication method to the instance. Only ssh via keypair
+# is supported for now. Additional methods will be handled in a
+# separate spec. (string value)
+# Allowed values: keypair
+#auth_method = keypair
+
+# Default IP version for ssh connections. (integer value)
+#ip_version_for_ssh = 4
+
+# Timeout in seconds to wait for ping to succeed. (integer value)
+#ping_timeout = 120
+
+# Timeout in seconds to wait for the TCP connection to be successful.
+# (integer value)
+#connect_timeout = 60
+
+# Timeout in seconds to wait for the ssh banner. (integer value)
+#ssh_timeout = 300
+
+
 [volume]
 
 #
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index f33204d..eccd600 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -69,7 +69,10 @@
 
         network = cls.get_tenant_network()
         if network:
-            cls.fixed_network_name = network['name']
+            if network.get('name'):
+                cls.fixed_network_name = network['name']
+            else:
+                cls.fixed_network_name = None
         else:
             cls.fixed_network_name = None
         network_kwargs = fixed_network.set_networks_kwarg(network)
diff --git a/tempest/api_schema/response/compute/flavors.py b/tempest/api_schema/response/compute/flavors.py
deleted file mode 100644
index 65f2c28..0000000
--- a/tempest/api_schema/response/compute/flavors.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from tempest.api_schema.response.compute import parameter_types
-
-list_flavors = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'flavors': {
-                'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'name': {'type': 'string'},
-                        'links': parameter_types.links,
-                        'id': {'type': 'string'}
-                    },
-                    'required': ['name', 'links', 'id']
-                }
-            },
-            'flavors_links': parameter_types.links
-        },
-        # NOTE(gmann): flavors_links attribute is not necessary
-        # to be present always So it is not 'required'.
-        'required': ['flavors']
-    }
-}
-
-common_flavor_info = {
-    'type': 'object',
-    'properties': {
-        'name': {'type': 'string'},
-        'links': parameter_types.links,
-        'ram': {'type': 'integer'},
-        'vcpus': {'type': 'integer'},
-        'swap': {'type': 'integer'},
-        'disk': {'type': 'integer'},
-        'id': {'type': 'string'}
-    },
-    'required': ['name', 'links', 'ram', 'vcpus',
-                 'swap', 'disk', 'id']
-}
-
-common_flavor_list_details = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'flavors': {
-                'type': 'array',
-                'items': common_flavor_info
-            }
-        },
-        'required': ['flavors']
-    }
-}
-
-common_flavor_details = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'flavor': common_flavor_info
-        },
-        'required': ['flavor']
-    }
-}
diff --git a/tempest/api_schema/response/compute/hosts.py b/tempest/api_schema/response/compute/hosts.py
deleted file mode 100644
index 2596c27..0000000
--- a/tempest/api_schema/response/compute/hosts.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-common_start_up_body = {
-    'type': 'object',
-    'properties': {
-        'host': {'type': 'string'},
-        'power_action': {'enum': ['startup']}
-    },
-    'required': ['host', 'power_action']
-}
-
-list_hosts = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'hosts': {
-                'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'host_name': {'type': 'string'},
-                        'service': {'type': 'string'},
-                        'zone': {'type': 'string'}
-                    },
-                    'required': ['host_name', 'service', 'zone']
-                }
-            }
-        },
-        'required': ['hosts']
-    }
-}
-
-show_host_detail = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'host': {
-                'type': 'array',
-                'item': {
-                    'type': 'object',
-                    'properties': {
-                        'resource': {
-                            'type': 'object',
-                            'properties': {
-                                'cpu': {'type': 'integer'},
-                                'disk_gb': {'type': 'integer'},
-                                'host': {'type': 'string'},
-                                'memory_mb': {'type': 'integer'},
-                                'project': {'type': 'string'}
-                            },
-                            'required': ['cpu', 'disk_gb', 'host',
-                                         'memory_mb', 'project']
-                        }
-                    },
-                    'required': ['resource']
-                }
-            }
-        },
-        'required': ['host']
-    }
-}
-
-update_host_common = {
-    'type': 'object',
-    'properties': {
-        'host': {'type': 'string'},
-        'maintenance_mode': {'enum': ['on_maintenance', 'off_maintenance']},
-        'status': {'enum': ['enabled', 'disabled']}
-    },
-    'required': ['host', 'maintenance_mode', 'status']
-}
diff --git a/tempest/api_schema/response/compute/keypairs.py b/tempest/api_schema/response/compute/keypairs.py
deleted file mode 100644
index 2ae410c..0000000
--- a/tempest/api_schema/response/compute/keypairs.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-list_keypairs = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'keypairs': {
-                'type': 'array',
-                'items': {
-                    'type': 'object',
-                    'properties': {
-                        'keypair': {
-                            'type': 'object',
-                            'properties': {
-                                'public_key': {'type': 'string'},
-                                'name': {'type': 'string'},
-                                'fingerprint': {'type': 'string'}
-                            },
-                            'required': ['public_key', 'name', 'fingerprint']
-                        }
-                    },
-                    'required': ['keypair']
-                }
-            }
-        },
-        'required': ['keypairs']
-    }
-}
-
-create_keypair = {
-    'type': 'object',
-    'properties': {
-        'keypair': {
-            'type': 'object',
-            'properties': {
-                'fingerprint': {'type': 'string'},
-                'name': {'type': 'string'},
-                'public_key': {'type': 'string'},
-                'user_id': {'type': 'string'},
-                'private_key': {'type': 'string'}
-            },
-            # When create keypair API is being called with 'Public key'
-            # (Importing keypair) then, response body does not contain
-            # 'private_key' So it is not defined as 'required'
-            'required': ['fingerprint', 'name', 'public_key', 'user_id']
-        }
-    },
-    'required': ['keypair']
-}
diff --git a/tempest/api_schema/response/compute/quotas.py b/tempest/api_schema/response/compute/quotas.py
deleted file mode 100644
index 863104c..0000000
--- a/tempest/api_schema/response/compute/quotas.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2014 NEC Corporation.  All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-common_quota_set = {
-    'status_code': [200],
-    'response_body': {
-        'type': 'object',
-        'properties': {
-            'quota_set': {
-                'type': 'object',
-                'properties': {
-                    'instances': {'type': 'integer'},
-                    'cores': {'type': 'integer'},
-                    'ram': {'type': 'integer'},
-                    'floating_ips': {'type': 'integer'},
-                    'fixed_ips': {'type': 'integer'},
-                    'metadata_items': {'type': 'integer'},
-                    'key_pairs': {'type': 'integer'},
-                    'security_groups': {'type': 'integer'},
-                    'security_group_rules': {'type': 'integer'},
-                    'server_group_members': {'type': 'integer'},
-                    'server_groups': {'type': 'integer'},
-                },
-                # NOTE: server_group_members and server_groups are represented
-                # when enabling quota_server_group extension. So they should
-                # not be required.
-                'required': ['instances', 'cores', 'ram',
-                             'floating_ips', 'fixed_ips',
-                             'metadata_items', 'key_pairs',
-                             'security_groups', 'security_group_rules']
-            }
-        },
-        'required': ['quota_set']
-    }
-}
diff --git a/tempest/api_schema/response/compute/v2_1/flavors.py b/tempest/api_schema/response/compute/v2_1/flavors.py
index 76c4cee..725d17a 100644
--- a/tempest/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/api_schema/response/compute/v2_1/flavors.py
@@ -12,52 +12,86 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
-
-from tempest.api_schema.response.compute import flavors
 from tempest.api_schema.response.compute import parameter_types
 
-list_flavors_details = copy.deepcopy(flavors.common_flavor_list_details)
+list_flavors = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                        'links': parameter_types.links,
+                        'id': {'type': 'string'}
+                    },
+                    'required': ['name', 'links', 'id']
+                }
+            },
+            'flavors_links': parameter_types.links
+        },
+        # NOTE(gmann): flavors_links attribute is not necessary
+        # to be present always So it is not 'required'.
+        'required': ['flavors']
+    }
+}
 
-# 'swap' attributes comes as integer value but if it is empty it comes as "".
-# So defining type of as string and integer.
-list_flavors_details['response_body']['properties']['flavors']['items'][
-    'properties']['swap'] = {'type': ['string', 'integer']}
+common_flavor_info = {
+    'type': 'object',
+    'properties': {
+        'name': {'type': 'string'},
+        'links': parameter_types.links,
+        'ram': {'type': 'integer'},
+        'vcpus': {'type': 'integer'},
+        # 'swap' attributes comes as integer value but if it is empty
+        # it comes as "". So defining type of as string and integer.
+        'swap': {'type': ['integer', 'string']},
+        'disk': {'type': 'integer'},
+        'id': {'type': 'string'},
+        'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
+        'os-flavor-access:is_public': {'type': 'boolean'},
+        'rxtx_factor': {'type': 'number'},
+        'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}
+    },
+    # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
+    # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'.
+    'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id']
+}
 
-# Defining 'flavors_links' attributes for V2 flavor schema
-list_flavors_details['response_body'][
-    'properties'].update({'flavors_links': parameter_types.links})
-# NOTE(gmann): flavors_links attribute is not necessary to be
-# present always So it is not 'required'.
-
-# Defining extra attributes for V2 flavor schema
-list_flavors_details['response_body']['properties']['flavors']['items'][
-    'properties'].update({'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
-                          'os-flavor-access:is_public': {'type': 'boolean'},
-                          'rxtx_factor': {'type': 'number'},
-                          'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}})
-# 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and 'OS-FLV-EXT-DATA'
-# are API extensions. So they are not 'required'.
+list_flavors_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavors': {
+                'type': 'array',
+                'items': common_flavor_info
+            },
+            # NOTE(gmann): flavors_links attribute is not necessary
+            # to be present always So it is not 'required'.
+            'flavors_links': parameter_types.links
+        },
+        'required': ['flavors']
+    }
+}
 
 unset_flavor_extra_specs = {
     'status_code': [200]
 }
 
-create_get_flavor_details = copy.deepcopy(flavors.common_flavor_details)
-
-# 'swap' attributes comes as integer value but if it is empty it comes as "".
-# So defining type of as string and integer.
-create_get_flavor_details['response_body']['properties']['flavor'][
-    'properties']['swap'] = {'type': ['string', 'integer']}
-
-# Defining extra attributes for V2 flavor schema
-create_get_flavor_details['response_body']['properties']['flavor'][
-    'properties'].update({'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
-                          'os-flavor-access:is_public': {'type': 'boolean'},
-                          'rxtx_factor': {'type': 'number'},
-                          'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'}})
-# 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and 'OS-FLV-EXT-DATA'
-# are API extensions. So they are not 'required'.
+create_get_flavor_details = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'flavor': common_flavor_info
+        },
+        'required': ['flavor']
+    }
+}
 
 delete_flavor = {
     'status_code': [202]
diff --git a/tempest/api_schema/response/compute/v2_1/hosts.py b/tempest/api_schema/response/compute/v2_1/hosts.py
index 0944792..72d5a07 100644
--- a/tempest/api_schema/response/compute/v2_1/hosts.py
+++ b/tempest/api_schema/response/compute/v2_1/hosts.py
@@ -14,12 +14,70 @@
 
 import copy
 
-from tempest.api_schema.response.compute import hosts
 
+list_hosts = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hosts': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'host_name': {'type': 'string'},
+                        'service': {'type': 'string'},
+                        'zone': {'type': 'string'}
+                    },
+                    'required': ['host_name', 'service', 'zone']
+                }
+            }
+        },
+        'required': ['hosts']
+    }
+}
+
+get_host_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {
+                'type': 'array',
+                'item': {
+                    'type': 'object',
+                    'properties': {
+                        'resource': {
+                            'type': 'object',
+                            'properties': {
+                                'cpu': {'type': 'integer'},
+                                'disk_gb': {'type': 'integer'},
+                                'host': {'type': 'string'},
+                                'memory_mb': {'type': 'integer'},
+                                'project': {'type': 'string'}
+                            },
+                            'required': ['cpu', 'disk_gb', 'host',
+                                         'memory_mb', 'project']
+                        }
+                    },
+                    'required': ['resource']
+                }
+            }
+        },
+        'required': ['host']
+    }
+}
 
 startup_host = {
     'status_code': [200],
-    'response_body': hosts.common_start_up_body
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {'type': 'string'},
+            'power_action': {'enum': ['startup']}
+        },
+        'required': ['host', 'power_action']
+    }
 }
 
 # The 'power_action' attribute of 'shutdown_host' API is 'shutdown'
@@ -38,5 +96,14 @@
 
 update_host = {
     'status_code': [200],
-    'response_body': hosts.update_host_common
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {'type': 'string'},
+            'maintenance_mode': {'enum': ['on_maintenance',
+                                          'off_maintenance']},
+            'status': {'enum': ['enabled', 'disabled']}
+        },
+        'required': ['host', 'maintenance_mode', 'status']
+    }
 }
diff --git a/tempest/api_schema/response/compute/v2_1/keypairs.py b/tempest/api_schema/response/compute/v2_1/keypairs.py
index ec26fa0..ceae6cf 100644
--- a/tempest/api_schema/response/compute/v2_1/keypairs.py
+++ b/tempest/api_schema/response/compute/v2_1/keypairs.py
@@ -12,8 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest.api_schema.response.compute import keypairs
-
 get_keypair = {
     'status_code': [200],
     'response_body': {
@@ -47,9 +45,56 @@
 
 create_keypair = {
     'status_code': [200],
-    'response_body': keypairs.create_keypair
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'keypair': {
+                'type': 'object',
+                'properties': {
+                    'fingerprint': {'type': 'string'},
+                    'name': {'type': 'string'},
+                    'public_key': {'type': 'string'},
+                    'user_id': {'type': 'string'},
+                    'private_key': {'type': 'string'}
+                },
+                # When create keypair API is being called with 'Public key'
+                # (Importing keypair) then, response body does not contain
+                # 'private_key' So it is not defined as 'required'
+                'required': ['fingerprint', 'name', 'public_key', 'user_id']
+            }
+        },
+        'required': ['keypair']
+    }
 }
 
 delete_keypair = {
     'status_code': [202],
 }
+
+list_keypairs = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'keypairs': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'keypair': {
+                            'type': 'object',
+                            'properties': {
+                                'public_key': {'type': 'string'},
+                                'name': {'type': 'string'},
+                                'fingerprint': {'type': 'string'}
+                            },
+                            'required': ['public_key', 'name', 'fingerprint']
+                        }
+                    },
+                    'required': ['keypair']
+                }
+            }
+        },
+        'required': ['keypairs']
+    }
+}
diff --git a/tempest/api_schema/response/compute/v2_1/quota_classes.py b/tempest/api_schema/response/compute/v2_1/quota_classes.py
index a7374df..a0cdaf5 100644
--- a/tempest/api_schema/response/compute/v2_1/quota_classes.py
+++ b/tempest/api_schema/response/compute/v2_1/quota_classes.py
@@ -20,12 +20,12 @@
 # NOTE(mriedem): os-quota-class-sets responses are the same as os-quota-sets
 # except for the key in the response body is quota_class_set instead of
 # quota_set, so update this copy of the schema from os-quota-sets.
-quota_set = copy.deepcopy(quotas.quota_set)
-quota_set['response_body']['properties']['quota_class_set'] = (
-    quota_set['response_body']['properties'].pop('quota_set'))
-quota_set['response_body']['required'] = ['quota_class_set']
+get_quota_class_set = copy.deepcopy(quotas.get_quota_set)
+get_quota_class_set['response_body']['properties']['quota_class_set'] = (
+    get_quota_class_set['response_body']['properties'].pop('quota_set'))
+get_quota_class_set['response_body']['required'] = ['quota_class_set']
 
-quota_set_update = copy.deepcopy(quotas.quota_set_update)
-quota_set_update['response_body']['properties']['quota_class_set'] = (
-    quota_set_update['response_body']['properties'].pop('quota_set'))
-quota_set_update['response_body']['required'] = ['quota_class_set']
+update_quota_class_set = copy.deepcopy(quotas.update_quota_set)
+update_quota_class_set['response_body']['properties']['quota_class_set'] = (
+    update_quota_class_set['response_body']['properties'].pop('quota_set'))
+update_quota_class_set['response_body']['required'] = ['quota_class_set']
diff --git a/tempest/api_schema/response/compute/v2_1/quotas.py b/tempest/api_schema/response/compute/v2_1/quotas.py
index 630b227..9141f7e 100644
--- a/tempest/api_schema/response/compute/v2_1/quotas.py
+++ b/tempest/api_schema/response/compute/v2_1/quotas.py
@@ -14,34 +14,49 @@
 
 import copy
 
-from tempest.api_schema.response.compute import quotas
+update_quota_set = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'quota_set': {
+                'type': 'object',
+                'properties': {
+                    'instances': {'type': 'integer'},
+                    'cores': {'type': 'integer'},
+                    'ram': {'type': 'integer'},
+                    'floating_ips': {'type': 'integer'},
+                    'fixed_ips': {'type': 'integer'},
+                    'metadata_items': {'type': 'integer'},
+                    'key_pairs': {'type': 'integer'},
+                    'security_groups': {'type': 'integer'},
+                    'security_group_rules': {'type': 'integer'},
+                    'server_group_members': {'type': 'integer'},
+                    'server_groups': {'type': 'integer'},
+                    'injected_files': {'type': 'integer'},
+                    'injected_file_content_bytes': {'type': 'integer'},
+                    'injected_file_path_bytes': {'type': 'integer'}
+                },
+                # NOTE: server_group_members and server_groups are represented
+                # when enabling quota_server_group extension. So they should
+                # not be required.
+                'required': ['instances', 'cores', 'ram',
+                             'floating_ips', 'fixed_ips',
+                             'metadata_items', 'key_pairs',
+                             'security_groups', 'security_group_rules',
+                             'injected_files', 'injected_file_content_bytes',
+                             'injected_file_path_bytes']
+            }
+        },
+        'required': ['quota_set']
+    }
+}
 
-quota_set = copy.deepcopy(quotas.common_quota_set)
-quota_set['response_body']['properties']['quota_set']['properties'][
+get_quota_set = copy.deepcopy(update_quota_set)
+get_quota_set['response_body']['properties']['quota_set']['properties'][
     'id'] = {'type': 'string'}
-quota_set['response_body']['properties']['quota_set']['properties'][
-    'injected_files'] = {'type': 'integer'}
-quota_set['response_body']['properties']['quota_set']['properties'][
-    'injected_file_content_bytes'] = {'type': 'integer'}
-quota_set['response_body']['properties']['quota_set']['properties'][
-    'injected_file_path_bytes'] = {'type': 'integer'}
-quota_set['response_body']['properties']['quota_set']['required'].extend([
-    'id',
-    'injected_files',
-    'injected_file_content_bytes',
-    'injected_file_path_bytes'])
-
-quota_set_update = copy.deepcopy(quotas.common_quota_set)
-quota_set_update['response_body']['properties']['quota_set']['properties'][
-    'injected_files'] = {'type': 'integer'}
-quota_set_update['response_body']['properties']['quota_set']['properties'][
-    'injected_file_content_bytes'] = {'type': 'integer'}
-quota_set_update['response_body']['properties']['quota_set']['properties'][
-    'injected_file_path_bytes'] = {'type': 'integer'}
-quota_set_update['response_body']['properties']['quota_set'][
-    'required'].extend(['injected_files',
-                        'injected_file_content_bytes',
-                        'injected_file_path_bytes'])
+get_quota_set['response_body']['properties']['quota_set']['required'].extend([
+    'id'])
 
 delete_quota = {
     'status_code': [202]
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index fec3bd4..f84771f 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -77,7 +77,8 @@
     - name: javelin_cirros
       owner: javelin
       file: cirros-0.3.2-x86_64-blank.img
-      format: ami
+      disk_format: ami
+      container_format: ami
       aki: cirros-0.3.2-x86_64-vmlinuz
       ari: cirros-0.3.2-x86_64-initrd
 
@@ -629,6 +630,15 @@
     for image in images:
         client = client_for_user(image['owner'])
 
+        # DEPRECATED: 'format' was used for ami images
+        # Use 'disk_format' and 'container_format' instead
+        if 'format' in image:
+            LOG.warning("Deprecated: 'format' is deprecated for images "
+                        "description. Please use 'disk_format' and 'container_"
+                        "format' instead.")
+            image['disk_format'] = image['format']
+            image['container_format'] = image['format']
+
         # only upload a new image if the name isn't there
         if _get_image_by_name(client, image['name']):
             LOG.info("Image '%s' already exists" % image['name'])
@@ -636,7 +646,7 @@
 
         # special handling for 3 part image
         extras = {}
-        if image['format'] == 'ami':
+        if image['disk_format'] == 'ami':
             name, fname = _resolve_image(image, 'aki')
             aki = client.images.create_image(
                 'javelin_' + name, 'aki', 'aki')
@@ -651,7 +661,8 @@
 
         _, fname = _resolve_image(image, 'file')
         body = client.images.create_image(
-            image['name'], image['format'], image['format'], **extras)
+            image['name'], image['container_format'],
+            image['disk_format'], **extras)
         image_id = body.get('id')
         client.images.store_image(image_id, open(fname, 'r'))
 
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 3c71e07..b61f286 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -24,6 +24,7 @@
 from six import moves
 
 from tempest import clients
+from tempest.common import credentials
 from tempest import config
 
 
@@ -254,7 +255,11 @@
     }
     # Get catalog list for endpoints to use for validation
     _token, auth_data = os.auth_provider.get_auth()
-    for entry in auth_data['serviceCatalog']:
+    if os.auth_version == 'v2':
+        catalog_key = 'serviceCatalog'
+    else:
+        catalog_key = 'catalog'
+    for entry in auth_data[catalog_key]:
         services.append(entry['type'])
     # Pull all catalog types from config file and compare against endpoint list
     for cfgname in dir(CONF._config):
@@ -329,7 +334,8 @@
         CONF_PARSER = moves.configparser.SafeConfigParser()
         CONF_PARSER.optionxform = str
         CONF_PARSER.readfp(conf_file)
-    os = clients.Manager()
+    icreds = credentials.get_isolated_credentials('verify_tempest_config')
+    os = clients.Manager(icreds.get_primary_creds())
     services = check_service_availability(os, update)
     results = {}
     for service in ['nova', 'cinder', 'neutron', 'swift']:
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index 6d376d6..acf6d4f 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -19,7 +19,9 @@
 from oslo_log import log as logging
 import yaml
 
+from tempest import clients
 from tempest.common import cred_provider
+from tempest.common import fixed_network
 from tempest import config
 from tempest import exceptions
 
@@ -60,15 +62,18 @@
 
     @classmethod
     def get_hash_dict(cls, accounts):
-        hash_dict = {'roles': {}, 'creds': {}}
+        hash_dict = {'roles': {}, 'creds': {}, 'networks': {}}
         # Loop over the accounts read from the yaml file
         for account in accounts:
             roles = []
             types = []
+            resources = []
             if 'roles' in account:
                 roles = account.pop('roles')
             if 'types' in account:
                 types = account.pop('types')
+            if 'resources' in account:
+                resources = account.pop('resources')
             temp_hash = hashlib.md5()
             temp_hash.update(str(account))
             temp_hash_key = temp_hash.hexdigest()
@@ -91,6 +96,13 @@
                         CONF.object_storage.reseller_admin_role,
                         temp_hash_key,
                         hash_dict)
+            # Populate the network subdict
+            for resource in resources:
+                if resource == 'network':
+                    hash_dict['networks'][temp_hash_key] = resources[resource]
+                else:
+                    LOG.warning('Unkown resource type %s, ignoring this field'
+                                % resource)
         return hash_dict
 
     def is_multi_user(self):
@@ -174,7 +186,7 @@
                 "Account file %s doesn't exist" % CONF.auth.test_accounts_file)
         useable_hashes = self._get_match_hash_list(roles)
         free_hash = self._get_free_hash(useable_hashes)
-        return self.hash_dict['creds'][free_hash]
+        return self._wrap_creds_with_network(free_hash)
 
     @lockutils.synchronized('test_accounts_io', external=True)
     def remove_hash(self, hash_string):
@@ -190,8 +202,15 @@
     def get_hash(self, creds):
         for _hash in self.hash_dict['creds']:
             # Comparing on the attributes that are expected in the YAML
-            if all([getattr(creds, k) == self.hash_dict['creds'][_hash][k] for
-                   k in creds.get_init_attributes()]):
+            init_attributes = creds.get_init_attributes()
+            hash_attributes = self.hash_dict['creds'][_hash].copy()
+            if ('user_domain_name' in init_attributes and 'user_domain_name'
+                    not in hash_attributes):
+                # Allow for the case of domain_name populated from config
+                domain_name = CONF.identity.admin_domain_name
+                hash_attributes['user_domain_name'] = domain_name
+            if all([getattr(creds, k) == hash_attributes[k] for
+                   k in init_attributes]):
                 return _hash
         raise AttributeError('Invalid credentials %s' % creds)
 
@@ -202,20 +221,16 @@
     def get_primary_creds(self):
         if self.isolated_creds.get('primary'):
             return self.isolated_creds.get('primary')
-        creds = self._get_creds()
-        primary_credential = cred_provider.get_credentials(
-            identity_version=self.identity_version, **creds)
-        self.isolated_creds['primary'] = primary_credential
-        return primary_credential
+        net_creds = self._get_creds()
+        self.isolated_creds['primary'] = net_creds
+        return net_creds
 
     def get_alt_creds(self):
         if self.isolated_creds.get('alt'):
             return self.isolated_creds.get('alt')
-        creds = self._get_creds()
-        alt_credential = cred_provider.get_credentials(
-            identity_version=self.identity_version, **creds)
-        self.isolated_creds['alt'] = alt_credential
-        return alt_credential
+        net_creds = self._get_creds()
+        self.isolated_creds['alt'] = net_creds
+        return net_creds
 
     def get_creds_by_roles(self, roles, force_new=False):
         roles = list(set(roles))
@@ -228,11 +243,9 @@
         elif exist_creds and force_new:
             new_index = str(roles) + '-' + str(len(self.isolated_creds))
             self.isolated_creds[new_index] = exist_creds
-        creds = self._get_creds(roles=roles)
-        role_credential = cred_provider.get_credentials(
-            identity_version=self.identity_version, **creds)
-        self.isolated_creds[str(roles)] = role_credential
-        return role_credential
+        net_creds = self._get_creds(roles=roles)
+        self.isolated_creds[str(roles)] = net_creds
+        return net_creds
 
     def clear_isolated_creds(self):
         for creds in self.isolated_creds.values():
@@ -252,6 +265,19 @@
     def admin_available(self):
         return self.is_role_available(CONF.identity.admin_role)
 
+    def _wrap_creds_with_network(self, hash):
+        creds_dict = self.hash_dict['creds'][hash]
+        credential = cred_provider.get_credentials(
+            identity_version=self.identity_version, **creds_dict)
+        net_creds = cred_provider.TestResources(credential)
+        net_clients = clients.Manager(credentials=credential)
+        compute_network_client = net_clients.networks_client
+        net_name = self.hash_dict['networks'].get(hash, None)
+        network = fixed_network.get_network_from_name(
+            net_name, compute_network_client)
+        net_creds.set_resources(network=network)
+        return net_creds
+
 
 class NotLockingAccounts(Accounts):
     """Credentials provider which always returns the first and second
@@ -282,8 +308,9 @@
             return self.isolated_creds.get('primary')
         primary_credential = cred_provider.get_configured_credentials(
             credential_type='user', identity_version=self.identity_version)
-        self.isolated_creds['primary'] = primary_credential
-        return primary_credential
+        self.isolated_creds['primary'] = cred_provider.TestResources(
+            primary_credential)
+        return self.isolated_creds['primary']
 
     def get_alt_creds(self):
         if self.isolated_creds.get('alt'):
@@ -291,8 +318,9 @@
         alt_credential = cred_provider.get_configured_credentials(
             credential_type='alt_user',
             identity_version=self.identity_version)
-        self.isolated_creds['alt'] = alt_credential
-        return alt_credential
+        self.isolated_creds['alt'] = cred_provider.TestResources(
+            alt_credential)
+        return self.isolated_creds['alt']
 
     def clear_isolated_creds(self):
         self.isolated_creds = {}
@@ -300,8 +328,8 @@
     def get_admin_creds(self):
         creds = cred_provider.get_configured_credentials(
             "identity_admin", fill_in=False)
-        self.isolated_creds['admin'] = creds
-        return creds
+        self.isolated_creds['admin'] = cred_provider.TestResources(creds)
+        return self.isolated_creds['admin']
 
     def get_creds_by_roles(self, roles, force_new=False):
         msg = "Credentials being specified through the config file can not be"\
diff --git a/tempest/common/cred_provider.py b/tempest/common/cred_provider.py
index 2c6763d..3223027 100644
--- a/tempest/common/cred_provider.py
+++ b/tempest/common/cred_provider.py
@@ -84,6 +84,8 @@
         domain_fields = set(x for x in auth.KeystoneV3Credentials.ATTRIBUTES
                             if 'domain' in x)
         if not domain_fields.intersection(kwargs.keys()):
+            # TODO(andreaf) It might be better here to use a dedicated config
+            # option such as CONF.auth.tenant_isolation_domain_name
             params['user_domain_name'] = CONF.identity.admin_domain_name
         auth_url = CONF.identity.uri_v3
     else:
diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py
index 67fbab1..1557474 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -13,17 +13,70 @@
 import copy
 from oslo_log import log as logging
 
+from tempest_lib.common.utils import misc as misc_utils
 from tempest_lib import exceptions as lib_exc
 
-from tempest.common import isolated_creds
 from tempest import config
-from tempest import exceptions
 
 CONF = config.CONF
 
 LOG = logging.getLogger(__name__)
 
 
+def get_network_from_name(name, compute_networks_client):
+    """Get a full network dict from just a network name
+
+    :param str name: the name of the network to use
+    :param NetworksClientJSON compute_networks_client: The network client
+        object to use for making the network lists api request
+    :return: The full dictionary for the network in question, unless the
+        network for the supplied name can not be found. In which case a dict
+        with just the name will be returned.
+    :rtype: dict
+    """
+    caller = misc_utils.find_test_caller()
+    if not name:
+        network = {'name': name}
+    else:
+        try:
+            resp = compute_networks_client.list_networks(name=name)
+            if isinstance(resp, list):
+                networks = resp
+            elif isinstance(resp, dict):
+                networks = resp['networks']
+            else:
+                raise lib_exc.NotFound()
+            if len(networks) > 0:
+                network = networks[0]
+            else:
+                msg = "Network with name: %s not found" % name
+                if caller:
+                    LOG.warn('(%s) %s' % (caller, msg))
+                else:
+                    LOG.warn(msg)
+                raise lib_exc.NotFound()
+            # To be consistent with network isolation, add name is only
+            # label is available
+            name = network.get('name', network.get('label'))
+            if name:
+                network['name'] = name
+            else:
+                raise lib_exc.NotFound()
+        except lib_exc.NotFound:
+            # In case of nova network, if the fixed_network_name is not
+            # owned by the tenant, and the network client is not an admin
+            # one, list_networks will not find it
+            msg = ('Unable to find network %s. '
+                   'Starting instance without specifying a network.' %
+                   name)
+            if caller:
+                LOG.info('(%s) %s' % (caller, msg))
+            else:
+                LOG.info(msg)
+            network = {'name': name}
+    return network
+
+
 def get_tenant_network(creds_provider, compute_networks_client):
     """Get a network usable by the primary tenant
 
@@ -35,43 +88,25 @@
            is the network to be used, and it's not visible to the tenant
     :return a dict with 'id' and 'name' of the network
     """
+    caller = misc_utils.find_test_caller()
     fixed_network_name = CONF.compute.fixed_network_name
-    network = None
-    # NOTE(andreaf) get_primary_network will always be available once
-    # bp test-accounts-continued is implemented
-    if (isinstance(creds_provider, isolated_creds.IsolatedCreds) and
-        (CONF.service_available.neutron and
-         not CONF.service_available.ironic)):
-        # tenant_allow_isolation == True, so network is defined
-        network = creds_provider.get_primary_creds().network
-    else:
+    net_creds = creds_provider.get_primary_creds()
+    network = getattr(net_creds, 'network', None)
+    if not network or not network.get('name'):
         if fixed_network_name:
-            try:
-                resp = compute_networks_client.list_networks(
-                    name=fixed_network_name)
-                if isinstance(resp, list):
-                    networks = resp
-                elif isinstance(resp, dict):
-                    networks = resp['networks']
-                else:
-                    raise lib_exc.NotFound()
-                if len(networks) > 0:
-                    network = networks[0]
-                else:
-                    msg = "Configured fixed_network_name not found"
-                    raise exceptions.InvalidConfiguration(msg)
-                # To be consistent with network isolation, add name is only
-                # label is available
-                network['name'] = network.get('name', network.get('label'))
-            except lib_exc.NotFound:
-                # In case of nova network, if the fixed_network_name is not
-                # owned by the tenant, and the network client is not an admin
-                # one, list_networks will not find it
-                LOG.info('Unable to find network %s. '
-                         'Starting instance without specifying a network.' %
-                         fixed_network_name)
-                network = {'name': fixed_network_name}
-    LOG.info('Found network %s available for tenant' % network)
+            msg = ('No valid network provided or created, defaulting to '
+                   'fixed_network_name')
+            if caller:
+                LOG.debug('(%s) %s' % (caller, msg))
+            else:
+                LOG.debug(msg)
+            network = get_network_from_name(fixed_network_name,
+                                            compute_networks_client)
+    msg = ('Found network %s available for tenant' % network)
+    if caller:
+        LOG.info('(%s) %s' % (caller, msg))
+    else:
+        LOG.info(msg)
     return network
 
 
@@ -87,5 +122,9 @@
         return params
 
     if network:
-        params.update({"networks": [{'uuid': network['id']}]})
+        if 'id' in network.keys():
+            params.update({"networks": [{'uuid': network['id']}]})
+        else:
+            LOG.warn('The provided network dict: %s was invalid and did not '
+                     ' contain an id' % network)
     return params
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index b19faef..29fb493 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -87,10 +87,11 @@
         cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
         return self.exec_command(cmd)
 
-    def ping_host(self, host):
+    def ping_host(self, host, count=CONF.compute.ping_count,
+                  size=CONF.compute.ping_size):
         addr = netaddr.IPAddress(host)
         cmd = 'ping6' if addr.version == 6 else 'ping'
-        cmd += ' -c1 -w1 {0}'.format(host)
+        cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
         return self.exec_command(cmd)
 
     def get_mac_address(self):
diff --git a/tempest/config.py b/tempest/config.py
index 2ed1103..bcbe41f 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -187,10 +187,6 @@
                default="root",
                help="User name used to authenticate to an instance using "
                     "the alternate image."),
-    cfg.StrOpt('image_alt_ssh_password',
-               default="password",
-               help="Password used to authenticate to an instance using "
-                    "the alternate image."),
     cfg.IntOpt('build_interval',
                default=1,
                help="Time in seconds between build status checks."),
@@ -205,16 +201,17 @@
     cfg.StrOpt('ssh_auth_method',
                default='keypair',
                help="Auth method used for authenticate to the instance. "
-                    "Valid choices are: keypair, configured, adminpass. "
-                    "keypair: start the servers with an ssh keypair. "
-                    "configured: use the configured user and password. "
-                    "adminpass: use the injected adminPass. "
-                    "disabled: avoid using ssh when it is an option."),
+                    "Valid choices are: keypair, configured, adminpass "
+                    "and disabled. "
+                    "Keypair: start the servers with a ssh keypair. "
+                    "Configured: use the configured user and password. "
+                    "Adminpass: use the injected adminPass. "
+                    "Disabled: avoid using ssh when it is an option."),
     cfg.StrOpt('ssh_connect_method',
-               default='fixed',
+               default='floating',
                help="How to connect to the instance? "
                     "fixed: using the first ip belongs the fixed network "
-                    "floating: creating and using a floating ip"),
+                    "floating: creating and using a floating ip."),
     cfg.StrOpt('ssh_user',
                default='root',
                help="User name used to authenticate to an instance."),
@@ -222,6 +219,14 @@
                default=120,
                help="Timeout in seconds to wait for ping to "
                     "succeed."),
+    cfg.IntOpt('ping_size',
+               default=56,
+               help="The packet size for ping packets originating "
+                    "from remote linux hosts"),
+    cfg.IntOpt('ping_count',
+               default=1,
+               help="The number of ping packets originating from remote "
+                    "linux hosts"),
     cfg.IntOpt('ssh_timeout',
                default=300,
                help="Timeout in seconds to wait for authentication to "
@@ -239,7 +244,8 @@
                     "tenants. If multiple networks are available for a tenant"
                     " this is the network which will be used for creating "
                     "servers if tempest does not create a network or a "
-                    "network is not specified elsewhere"),
+                    "network is not specified elsewhere. It may be used for "
+                    "ssh validation only if floating IPs are disabled."),
     cfg.StrOpt('network_for_ssh',
                default='public',
                help="Network used for SSH connections. Ignored if "
@@ -264,9 +270,6 @@
                choices=['public', 'admin', 'internal',
                         'publicURL', 'adminURL', 'internalURL'],
                help="The endpoint type to use for the compute service."),
-    cfg.StrOpt('path_to_private_key',
-               help="Path to a private key file for SSH access to remote "
-                    "hosts"),
     cfg.StrOpt('volume_device_name',
                default='vdb',
                help="Expected device name when a volume is attached to "
@@ -449,12 +452,16 @@
                help="The mask bits for tenant ipv6 subnets"),
     cfg.BoolOpt('tenant_networks_reachable',
                 default=False,
-                help="Whether tenant network connectivity should be "
-                     "evaluated directly"),
+                help="Whether tenant networks can be reached directly from "
+                     "the test client. This must be set to True when the "
+                     "'fixed' ssh_connect_method is selected."),
     cfg.StrOpt('public_network_id',
                default="",
                help="Id of the public network that provides external "
                     "connectivity"),
+    cfg.StrOpt('floating_network_name',
+               help="Default floating network name. Used to allocate floating "
+                    "IPs when neutron is enabled."),
     cfg.StrOpt('public_router_id',
                default="",
                help="Id of the public router that provides external "
@@ -540,6 +547,37 @@
                help='The maximum grace period for a claim'),
 ]
 
+validation_group = cfg.OptGroup(name='validation',
+                                title='SSH Validation options')
+
+ValidationGroup = [
+    cfg.StrOpt('connect_method',
+               default='floating',
+               choices=['fixed', 'floating'],
+               help='Default IP type used for validation: '
+                    '-fixed: uses the first IP belonging to the fixed network '
+                    '-floating: creates and uses a floating IP'),
+    cfg.StrOpt('auth_method',
+               default='keypair',
+               choices=['keypair'],
+               help='Default authentication method to the instance. '
+                    'Only ssh via keypair is supported for now. '
+                    'Additional methods will be handled in a separate spec.'),
+    cfg.IntOpt('ip_version_for_ssh',
+               default=4,
+               help='Default IP version for ssh connections.'),
+    cfg.IntOpt('ping_timeout',
+               default=120,
+               help='Timeout in seconds to wait for ping to succeed.'),
+    cfg.IntOpt('connect_timeout',
+               default=60,
+               help='Timeout in seconds to wait for the TCP connection to be '
+                    'successful.'),
+    cfg.IntOpt('ssh_timeout',
+               default=300,
+               help='Timeout in seconds to wait for the ssh banner.'),
+]
+
 volume_group = cfg.OptGroup(name='volume',
                             title='Block Storage Options')
 
@@ -1092,6 +1130,7 @@
     (network_group, NetworkGroup),
     (network_feature_group, NetworkFeaturesGroup),
     (messaging_group, MessagingGroup),
+    (validation_group, ValidationGroup),
     (volume_group, VolumeGroup),
     (volume_feature_group, VolumeFeaturesGroup),
     (object_storage_group, ObjectStoreGroup),
@@ -1152,6 +1191,7 @@
         self.image_feature_enabled = _CONF['image-feature-enabled']
         self.network = _CONF.network
         self.network_feature_enabled = _CONF['network-feature-enabled']
+        self.validation = _CONF.validation
         self.volume = _CONF.volume
         self.volume_feature_enabled = _CONF['volume-feature-enabled']
         self.object_storage = _CONF['object-storage']
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 65d516f..d2c41f0 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -309,8 +309,13 @@
         if isinstance(server_or_ip, six.string_types):
             ip = server_or_ip
         else:
-            addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0]
-            ip = addr['addr']
+            addrs = server_or_ip['addresses'][CONF.compute.network_for_ssh]
+            try:
+                ip = (addr['addr'] for addr in addrs if
+                      netaddr.valid_ipv4(addr['addr'])).next()
+            except StopIteration:
+                raise lib_exc.NotFound("No IPv4 addresses to use for SSH to "
+                                       "remote server.")
 
         if username is None:
             username = CONF.scenario.ssh_user
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 88c502f..b97ad0b 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -318,11 +318,15 @@
             LOG.info(msg)
             return
 
-        subnet = self._list_subnets(
-            network_id=CONF.network.public_network_id)
-        self.assertEqual(1, len(subnet), "Found %d subnets" % len(subnet))
+        # We ping the external IP from the instance using its floating IP
+        # which is always IPv4, so we must only test connectivity to
+        # external IPv4 IPs if the external network is dualstack.
+        v4_subnets = [s for s in self._list_subnets(
+            network_id=CONF.network.public_network_id) if s['ip_version'] == 4]
+        self.assertEqual(1, len(v4_subnets),
+                         "Found %d IPv4 subnets" % len(v4_subnets))
 
-        external_ips = [subnet[0]['gateway_ip']]
+        external_ips = [v4_subnets[0]['gateway_ip']]
         self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
                                         external_ips)
 
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 25b1869..2de43cf 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -16,11 +16,10 @@
 import json
 import urllib
 
-from tempest.api_schema.response.compute import flavors as common_schema
 from tempest.api_schema.response.compute import flavors_access as schema_access
 from tempest.api_schema.response.compute import flavors_extra_specs \
     as schema_extra_specs
-from tempest.api_schema.response.compute.v2_1 import flavors as v2schema
+from tempest.api_schema.response.compute.v2_1 import flavors as schema
 from tempest.common import service_client
 
 
@@ -33,7 +32,7 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(common_schema.list_flavors, resp, body)
+        self.validate_response(schema.list_flavors, resp, body)
         return service_client.ResponseBodyList(resp, body['flavors'])
 
     def list_flavors_with_detail(self, params=None):
@@ -43,13 +42,13 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(v2schema.list_flavors_details, resp, body)
+        self.validate_response(schema.list_flavors_details, resp, body)
         return service_client.ResponseBodyList(resp, body['flavors'])
 
     def get_flavor_details(self, flavor_id):
         resp, body = self.get("flavors/%s" % str(flavor_id))
         body = json.loads(body)
-        self.validate_response(v2schema.create_get_flavor_details, resp, body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
         return service_client.ResponseBody(resp, body['flavor'])
 
     def create_flavor(self, name, ram, vcpus, disk, flavor_id, **kwargs):
@@ -73,13 +72,13 @@
         resp, body = self.post('flavors', post_body)
 
         body = json.loads(body)
-        self.validate_response(v2schema.create_get_flavor_details, resp, body)
+        self.validate_response(schema.create_get_flavor_details, resp, body)
         return service_client.ResponseBody(resp, body['flavor'])
 
     def delete_flavor(self, flavor_id):
         """Deletes the given flavor."""
         resp, body = self.delete("flavors/{0}".format(flavor_id))
-        self.validate_response(v2schema.delete_flavor, resp, body)
+        self.validate_response(schema.delete_flavor, resp, body)
         return service_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
@@ -137,7 +136,7 @@
         """Unsets extra Specs from the mentioned flavor."""
         resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
                                  (str(flavor_id), key))
-        self.validate_response(v2schema.unset_flavor_extra_specs, resp, body)
+        self.validate_response(schema.unset_flavor_extra_specs, resp, body)
         return service_client.ResponseBody(resp, body)
 
     def list_flavor_access(self, flavor_id):
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index de925a9..088e695 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -15,8 +15,7 @@
 import json
 import urllib
 
-from tempest.api_schema.response.compute import hosts as schema
-from tempest.api_schema.response.compute.v2_1 import hosts as v2_schema
+from tempest.api_schema.response.compute.v2_1 import hosts as schema
 from tempest.common import service_client
 
 
@@ -39,7 +38,7 @@
 
         resp, body = self.get("os-hosts/%s" % str(hostname))
         body = json.loads(body)
-        self.validate_response(schema.show_host_detail, resp, body)
+        self.validate_response(schema.get_host_detail, resp, body)
         return service_client.ResponseBodyList(resp, body['host'])
 
     def update_host(self, hostname, **kwargs):
@@ -54,7 +53,7 @@
 
         resp, body = self.put("os-hosts/%s" % str(hostname), request_body)
         body = json.loads(body)
-        self.validate_response(v2_schema.update_host, resp, body)
+        self.validate_response(schema.update_host, resp, body)
         return service_client.ResponseBody(resp, body)
 
     def startup_host(self, hostname):
@@ -62,7 +61,7 @@
 
         resp, body = self.get("os-hosts/%s/startup" % str(hostname))
         body = json.loads(body)
-        self.validate_response(v2_schema.startup_host, resp, body)
+        self.validate_response(schema.startup_host, resp, body)
         return service_client.ResponseBody(resp, body['host'])
 
     def shutdown_host(self, hostname):
@@ -70,7 +69,7 @@
 
         resp, body = self.get("os-hosts/%s/shutdown" % str(hostname))
         body = json.loads(body)
-        self.validate_response(v2_schema.shutdown_host, resp, body)
+        self.validate_response(schema.shutdown_host, resp, body)
         return service_client.ResponseBody(resp, body['host'])
 
     def reboot_host(self, hostname):
@@ -78,5 +77,5 @@
 
         resp, body = self.get("os-hosts/%s/reboot" % str(hostname))
         body = json.loads(body)
-        self.validate_response(v2_schema.reboot_host, resp, body)
+        self.validate_response(schema.reboot_host, resp, body)
         return service_client.ResponseBody(resp, body['host'])
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index 722aefa..7fe335b 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -15,7 +15,6 @@
 
 import json
 
-from tempest.api_schema.response.compute import keypairs as common_schema
 from tempest.api_schema.response.compute.v2_1 import keypairs as schema
 from tempest.common import service_client
 
@@ -30,7 +29,7 @@
         # servers, etc. A bug?
         # For now we shall adhere to the spec, but the spec for keypairs
         # is yet to be found
-        self.validate_response(common_schema.list_keypairs, resp, body)
+        self.validate_response(schema.list_keypairs, resp, body)
         return service_client.ResponseBodyList(resp, body['keypairs'])
 
     def get_keypair(self, key_name):
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index 89f4acd..6e38c47 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -31,7 +31,7 @@
             url += '?user_id=%s' % str(user_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.quota_set, resp, body)
+        self.validate_response(schema.get_quota_set, resp, body)
         return service_client.ResponseBody(resp, body['quota_set'])
 
     def get_default_quota_set(self, tenant_id):
@@ -40,7 +40,7 @@
         url = 'os-quota-sets/%s/defaults' % str(tenant_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(schema.quota_set, resp, body)
+        self.validate_response(schema.get_quota_set, resp, body)
         return service_client.ResponseBody(resp, body['quota_set'])
 
     def update_quota_set(self, tenant_id, user_id=None,
@@ -105,7 +105,7 @@
                                   post_body)
 
         body = json.loads(body)
-        self.validate_response(schema.quota_set_update, resp, body)
+        self.validate_response(schema.update_quota_set, resp, body)
         return service_client.ResponseBody(resp, body['quota_set'])
 
     def delete_quota_set(self, tenant_id):
@@ -123,7 +123,7 @@
         url = 'os-quota-class-sets/%s' % str(quota_class_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.validate_response(classes_schema.quota_set, resp, body)
+        self.validate_response(classes_schema.get_quota_class_set, resp, body)
         return service_client.ResponseBody(resp, body['quota_class_set'])
 
     def update_quota_class_set(self, quota_class_id, **kwargs):
@@ -136,5 +136,6 @@
                               post_body)
 
         body = json.loads(body)
-        self.validate_response(classes_schema.quota_set_update, resp, body)
+        self.validate_response(classes_schema.update_quota_class_set,
+                               resp, body)
         return service_client.ResponseBody(resp, body['quota_class_set'])
diff --git a/tempest/tests/common/test_accounts.py b/tempest/tests/common/test_accounts.py
index 6371e49..b4048ba 100644
--- a/tempest/tests/common/test_accounts.py
+++ b/tempest/tests/common/test_accounts.py
@@ -23,11 +23,13 @@
 
 from tempest import auth
 from tempest.common import accounts
+from tempest.common import cred_provider
 from tempest import config
 from tempest import exceptions
 from tempest.services.identity.v2.json import token_client
 from tempest.tests import base
 from tempest.tests import fake_config
+from tempest.tests import fake_http
 from tempest.tests import fake_identity
 
 
@@ -37,6 +39,9 @@
         super(TestAccount, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+        self.fake_http = fake_http.fake_httplib2(return_type=200)
+        self.stubs.Set(token_client.TokenClientJSON, 'raw_request',
+                       fake_identity._fake_v2_response)
         self.useFixture(lockutils_fixtures.ExternalLockFixture())
         self.test_accounts = [
             {'username': 'test_user1', 'tenant_name': 'test_tenant1',
@@ -64,7 +69,7 @@
             {'username': 'test_user12', 'tenant_name': 'test_tenant12',
              'password': 'p', 'roles': [cfg.CONF.identity.admin_role]},
         ]
-        self.useFixture(mockpatch.Patch(
+        self.accounts_mock = self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
             return_value=self.test_accounts))
         cfg.CONF.set_default('test_accounts_file', 'fake_path', group='auth')
@@ -275,6 +280,31 @@
         for i in admin_hashes:
             self.assertNotIn(i, args)
 
+    def test_networks_returned_with_creds(self):
+        test_accounts = [
+            {'username': 'test_user13', 'tenant_name': 'test_tenant13',
+             'password': 'p', 'resources': {'network': 'network-1'}},
+            {'username': 'test_user14', 'tenant_name': 'test_tenant14',
+             'password': 'p', 'roles': ['role-7', 'role-11'],
+             'resources': {'network': 'network-2'}}]
+        # Clear previous mock using self.test_accounts
+        self.accounts_mock.cleanUp()
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.accounts.read_accounts_yaml',
+            return_value=test_accounts))
+        test_accounts_class = accounts.Accounts('v2', 'test_name')
+        with mock.patch('tempest.services.compute.json.networks_client.'
+                        'NetworksClientJSON.list_networks',
+                        return_value=[{'name': 'network-2', 'id': 'fake-id'}]):
+            creds = test_accounts_class.get_creds_by_roles(['role-7'])
+        self.assertTrue(isinstance(creds, cred_provider.TestResources))
+        network = creds.network
+        self.assertIsNotNone(network)
+        self.assertIn('name', network)
+        self.assertIn('id', network)
+        self.assertEqual('fake-id', network['id'])
+        self.assertEqual('network-2', network['name'])
+
 
 class TestNotLockingAccount(base.TestCase):
 
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 40b7b32..d6377e6 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -100,15 +100,17 @@
         self._assert_exec_called_with('cut -f1 -d. /proc/uptime')
 
     def test_ping_host(self):
-        ping_response = """PING localhost (127.0.0.1) 56(84) bytes of data.
-64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.048 ms
+        ping_response = """PING localhost (127.0.0.1) 70(98) bytes of data.
+78 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.048 ms
+78 bytes from localhost (127.0.0.1): icmp_req=2 ttl=64 time=0.048 ms
 
 --- localhost ping statistics ---
-1 packets transmitted, 1 received, 0% packet loss, time 0ms
+2 packets transmitted, 2 received, 0% packet loss, time 0ms
 rtt min/avg/max/mdev = 0.048/0.048/0.048/0.000 ms"""
         self.ssh_mock.mock.exec_command.return_value = ping_response
-        self.assertEqual(self.conn.ping_host('127.0.0.1'), ping_response)
-        self._assert_exec_called_with('ping -c1 -w1 127.0.0.1')
+        self.assertEqual(self.conn.ping_host('127.0.0.1', count=2, size=70),
+                         ping_response)
+        self._assert_exec_called_with('ping -c2 -w2 -s70 127.0.0.1')
 
     def test_get_mac_address(self):
         macs = """0a:0b:0c:0d:0e:0f