Update export location retrieval APIs

Update the export location retrieval APIs for shares and share
instances to do the following:

1. Restore the API-to-view-builder calling convention of
index-->summary and show-->detail. In so doing, modify which
values are returned by the list commands (currently, all but
the timestamps). The admin context from the request determines
whether the admin-only values are returned.

2. Report the UUID field from the export location table as
'id' to be consistent will all other objects returned via the
Manila REST API.

3. Add the preferred flag to the output of the API. Drivers
can report preferred:True or preferred:False in their export
location metadata, and this standard flag will be returned
via the REST interface, like this:

+-------------------+--------------------------------------+
| Property          | Value                                |
+-------------------+--------------------------------------+
| is_admin_only     | False                                |
| uuid              | df828d44-0b04-47fa-8ee5-516ffc199ca7 |
| share_instance_id | 1b40e873-331e-4e1c-ab53-38ec95b3bfcc |
| path              | 10.0.0.100:/share_1b40e873           |
| created_at        | 2016-02-18T21:12:51.000000           |
| updated_at        | 2016-02-18T21:12:51.000000           |
| preferred         | True                                 |
+-------------------+--------------------------------------+

APIImpact
Implements: blueprint update-export-location-retrieval-apis
Change-Id: Ia63477d4f3e28ab4f53d7b9d51756cc798c977b9
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index c0c38bd..10da4cb 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -36,7 +36,7 @@
                help="The minimum api microversion is configured to be the "
                     "value of the minimum microversion supported by Manila."),
     cfg.StrOpt("max_api_microversion",
-               default="2.13",
+               default="2.14",
                help="The maximum api microversion is configured to be the "
                     "value of the latest microversion supported by Manila."),
     cfg.StrOpt("region",
diff --git a/manila_tempest_tests/tests/api/admin/test_export_locations.py b/manila_tempest_tests/tests/api/admin/test_export_locations.py
index 4791cb0..a7b45a8 100644
--- a/manila_tempest_tests/tests/api/admin/test_export_locations.py
+++ b/manila_tempest_tests/tests/api/admin/test_export_locations.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import ddt
 from oslo_utils import timeutils
 from oslo_utils import uuidutils
 import six
@@ -21,11 +22,14 @@
 
 from manila_tempest_tests import clients_share as clients
 from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
 
 CONF = config.CONF
+LATEST_MICROVERSION = CONF.share.max_api_microversion
 
 
 @base.skip_if_microversion_not_supported("2.9")
+@ddt.ddt
 class ExportLocationsTest(base.BaseSharesAdminTest):
 
     @classmethod
@@ -38,42 +42,82 @@
         cls.share_instances = cls.shares_v2_client.get_instances_of_share(
             cls.share['id'])
 
-    def _verify_export_location_structure(self, export_locations,
-                                          role='admin'):
-        expected_keys = [
-            'created_at', 'updated_at', 'path', 'uuid',
-        ]
-        if role == 'admin':
-            expected_keys.extend(['is_admin_only', 'share_instance_id'])
+    def _verify_export_location_structure(
+            self, export_locations, role='admin', version=LATEST_MICROVERSION,
+            format='summary'):
+
+        # Determine which keys to expect based on role, version and format
+        summary_keys = ['id', 'path']
+        if utils.is_microversion_ge(version, '2.14'):
+            summary_keys += ['preferred']
+
+        admin_summary_keys = summary_keys + [
+            'share_instance_id', 'is_admin_only']
+
+        detail_keys = summary_keys + ['created_at', 'updated_at']
+
+        admin_detail_keys = admin_summary_keys + ['created_at', 'updated_at']
+
+        if format == 'summary':
+            if role == 'admin':
+                expected_keys = admin_summary_keys
+            else:
+                expected_keys = summary_keys
+        else:
+            if role == 'admin':
+                expected_keys = admin_detail_keys
+            else:
+                expected_keys = detail_keys
 
         if not isinstance(export_locations, (list, tuple, set)):
             export_locations = (export_locations, )
 
         for export_location in export_locations:
+
+            # Check that the correct keys are present
             self.assertEqual(len(expected_keys), len(export_location))
             for key in expected_keys:
                 self.assertIn(key, export_location)
+
+            # Check the format of ever-present summary keys
+            self.assertTrue(uuidutils.is_uuid_like(export_location['id']))
+            self.assertTrue(isinstance(export_location['path'],
+                                       six.string_types))
+
+            if utils.is_microversion_ge(version, '2.14'):
+                self.assertIn(export_location['preferred'], (True, False))
+
             if role == 'admin':
                 self.assertIn(export_location['is_admin_only'], (True, False))
-                self.assertTrue(
-                    uuidutils.is_uuid_like(
-                        export_location['share_instance_id']))
-            self.assertTrue(uuidutils.is_uuid_like(export_location['uuid']))
-            self.assertTrue(
-                isinstance(export_location['path'], six.string_types))
-            for time in (export_location['created_at'],
-                         export_location['updated_at']):
-                # If var 'time' has incorrect value then ValueError exception
-                # is expected to be raised. So, just try parse it making
-                # assertion that it has proper date value.
-                timeutils.parse_strtime(time)
+                self.assertTrue(uuidutils.is_uuid_like(
+                    export_location['share_instance_id']))
+
+            # Check the format of the detail keys
+            if format == 'detail':
+                for time in (export_location['created_at'],
+                             export_location['updated_at']):
+                    # If var 'time' has incorrect value then ValueError
+                    # exception is expected to be raised. So, just try parse
+                    # it making assertion that it has proper date value.
+                    timeutils.parse_strtime(time)
 
     @test.attr(type=["gate", ])
+    @utils.skip_if_microversion_not_supported('2.13')
     def test_list_share_export_locations(self):
         export_locations = self.admin_client.list_share_export_locations(
-            self.share['id'])
+            self.share['id'], version='2.13')
 
