Merge "Remove _create_port and create_keypair methods"
diff --git a/manila_tempest_tests/common/remote_client.py b/manila_tempest_tests/common/remote_client.py
index 30e64c7..573cdd6 100644
--- a/manila_tempest_tests/common/remote_client.py
+++ b/manila_tempest_tests/common/remote_client.py
@@ -70,8 +70,17 @@
         self.server = server
         self.servers_client = servers_client
         self.log_console = CONF.compute_feature_enabled.console_output
+        kwargs = {}
 
-        self.ssh_client = ssh.Client(ip_address, username, password, pkey=pkey)
+        try:
+            kwargs['ssh_key_type'] = CONF.validation.ssh_key_type
+        except Exception:
+            # Not all versions of tempest support the
+            # "validation.ssh_key_type" config option
+            pass
+
+        self.ssh_client = ssh.Client(
+            ip_address, username, password, pkey=pkey, **kwargs)
 
     @debug_ssh
     def exec_command(self, cmd):
diff --git a/manila_tempest_tests/common/waiters.py b/manila_tempest_tests/common/waiters.py
index 24b30de..8a97c8e 100644
--- a/manila_tempest_tests/common/waiters.py
+++ b/manila_tempest_tests/common/waiters.py
@@ -217,3 +217,31 @@
                            'timeout': client.build_timeout,
                        })
             raise exceptions.TimeoutException(message)
+
+
+def wait_for_subnet_create_check(client, share_network_id,
+                                 neutron_net_id=None,
+                                 neutron_subnet_id=None,
+                                 availability_zone=None):
+    result = client.subnet_create_check(
+        share_network_id, neutron_net_id=neutron_net_id,
+        neutron_subnet_id=neutron_subnet_id,
+        availability_zone=availability_zone)
+    start = int(time.time())
+    while not result['compatible']:
+        time.sleep(client.build_interval)
+        result = client.subnet_create_check(
+            share_network_id, neutron_net_id=neutron_net_id,
+            neutron_subnet_id=neutron_subnet_id,
+            availability_zone=availability_zone)
+        if result['compatible']:
+            break
+        elif int(time.time()) - start >= client.build_timeout or (
+                result['compatible'] is False):
+            message = ('Subnet create check failed within the '
+                       'required time %(timeout)s seconds for share network '
+                       '%(share_network)s.' % {
+                           'timeout': client.build_timeout,
+                           'share_network': share_network_id,
+                       })
+            raise exceptions.TimeoutException(message)
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index f5022af..b62c409 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -40,7 +40,7 @@
                     "This value is only used to validate the versions "
                     "response from Manila."),
     cfg.StrOpt("max_api_microversion",
-               default="2.69",
+               default="2.71",
                help="The maximum api microversion is configured to be the "
                     "value of the latest microversion supported by Manila."),
     cfg.StrOpt("region",
@@ -287,6 +287,16 @@
                 default=False,
                 help="Defines whether to run share servers migration tests. "
                      "Enable this option if the used driver supports it."),
