Add network support to the accounts providers

This commits adds support for specifying the network to use with a
user/tenant into the accounts.yaml file. You can only specify a single
network which will be the network used for all that requires a
pre-existing network. This also means regardless of which cred provider
is configured the fixed_network can assume a TestResource object will
be returned from a get_creds call. As part of this change a common
method to return the full network dict from a just a network name is
abstracted out into tempest.common.fixed_network module since this
same method is needed to have the accounts file provide a network by
name.

Partially-implements: bp test-accounts-continued
Change-Id: I6f5ac1239d18f2935847b385a08de608f40fdda5
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/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/common/accounts.py b/tempest/common/accounts.py
index 1d5516f..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):
@@ -209,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))
@@ -235,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():
@@ -259,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
@@ -289,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'):
@@ -298,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 = {}
@@ -307,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/fixed_network.py b/tempest/common/fixed_network.py
index b2d7c0b..1557474 100644
--- a/tempest/common/fixed_network.py
+++ b/tempest/common/fixed_network.py
@@ -16,15 +16,67 @@
 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
 
@@ -38,15 +90,9 @@
     """
     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:
             msg = ('No valid network provided or created, defaulting to '
                    'fixed_network_name')
@@ -54,41 +100,8 @@
                 LOG.debug('(%s) %s' % (caller, msg))
             else:
                 LOG.debug(msg)
-            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"
-                    if caller:
-                        msg = '(%s) %s' % (caller, msg)
-                    raise exceptions.InvalidConfiguration(msg)
-                # 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.' %
-                       fixed_network_name)
-                if caller:
-                    LOG.info('(%s) %s' % (caller, msg))
-                else:
-                    LOG.info(msg)
-                network = {'name': fixed_network_name}
+            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))
diff --git a/tempest/tests/common/test_accounts.py b/tempest/tests/common/test_accounts.py
index 6371e49..b176675 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',
@@ -63,6 +68,11 @@
              'password': 'p', 'roles': [cfg.CONF.identity.admin_role]},
             {'username': 'test_user12', 'tenant_name': 'test_tenant12',
              'password': 'p', 'roles': [cfg.CONF.identity.admin_role]},
+            {'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'}},
         ]
         self.useFixture(mockpatch.Patch(
             'tempest.common.accounts.read_accounts_yaml',
@@ -271,10 +281,27 @@
         calls = get_free_hash_mock.mock.mock_calls
         self.assertEqual(len(calls), 1)
         args = calls[0][1][0]
-        self.assertEqual(len(args), 10)
+        self.assertEqual(len(args), 12)
         for i in admin_hashes:
             self.assertNotIn(i, args)
 
+    def test_networks_returned_with_creds(self):
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.accounts.read_accounts_yaml',
+            return_value=self.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):