-        self._verify_export_location_structure(export_locations)
+        self._verify_export_location_structure(export_locations,
+                                               version='2.13')
+
+    @test.attr(type=["gate", ])
+    @utils.skip_if_microversion_not_supported('2.14')
+    def test_list_share_export_locations_with_preferred_flag(self):
+        export_locations = self.admin_client.list_share_export_locations(
+            self.share['id'], version='2.14')
+
+        self._verify_export_location_structure(export_locations,
+                                               version='2.14')
 
     @test.attr(type=["gate", ])
     def test_get_share_export_location(self):
@@ -82,15 +126,15 @@
 
         for export_location in export_locations:
             el = self.admin_client.get_share_export_location(
-                self.share['id'], export_location['uuid'])
-            self._verify_export_location_structure(el)
+                self.share['id'], export_location['id'])
+            self._verify_export_location_structure(el, format='detail')
 
     @test.attr(type=["gate", ])
     def test_list_share_export_locations_by_member(self):
         export_locations = self.member_client.list_share_export_locations(
             self.share['id'])
 
-        self._verify_export_location_structure(export_locations, 'member')
+        self._verify_export_location_structure(export_locations, role='member')
 
     @test.attr(type=["gate", ])
     def test_get_share_export_location_by_member(self):
@@ -101,16 +145,29 @@
             if export_location['is_admin_only']:
                 continue
             el = self.member_client.get_share_export_location(
-                self.share['id'], export_location['uuid'])
-            self._verify_export_location_structure(el, 'member')
+                self.share['id'], export_location['id'])
+            self._verify_export_location_structure(el, role='member',
+                                                   format='detail')
 
     @test.attr(type=["gate", ])
+    @utils.skip_if_microversion_not_supported('2.13')
     def test_list_share_instance_export_locations(self):
         for share_instance in self.share_instances:
             export_locations = (
                 self.admin_client.list_share_instance_export_locations(
-                    share_instance['id']))
-            self._verify_export_location_structure(export_locations)
+                    share_instance['id'], version='2.13'))
+            self._verify_export_location_structure(export_locations,
+                                                   version='2.13')
+
+    @test.attr(type=["gate", ])
+    @utils.skip_if_microversion_not_supported('2.14')
+    def test_list_share_instance_export_locations_with_preferred_flag(self):
+        for share_instance in self.share_instances:
+            export_locations = (
+                self.admin_client.list_share_instance_export_locations(
+                    share_instance['id'], version='2.14'))
+            self._verify_export_location_structure(export_locations,
+                                                   version='2.14')
 
     @test.attr(type=["gate", ])
     def test_get_share_instance_export_location(self):
@@ -120,8 +177,8 @@
                     share_instance['id']))
             for el in export_locations:
                 el = self.admin_client.get_share_instance_export_location(
-                    share_instance['id'], el['uuid'])
-                self._verify_export_location_structure(el)
+                    share_instance['id'], el['id'])
+                self._verify_export_location_structure(el, format='detail')
 
     @test.attr(type=["gate", ])
     def test_share_contains_all_export_locations_of_all_share_instances(self):
@@ -140,6 +197,6 @@
             len(share_instances_export_locations)
         )
         self.assertEqual(
-            sorted(share_export_locations, key=lambda el: el['uuid']),
-            sorted(share_instances_export_locations, key=lambda el: el['uuid'])
+            sorted(share_export_locations, key=lambda el: el['id']),
+            sorted(share_instances_export_locations, key=lambda el: el['id'])
         )
diff --git a/manila_tempest_tests/tests/api/admin/test_export_locations_negative.py b/manila_tempest_tests/tests/api/admin/test_export_locations_negative.py
index 9d53373..fda77b1 100644
--- a/manila_tempest_tests/tests/api/admin/test_export_locations_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_export_locations_negative.py
@@ -90,5 +90,5 @@
                 self.assertRaises(
                     lib_exc.Forbidden,
                     self.member_client.get_share_instance_export_location,
-                    share_instance['id'], el['uuid'],
+                    share_instance['id'], el['id'],
                 )