+    cfg.BoolOpt("run_share_server_multiple_subnet_tests",
+                default=False,
+                help="Defines whether to run the share server multiple "
+                     "subnets tests. Enable this option if the used driver "
+                     "supports it."),
+    cfg.BoolOpt("run_network_allocation_update_tests",
+                default=False,
+                help="Defines whether to run the network allocation update "
+                     "tests. Enable this option if the used driver "
+                     "supports it."),
 
     cfg.StrOpt("image_with_share_tools",
                default="manila-service-image-master",
diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py
index 6db5393..a87648c 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -1957,6 +1957,29 @@
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def subnet_create_check(
+        self, share_network_id, neutron_net_id=None,
+        neutron_subnet_id=None, availability_zone=None,
+        reset=False, version=LATEST_MICROVERSION):
+        body = {
+            'share_network_subnet_create_check': {
+                'neutron_net_id': neutron_net_id,
+                'neutron_subnet_id': neutron_subnet_id,
+                'availability_zone': availability_zone,
+                'reset': reset,
+            }
+        }
+
+        body = json.dumps(body)
+        resp, body = self.post(
+            f'share-networks/{share_network_id}/action',
+            body, headers=EXPERIMENTAL, extra_headers=True,
+            version=version)
+        self.expected_success(202, resp.status)
+
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
 ###############
 
     def share_server_migration_check(
diff --git a/manila_tempest_tests/tests/api/admin/test_share_instances.py b/manila_tempest_tests/tests/api/admin/test_share_instances.py
index 38c3f49..4f2e454 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_instances.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_instances.py
@@ -95,6 +95,8 @@
             expected_keys.append("cast_rules_to_readonly")
         if utils.is_microversion_ge(version, '2.54'):
             expected_keys.append("progress")
+        if utils.is_microversion_ge(version, '2.71'):
+            expected_keys.append("updated_at")
         expected_keys = sorted(expected_keys)
         actual_keys = sorted(si.keys())
         self.assertEqual(expected_keys, actual_keys,
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers.py b/manila_tempest_tests/tests/api/admin/test_share_servers.py
index b406a92..01cc9fc 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers.py
@@ -196,8 +196,13 @@
         if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.49"):
             keys.append("is_auto_deletable")
             keys.append("identifier")
-        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.51"):
+        if utils.is_microversion_ge(
+            CONF.share.max_api_microversion, "2.51") and (
+                utils.is_microversion_lt(
+                    CONF.share.max_api_microversion, "2.70")):
             keys.append("share_network_subnet_id")
+        if utils.is_microversion_ge(CONF.share.max_api_microversion, "2.70"):
+            keys.append("share_network_subnet_ids")
         # all expected keys are present
         for key in keys:
             self.assertIn(key, server.keys())
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py
index 126dee1..8fdc22b 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers_manage.py
@@ -66,6 +66,12 @@
             msg = ("Manage share server with share network subnet is "
                    "supported starting from microversion '2.51'.")
             raise self.skipException(msg)
+        check_multiple_subnet = utils.is_microversion_ge(
+            CONF.share.max_api_microversion, '2.70')
+        if check_multiple_subnet:
+            network_subnet = 'share_network_subnet_ids'
+        else:
+            network_subnet = 'share_network_subnet_id'
         # create a new share network to make sure that a new share server
         # will be created
         original_share_network = self.shares_v2_client.get_share_network(
@@ -91,7 +97,7 @@
                 neutron_subnet_id=share_network['neutron_subnet_id'],
                 availability_zone=az
             )['share_network_subnet']
-            params = {'share_network_subnet_id': az_subnet['id']}
+            params = {network_subnet: az_subnet['id']}
 
         # create share
         share = self.create_share(
@@ -119,7 +125,7 @@
             "identifier",
         ]
         if add_subnet_field:
-            keys.append('share_network_subnet_id')
+            keys.append(network_subnet)
         # all expected keys are present
         for key in keys:
             self.assertIn(key, share_server)
@@ -127,9 +133,10 @@
         # check that the share server is initially auto-deletable
         self.assertIs(True, share_server["is_auto_deletable"])
         self.assertIsNotNone(share_server["identifier"])
-        if add_subnet_field:
-            self.assertEqual(az_subnet["id"],
-                             share_server["share_network_subnet_id"])
+        if add_subnet_field and check_multiple_subnet:
+            self.assertIn(az_subnet["id"], share_server[network_subnet])
+        elif add_subnet_field and not check_multiple_subnet:
+            self.assertEqual(az_subnet["id"], share_server[network_subnet])
 
         self._unmanage_share_and_wait(share)
 
diff --git a/manila_tempest_tests/tests/api/test_replication.py b/manila_tempest_tests/tests/api/test_replication.py
index 722f02f..20ba8fe 100644
--- a/manila_tempest_tests/tests/api/test_replication.py
+++ b/manila_tempest_tests/tests/api/test_replication.py
@@ -187,6 +187,145 @@
         # Delete subnet
         self.shares_v2_client.delete_subnet(self.sn_id, subnet['id'])
 
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    @testtools.skipIf(
+        not CONF.share.multitenancy_enabled, "Only for multitenancy.")
+    @testtools.skipIf(
+        not CONF.share.run_share_server_multiple_subnet_tests,
+        "Share server multiple subnet tests are disabled.")
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share networks.")
+    @utils.skip_if_microversion_not_supported("2.70")
+    @decorators.idempotent_id('4235e789-dbd6-47a8-8f2e-d70edf78e532')
+    def test_add_delete_share_replica_multiple_subnets(self):
+        extra_specs = {
+            "replication_type": self.replication_type,
+            "driver_handles_share_servers": CONF.share.multitenancy_enabled,
+            "share_server_multiple_subnet_support": True,
+        }
+        share_type = self.create_share_type(
+            extra_specs=extra_specs, client=self.admin_client)
+        default_subnet = utils.share_network_get_default_subnet(
+            self.share_network)
+        new_share_network_id = self.create_share_network(
+            cleanup_in_class=False)['id']
+        subnet_data = {
+            'neutron_net_id': default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': default_subnet.get('neutron_subnet_id'),
+            'share_network_id': new_share_network_id,
+            'availability_zone': self.replica_zone,
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+        subnet2 = self.create_share_network_subnet(**subnet_data)
+        # Creating a third subnet in share replica az
+        subnet_data.update({'availability_zone': self.share_zone})
+        subnet3 = self.create_share_network_subnet(**subnet_data)
+        # Create the share and share replica
+        share = self.create_share(
+            share_type_id=share_type['id'], cleanup_in_class=False,
+            availability_zone=self.share_zone,
+            share_network_id=new_share_network_id)
+        share = self.admin_client.get_share(share['id'])['share']
+        replica = self.create_share_replica(share['id'], self.replica_zone)
+        replica = self.admin_client.get_share_replica(
+            replica['id'])['share_replica']
+        share_server = self.admin_client.show_share_server(
+            replica['share_server_id'])['share_server']
+        self.assertIn(subnet1['id'],
+                      share_server['share_network_subnet_ids'])
+        self.assertIn(subnet2['id'],
+                      share_server['share_network_subnet_ids'])
+        # Delete the replica
+        self.delete_share_replica(replica['id'])
+        # Delete share
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete subnets
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet1['id'])
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet2['id'])
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet3['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    @testtools.skipIf(
+        not CONF.share.multitenancy_enabled, "Only for multitenancy.")
+    @testtools.skipIf(
+        not CONF.share.run_network_allocation_update_tests,
+        "Share server network allocation update tests are disabled.")
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share_network.")
+    @utils.skip_if_microversion_not_supported("2.70")
+    @decorators.idempotent_id('26694947-d4a0-46c8-99e8-2e0eca1b6a08')
+    def test_add_delete_share_replica_network_allocation_update(self):
+        extra_specs = {
+            "replication_type": self.replication_type,
+            "driver_handles_share_servers": CONF.share.multitenancy_enabled,
+            "network_allocation_update_support": True,
+        }
+        share_type = self.create_share_type(extra_specs=extra_specs)
+
+        default_subnet = utils.share_network_get_default_subnet(
+            self.share_network)
+        new_share_network_id = self.create_share_network(
+            cleanup_in_class=False)['id']
+        subnet_data = {
+            'neutron_net_id': default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': default_subnet.get('neutron_subnet_id'),
+            'share_network_id': new_share_network_id,
+            'availability_zone': self.share_zone,
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+        subnet_data.update({'availability_zone': self.replica_zone})
+        subnet2 = self.create_share_network_subnet(**subnet_data)
+        # Create the share and share replica
+        share = self.create_share(
+            share_type_id=share_type['id'], cleanup_in_class=False,
+            availability_zone=self.share_zone,
+            share_network_id=new_share_network_id)
+        share = self.admin_client.get_share(share['id'])['share']
+
+        replica = self.create_share_replica(share['id'], self.replica_zone)
+        replica = self.admin_client.get_share_replica(
+            replica['id'])['share_replica']
+
+        # Waits until the check is completed and positive
+        waiters.wait_for_subnet_create_check(
+            self.shares_v2_client, new_share_network_id,
+            neutron_net_id=subnet_data['neutron_net_id'],
+            neutron_subnet_id=subnet_data['neutron_subnet_id'],
+            availability_zone=self.replica_zone)
+        # Creating a third subnet in replica zone to trigger the network
+        # allocation update
+        subnet3 = self.create_share_network_subnet(**subnet_data)
+        waiters.wait_for_resource_status(
+            self.admin_client, replica['share_server_id'],
+            constants.SERVER_STATE_ACTIVE,
+            resource_name="share_server",
+            status_attr="status")
+        share_server = self.admin_client.show_share_server(
+            replica['share_server_id']
+        )['share_server']
+        self.assertIn(subnet2['id'],
+                      share_server['share_network_subnet_ids'])
+        self.assertIn(subnet3['id'],
+                      share_server['share_network_subnet_ids'])
+        # Delete the replica
+        self.delete_share_replica(replica['id'])
+        # Delete share
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete subnets
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet1['id'])
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet2['id'])
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet3['id'])
+
     @decorators.idempotent_id('00e12b41-b95d-494a-99be-e584aae10f5c')
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
     def test_add_access_rule_create_replica_delete_rule(self):
diff --git a/manila_tempest_tests/tests/api/test_share_groups.py b/manila_tempest_tests/tests/api/test_share_groups.py
index 3b56161..b203481 100644
--- a/manila_tempest_tests/tests/api/test_share_groups.py
+++ b/manila_tempest_tests/tests/api/test_share_groups.py
@@ -15,12 +15,14 @@
 
 import ddt
 from tempest import config
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 import testtools
 from testtools import testcase as tc
 
 from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
 from manila_tempest_tests.tests.api import base
 from manila_tempest_tests import utils
 
@@ -263,3 +265,130 @@
         # Verify that share always has the same AZ as share group does
         self.assertEqual(
             share_group['availability_zone'], share['availability_zone'])
+
+    @utils.skip_if_microversion_not_supported("2.70")
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @testtools.skipUnless(CONF.share.multitenancy_enabled,
+                          "Multitenancy is disabled.")
+    @testtools.skipUnless(CONF.share.run_share_server_multiple_subnet_tests,
+                          "Share server multiple subnet tests are disabled.")
+    @testtools.skipIf(CONF.share.share_network_id != "",
+                      "This test is not suitable for pre-existing "
+                      "share networks.")
+    @ddt.data(False, True)
+    @decorators.idempotent_id('17fd1867-03a3-43d0-9be3-daf90b6c5e02')
+    def test_create_sg_and_share_with_multiple_subnets(
+        self, network_allocation_update):
+        if network_allocation_update and not (
+            CONF.share.run_network_allocation_update_tests):
+            raise self.skipException(
+                'Network allocation update tests are disabled.')
+        extra_specs = {
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+            'share_server_multiple_subnet_support': True,
+        }
+        if network_allocation_update:
+            extra_specs['network_allocation_update_support'] = True
+        share_type = self.create_share_type(extra_specs=extra_specs)
+        sg_type_name = data_utils.rand_name("tempest-manila")
+        sg_type = self.create_share_group_type(
+            name=sg_type_name, share_types=share_type['id'],
+            client=self.admin_shares_v2_client)
+        # Get list of existing availability zones, at least one always
+        # should exist
+        azs = self.get_availability_zones_matching_share_type(share_type)
+        if len(azs) == 0:
+            raise self.skipException(
+                "No AZs were found. Make sure there is at least one "
+                "configured.")
+        share_network = self.shares_v2_client.get_share_network(
+            self.shares_v2_client.share_network_id)['share_network']
+        new_share_network_id = self.create_share_network(
+            cleanup_in_class=False)['id']
+
+        default_subnet = utils.share_network_get_default_subnet(
+            share_network)
+        subnet_data = {
+            'neutron_net_id': default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': default_subnet.get('neutron_subnet_id'),
+            'share_network_id': new_share_network_id,
+            'availability_zone': azs[0]
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+        if not network_allocation_update:
+            subnet2 = self.create_share_network_subnet(**subnet_data)
+
+        sg_kwargs = {
+            'share_group_type_id': sg_type['id'],
+            'share_type_ids': [share_type['id']],
+            'share_network_id': new_share_network_id,
+            'availability_zone': azs[0],
+            'version': constants.MIN_SHARE_GROUP_MICROVERSION,
+            'cleanup_in_class': False,
+        }
+
+        # Create share group
+        share_group = self.create_share_group(**sg_kwargs)
+
+        # Get latest share group info
+        share_group = self.shares_v2_client.get_share_group(
+            share_group['id'])['share_group']
+
+        self.assertIn('availability_zone', share_group)
+        self.assertEqual(azs[0], share_group['availability_zone'])
+
+        # Test 'consistent_snapshot_support' as part of 2.33 API change
+        self.assertIn('consistent_snapshot_support', share_group)
+        self.assertIn(
+            share_group['consistent_snapshot_support'], ('host', 'pool', None))
+
+        share_data = {
+            'share_type_id': share_type['id'],
+            'share_group_id': share_group['id'],
+            'share_network_id': new_share_network_id,
+            'availability_zone': azs[0],
+            'cleanup_in_class': False,
+        }
+
+        # Create share in share group
+        share = self.create_share(**share_data)
+
+        # Get latest share info
+        share = self.admin_shares_v2_client.get_share(share['id'])['share']
+        # Verify that share always has the same AZ as share group does
+        self.assertEqual(
+            share_group['availability_zone'], share['availability_zone'])
+
+        # Get share server info
+        share_server = self.admin_shares_v2_client.show_share_server(
+            share['share_server_id'])['share_server']
+        if network_allocation_update:
+            waiters.wait_for_subnet_create_check(
+                self.shares_v2_client, new_share_network_id,
+                neutron_net_id=subnet_data['neutron_net_id'],
+                neutron_subnet_id=subnet_data['neutron_subnet_id'],
+                availability_zone=azs[0])
+
+            subnet2 = self.create_share_network_subnet(**subnet_data)
+            waiters.wait_for_resource_status(
+                self.admin_shares_v2_client, share['share_server_id'],
+                constants.SERVER_STATE_ACTIVE,
+                resource_name="share_server",
+                status_attr="status")
+        share_server = self.admin_shares_v2_client.show_share_server(
+            share['share_server_id'])['share_server']
+        # Check if share server has multiple subnets
+        self.assertIn(subnet1['id'], share_server['share_network_subnet_ids'])
+        self.assertIn(subnet2['id'], share_server['share_network_subnet_ids'])
+        # Delete share
+        params = {"share_group_id": share_group['id']}
+        self.shares_v2_client.delete_share(
+            share['id'],
+            params=params,
+            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+        self.shares_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete subnet
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet1['id'])
+        self.shares_v2_client.delete_subnet(
+            new_share_network_id, subnet2['id'])
diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets.py b/manila_tempest_tests/tests/api/test_share_network_subnets.py
index 0f651c9..cdf2162 100644
--- a/manila_tempest_tests/tests/api/test_share_network_subnets.py
+++ b/manila_tempest_tests/tests/api/test_share_network_subnets.py
@@ -127,6 +127,8 @@
             msg = ("This test needs at least two compatible storage "
                    "availability zones.")
             raise self.skipException(msg)
+        check_multiple_subnet = utils.is_microversion_ge(
+            CONF.share.max_api_microversion, '2.70')
 
         original_share_network = self.shares_v2_client.get_share_network(
             self.shares_v2_client.share_network_id
@@ -173,8 +175,12 @@
         # Match new subnet content
         self.assertDictContainsSubset(data, subnet)
         # Match share server subnet
-        self.assertEqual(subnet['id'],
-                         share_server['share_network_subnet_id'])
+        if check_multiple_subnet:
+            self.assertIn(subnet['id'],
+                          share_server['share_network_subnet_ids'])
+        else:
+            self.assertIn(subnet['id'],
+                          share_server['share_network_subnet_id'])
         # Delete share
         self.shares_v2_client.delete_share(share['id'])
         self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
@@ -197,10 +203,11 @@
             msg = ("This test needs at least two compatible storage "
                    "availability zones.")
             raise self.skipException(msg)
+        check_multiple_subnet = utils.is_microversion_ge(
+            CONF.share.max_api_microversion, '2.70')
 
         original_share_network = self.shares_v2_client.get_share_network(
-            self.shares_v2_client.share_network_id
-        )['share_network']
+            self.shares_v2_client.share_network_id)['share_network']
         share_net_info = (
             utils.share_network_get_default_subnet(original_share_network))
         share_network = self.create_share_network(
@@ -254,8 +261,12 @@
         # Default subnet was created during share network creation
         self.assertIsNone(default_subnet['availability_zone'])
         # Match share server subnet
-        self.assertEqual(expected_subnet_id,
-                         share_server['share_network_subnet_id'])
+        if not check_multiple_subnet:
+            self.assertEqual(
+                expected_subnet_id, share_server['share_network_subnet_id'])
+        else:
+            self.assertIn(
+                expected_subnet_id, share_server['share_network_subnet_ids'])
         if create_share_with_az:
             self.assertEqual(destination_az,
                              share['availability_zone'])
diff --git a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
index 4af34f2..7b0ca55 100644
--- a/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_network_subnets_negative.py
@@ -27,6 +27,7 @@
 
 
 CONF = config.CONF
+LATEST_MICROVERSION = CONF.share.max_api_microversion
 
 
 @ddt.ddt
@@ -72,6 +73,7 @@
     @decorators.idempotent_id('13f397bf-5e3a-42b0-b4f9-9cd2dbbb0955')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
     @ddt.data(True, False)
+    @utils.skip_if_is_microversion_ge(LATEST_MICROVERSION, "2.70")
     def test_add_share_network_subnet_in_same_az_exists(self, is_default):
         share_network = self.shares_v2_client.create_share_network(
             )['share_network']
@@ -231,7 +233,8 @@
         # Get a compatible availability zone
         az = self.get_availability_zones_matching_share_type(
             self.share_type)[0]
-
+        check_multiple_subnets = utils.is_microversion_ge(
+            CONF.share.max_api_microversion, '2.70')
         original_share_network = self.shares_v2_client.get_share_network(
             self.shares_v2_client.share_network_id
         )['share_network']
@@ -269,8 +272,12 @@
             share['share_server_id']
         )['share_server']
         # Match share server subnet
-        self.assertEqual(subnet['id'],
-                         share_server['share_network_subnet_id'])
+        if check_multiple_subnets:
+            self.assertIn(subnet['id'],
+                          share_server['share_network_subnet_ids'])
+        else:
+            self.assertEqual(subnet['id'],
+                             share_server['share_network_subnet_id'])
 
         # Assert that the user cannot delete a subnet that contain shares
         self.assertRaises(lib_exc.Conflict,
diff --git a/manila_tempest_tests/tests/api/test_share_networks_negative.py b/manila_tempest_tests/tests/api/test_share_networks_negative.py
index 17e0c12..3306068 100644
--- a/manila_tempest_tests/tests/api/test_share_networks_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_networks_negative.py
@@ -195,3 +195,24 @@
             self.shares_v2_client.create_share_network,
             availability_zone='inexistent-availability-zone',
         )
+
+    @utils.skip_if_microversion_not_supported("2.70")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    @decorators.idempotent_id('f6f47c64-6821-4d4a-aa7d-3b0244158197')
+    def test_check_add_share_network_subnet_share_network_not_found(self):
+        data = self.generate_subnet_data()
+        self.assertRaises(lib_exc.NotFound,
+                          self.shares_v2_client.subnet_create_check,
+                          'fake_inexistent_id',
+                          **data)
+
+    @utils.skip_if_microversion_not_supported("2.70")
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    @decorators.idempotent_id('d9a487fb-6638-4f93-8b69-3e1a85bfbc7d')
+    def test_check_add_share_network_subnet_az_not_found(self):
+        share_network = self.create_share_network()
+        data = {'availability_zone': 'non-existent-az'}
+
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.subnet_create_check,
+                          share_network['id'], **data)
diff --git a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py
new file mode 100644
index 0000000..fccbf03
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet.py
@@ -0,0 +1,196 @@
+# Copyright 2022 NetApp Inc.
+# 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 import config
+from tempest.lib import decorators
+import testtools
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareServerMultipleSubnetTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareServerMultipleSubnetTest, cls).skip_checks()
+        if not CONF.share.multitenancy_enabled:
+            raise cls.skipException('Multitenancy tests are disabled.')
+        if not CONF.share.run_share_server_multiple_subnet_tests and not (
+                CONF.share.run_network_allocation_update_tests):
+            raise cls.skipException(
+                'Share server multiple subnets and network allocation '
+                'update tests are disabled.')
+        if CONF.share.share_network_id != "":
+            raise cls.skipException(
+                'These tests are not suitable for pre-existing '
+                'share_network.')
+        utils.check_skip_if_microversion_not_supported("2.70")
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareServerMultipleSubnetTest, cls).resource_setup()
+        cls.extra_specs = {
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+        }
+        if CONF.share.run_share_server_multiple_subnet_tests:
+            cls.extra_specs['share_server_multiple_subnet_support'] = True
+        if CONF.share.run_network_allocation_update_tests:
+            cls.extra_specs['network_allocation_update_support'] = True
+        share_type = cls.create_share_type(extra_specs=cls.extra_specs)
+        cls.share_type_id = share_type['id']
+
+        cls.zones = cls.get_availability_zones_matching_share_type(
+            share_type)
+        if len(cls.zones) == 0:
+            msg = ("These tests need at least one compatible "
+                   "availability zone.")
+            raise cls.skipException(msg)
+
+        cls.share_network = cls.alt_shares_v2_client.get_share_network(
+            cls.alt_shares_v2_client.share_network_id)['share_network']
+        cls.default_subnet = utils.share_network_get_default_subnet(
+            cls.share_network)
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    @testtools.skipIf(
+        not CONF.share.run_share_server_multiple_subnet_tests,
+        "Share network multiple subnets tests are disabled.")
+    @decorators.idempotent_id('5600bd52-ecb4-47d3-a4e8-3e6565cb0b80')
+    def test_create_share_on_multiple_subnets_same_az(self):
+        share_network_id = self.create_share_network(
+            cleanup_in_class=False)["id"]
+        subnet_data = {
+            'neutron_net_id': self.default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'),
+            'share_network_id': share_network_id,
+            'availability_zone': self.zones[0],
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+        subnet2 = self.create_share_network_subnet(**subnet_data)
+
+        share = self.create_share(
+            share_type_id=self.share_type_id,
+            share_network_id=share_network_id,
+            availability_zone=self.zones[0])
+        self.assertIn(share['status'], ('creating', 'available'))
+
+        share = self.admin_shares_v2_client.get_share(share['id'])['share']
+        share_server = self.admin_shares_v2_client.show_share_server(
+            share['share_server_id']
+        )['share_server']
+        self.assertIn(subnet1['id'],
+                      share_server['share_network_subnet_ids'])
+        self.assertIn(subnet2['id'],
+                      share_server['share_network_subnet_ids'])
+
+        # Delete share
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete the subnets
+        self.shares_v2_client.delete_subnet(share_network_id, subnet1['id'])
+        self.shares_v2_client.delete_subnet(share_network_id, subnet2['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    @testtools.skipIf(
+        not CONF.share.run_network_allocation_update_tests,
+        "Share server network allocation update are disabled.")
+    @decorators.idempotent_id('2a9debd5-47a3-42cc-823b-2b9de435a5e4')
+    def test_create_share_with_network_allocation_update(self):
+        share_network_id = self.create_share_network(
+            cleanup_in_class=False)["id"]
+        subnet_data = {
+            'neutron_net_id': self.default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'),
+            'share_network_id': share_network_id,
+            'availability_zone': self.zones[0],
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+
+        share = self.create_share(
+            share_type_id=self.share_type_id,
+            share_network_id=share_network_id,
+            availability_zone=self.zones[0])
+        self.assertIn(share['status'], ('creating', 'available'))
+        share = self.admin_shares_v2_client.get_share(share['id'])['share']
+
+        waiters.wait_for_subnet_create_check(
+            self.shares_v2_client, share_network_id,
+            neutron_net_id=subnet_data['neutron_net_id'],
+            neutron_subnet_id=subnet_data['neutron_subnet_id'],
+            availability_zone=self.zones[0])
+        subnet2 = self.create_share_network_subnet(**subnet_data)
+
+        waiters.wait_for_resource_status(
+            self.admin_shares_v2_client, share['share_server_id'],
+            constants.SERVER_STATE_ACTIVE,
+            resource_name="share_server",
+            status_attr="status")
+        share_server = self.admin_shares_v2_client.show_share_server(
+            share['share_server_id']
+        )['share_server']
+
+        self.assertIn(subnet1['id'],
+                      share_server['share_network_subnet_ids'])
+        self.assertIn(subnet2['id'],
+                      share_server['share_network_subnet_ids'])
+
+        # Delete share
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete subnets
+        self.shares_v2_client.delete_subnet(share_network_id, subnet1['id'])
+        self.shares_v2_client.delete_subnet(share_network_id, subnet2['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    @testtools.skipIf(
+        not CONF.share.run_network_allocation_update_tests,
+        "Share server network allocation update are disabled.")
+    @decorators.idempotent_id('2624f9a7-660b-4f91-89b8-c026b3bb8d1f')
+    def test_share_network_subnet_create_check(self):
+        """The share network subnet create check compatibility test."""
+
+        share_network_id = self.create_share_network(
+            cleanup_in_class=False)["id"]
+        subnet_data = {
+            'neutron_net_id': self.default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': self.default_subnet.get('neutron_subnet_id'),
+            'share_network_id': share_network_id,
+            'availability_zone': self.zones[0],
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+
+        share = self.create_share(
+            share_type_id=self.share_type_id,
+            share_network_id=share_network_id,
+            availability_zone=self.zones[0]
+        )
+        self.assertIn(share['status'], ('creating', 'available'))
+        waiters.wait_for_subnet_create_check(
+            self.shares_v2_client, share_network_id,
+            neutron_net_id=subnet_data['neutron_net_id'],
+            neutron_subnet_id=subnet_data['neutron_subnet_id'],
+            availability_zone=self.zones[0])
+
+        # Delete share
+        self.shares_v2_client.delete_share(share['id'])
+        self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
+        # Delete subnets
+        self.shares_v2_client.delete_subnet(share_network_id, subnet1['id'])
diff --git a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py
new file mode 100644
index 0000000..522f34e
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py
@@ -0,0 +1,90 @@
+# Copyright 2022 NetApp Inc.
+# 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 import config
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from testtools import testcase as tc
+
+from manila_tempest_tests import share_exceptions
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareServerMultipleSubNegativeTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareServerMultipleSubNegativeTest, cls).skip_checks()
+        if not CONF.share.multitenancy_enabled:
+            raise cls.skipException('Multitenancy tests are disabled.')
+        utils.check_skip_if_microversion_not_supported("2.70")
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareServerMultipleSubNegativeTest, cls).resource_setup()
+        cls.share_network = cls.alt_shares_v2_client.get_share_network(
+            cls.alt_shares_v2_client.share_network_id)['share_network']
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('1e2a9415-b02f-4c02-812d-bedc361f92ce')
+    def test_create_share_multiple_subnets_to_unsupported_backend(self):
+        extra_specs = {
+            'driver_handles_share_servers': CONF.share.multitenancy_enabled,
+            'share_server_multiple_subnet_support': False
+        }
+        share_type = self.create_share_type(extra_specs=extra_specs)
+        pools = self.get_pools_matching_share_type(
+            share_type, client=self.admin_shares_v2_client)
+        zones = self.get_availability_zones_matching_share_type(
+            share_type)
+        if not pools or not zones:
+            raise self.skipException("At least one backend that supports "
+                                     "adding multiple subnets into a share "
+                                     "network is needed for this test.")
+        extra_specs = {'pool_name': pools[0]['pool'],
+                       'availability_zone': zones[0]}
+        self.admin_shares_v2_client.update_share_type_extra_specs(
+            share_type['id'], extra_specs)
+
+        share_network_id = self.create_share_network(
+            cleanup_in_class=True)["id"]
+        default_subnet = utils.share_network_get_default_subnet(
+            self.share_network)
+        subnet_data = {
+            'neutron_net_id': default_subnet.get('neutron_net_id'),
+            'neutron_subnet_id': default_subnet.get('neutron_subnet_id'),
+            'share_network_id': share_network_id,
+            'availability_zone': zones[0],
+            'cleanup_in_class': False
+        }
+        subnet1 = self.create_share_network_subnet(**subnet_data)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.shares_v2_client.delete_subnet,
+                        share_network_id, subnet1['id'])
+        subnet2 = self.create_share_network_subnet(**subnet_data)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.shares_v2_client.delete_subnet,
+                        share_network_id, subnet2['id'])
+        self.assertRaises(
+            share_exceptions.ShareBuildErrorException,
+            self.create_share,
+            share_type_id=share_type['id'],
+            share_network_id=share_network_id,
+            availability_zone=zones[0],
+            cleanup_in_class=False
+        )
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index d0860f0..5ecfb36 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -94,6 +94,16 @@
     return lambda f: f
 
 
+def skip_if_is_microversion_ge(left, right):
+    """Skip if version for left is greater than or equal to the right one."""
+
+    if is_microversion_ge(left, right):
+        reason = ("Skipped. Test requires microversion "
+                  "< than '%s'." % right)
+        return testtools.skip(reason)
+    return lambda f: f
+
+
 def check_skip_if_microversion_not_supported(microversion):
     """Callable method for tests that are microversion-specific."""
     if not is_microversion_supported(microversion):
diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml
index 494b285..a68aafe 100644
--- a/zuul.d/manila-tempest-jobs.yaml
+++ b/zuul.d/manila-tempest-jobs.yaml
@@ -246,6 +246,8 @@
               backend_names: LONDON,PARIS
               multi_backend: true
               run_share_server_migration_tests: true
+              run_share_server_multiple_subnet_tests: true
+              run_network_allocation_update_tests: true
 
 - job:
     name: manila-tempest-plugin-generic
@@ -490,6 +492,8 @@
               run_replication_tests: true
               run_revert_to_snapshot_tests: true
               run_share_server_migration_tests: true
+              run_share_server_multiple_subnet_tests: true
+              run_network_allocation_update_tests: true
 
 - job:
     name: manila-tempest-plugin-glusterfs-native