Merge "Change some compute admin image client methods to return one value"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 234273b..02609ae 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -135,6 +135,9 @@
 # concurrent test processes. (boolean value)
 #locking_credentials_provider = false
 
+# Roles to assign to all users created by tempest (list value)
+#tempest_roles =
+
 
 [baremetal]
 
@@ -582,9 +585,6 @@
 # applies to user and project (string value)
 #admin_domain_name = <None>
 
-# Roles to assign to all users created by tempest (list value)
-#tempest_roles =
-
 
 [identity-feature-enabled]
 
@@ -920,6 +920,11 @@
 # operations testing. (integer value)
 #large_ops_number = 0
 
+# DHCP client used by images to renew DCHP lease. If left empty,
+# update operation will be skipped. Supported clients: "udhcpc",
+# "dhclient" (string value)
+#dhcp_client = udhcpc
+
 
 [service_available]
 
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index e70519e..65aeb24 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -15,7 +15,6 @@
 import itertools
 
 import netaddr
-import testtools
 
 from tempest.api.network import base
 from tempest.common import custom_matchers
@@ -571,9 +570,16 @@
                               test_subnet_ids,
                               'Subnet are not in the same network')
 
-    @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
-                          "IPv6 extended attributes for subnets not "
-                          "available")
+
+class NetworksIpV6TestAttrs(NetworksIpV6TestJSON):
+
+    @classmethod
+    def resource_setup(cls):
+        if not CONF.network_feature_enabled.ipv6_subnet_attributes:
+            raise cls.skipException("IPv6 extended attributes for "
+                                    "subnets not available")
+        super(NetworksIpV6TestAttrs, cls).resource_setup()
+
     @test.attr(type='smoke')
     def test_create_delete_subnet_with_v6_attributes_stateful(self):
         self._create_verify_delete_subnet(
@@ -581,20 +587,54 @@
             ipv6_ra_mode='dhcpv6-stateful',
             ipv6_address_mode='dhcpv6-stateful')
 
-    @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
-                          "IPv6 extended attributes for subnets not "
-                          "available")
     @test.attr(type='smoke')
     def test_create_delete_subnet_with_v6_attributes_slaac(self):
         self._create_verify_delete_subnet(
             ipv6_ra_mode='slaac',
             ipv6_address_mode='slaac')
 
-    @testtools.skipUnless(CONF.network_feature_enabled.ipv6_subnet_attributes,
-                          "IPv6 extended attributes for subnets not "
-                          "available")
     @test.attr(type='smoke')
     def test_create_delete_subnet_with_v6_attributes_stateless(self):
         self._create_verify_delete_subnet(
             ipv6_ra_mode='dhcpv6-stateless',
             ipv6_address_mode='dhcpv6-stateless')
+
+    def _test_delete_subnet_with_ports(self, mode):
+        """Create subnet and delete it with existing ports"""
+        slaac_network = self.create_network()
+        subnet_slaac = self.create_subnet(slaac_network,
+                                          **{'ipv6_ra_mode': mode,
+                                             'ipv6_address_mode': mode})
+        port = self.create_port(slaac_network)
+        self.assertIsNotNone(port['fixed_ips'][0]['ip_address'])
+        self.client.delete_subnet(subnet_slaac['id'])
+        self.subnets.pop()
+        subnets = self.client.list_subnets()
+        subnet_ids = [subnet['id'] for subnet in subnets['subnets']]
+        self.assertNotIn(subnet_slaac['id'], subnet_ids,
+                         "Subnet wasn't deleted")
+        self.assertRaisesRegexp(
+            exceptions.Conflict,
+            "There are one or more ports still in use on the network",
+            self.client.delete_network,
+            slaac_network['id'])
+
+    @test.attr(type='smoke')
+    def test_create_delete_slaac_subnet_with_ports(self):
+        """Test deleting subnet with SLAAC ports
+
+        Create subnet with SLAAC, create ports in network
+        and then you shall be able to delete subnet without port
+        deletion. But you still can not delete the network.
+        """
+        self._test_delete_subnet_with_ports("slaac")
+
+    @test.attr(type='smoke')
+    def test_create_delete_stateless_subnet_with_ports(self):
+        """Test deleting subnet with DHCPv6 stateless ports
+
+        Create subnet with DHCPv6 stateless, create ports in network
+        and then you shall be able to delete subnet without port
+        deletion. But you still can not delete the network.
+        """
+        self._test_delete_subnet_with_ports("dhcpv6-stateless")
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index ee0e4c8..bf85e90 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -17,6 +17,7 @@
 import socket
 
 from tempest.api.network import base
+from tempest.api.network import base_security_groups as sec_base
 from tempest.common import custom_matchers
 from tempest.common.utils import data_utils
 from tempest import config
@@ -25,7 +26,7 @@
 CONF = config.CONF
 
 
-class PortsTestJSON(base.BaseNetworkTest):
+class PortsTestJSON(sec_base.BaseSecGroupTest):
     _interface = 'json'
 
     """
@@ -102,7 +103,8 @@
         address = self._get_ipaddress_from_tempest_conf()
         allocation_pools = {'allocation_pools': [{'start': str(address + 4),
                                                   'end': str(address + 6)}]}
-        self.create_subnet(network, **allocation_pools)
+        subnet = self.create_subnet(network, **allocation_pools)
+        self.addCleanup(self.client.delete_subnet, subnet['id'])
         body = self.client.create_port(network_id=net_id)
         self.addCleanup(self.client.delete_port, body['port']['id'])
         port = body['port']
@@ -148,8 +150,11 @@
     def test_port_list_filter_by_router_id(self):
         # Create a router
         network = self.create_network()
-        self.create_subnet(network)
+        self.addCleanup(self.client.delete_network, network['id'])
+        subnet = self.create_subnet(network)
+        self.addCleanup(self.client.delete_subnet, subnet['id'])
         router = self.create_router(data_utils.rand_name('router-'))
+        self.addCleanup(self.client.delete_router, router['id'])
         port = self.client.create_port(network_id=network['id'])
         # Add router interface to port created above
         self.client.add_router_interface_with_port_id(
@@ -175,31 +180,41 @@
             self.assertEqual(sorted(fields), sorted(port.keys()))
 
     @test.attr(type='smoke')
-    def test_update_port_with_second_ip(self):
+    def test_create_update_port_with_second_ip(self):
         # Create a network with two subnets
         network = self.create_network()
+        self.addCleanup(self.client.delete_network, network['id'])
         subnet_1 = self.create_subnet(network)
+        self.addCleanup(self.client.delete_subnet, subnet_1['id'])
         subnet_2 = self.create_subnet(network)
+        self.addCleanup(self.client.delete_subnet, subnet_2['id'])
         fixed_ip_1 = [{'subnet_id': subnet_1['id']}]
         fixed_ip_2 = [{'subnet_id': subnet_2['id']}]
 
-        # Create a port with a single IP address from first subnet
-        port = self.create_port(network,
-                                fixed_ips=fixed_ip_1)
-        self.assertEqual(1, len(port['fixed_ips']))
-
-        # Update the port with a second IP address from second subnet
         fixed_ips = fixed_ip_1 + fixed_ip_2
-        port = self.update_port(port, fixed_ips=fixed_ips)
+
+        # Create a port with multiple IP addresses
+        port = self.create_port(network,
+                                fixed_ips=fixed_ips)
+        self.addCleanup(self.client.delete_port, port['id'])
         self.assertEqual(2, len(port['fixed_ips']))
+        check_fixed_ips = [subnet_1['id'], subnet_2['id']]
+        for item in port['fixed_ips']:
+            self.assertIn(item['subnet_id'], check_fixed_ips)
 
         # Update the port to return to a single IP address
         port = self.update_port(port, fixed_ips=fixed_ip_1)
         self.assertEqual(1, len(port['fixed_ips']))
 
+        # Update the port with a second IP address from second subnet
+        port = self.update_port(port, fixed_ips=fixed_ips)
+        self.assertEqual(2, len(port['fixed_ips']))
+
     def _update_port_with_security_groups(self, security_groups_names):
-        post_body = {"network_id": self.network['id']}
-        self.create_subnet(self.network)
+        subnet_1 = self.create_subnet(self.network)
+        self.addCleanup(self.client.delete_subnet, subnet_1['id'])
+        fixed_ip_1 = [{'subnet_id': subnet_1['id']}]
+
         security_groups_list = list()
         for name in security_groups_names:
             group_create_body = self.client.create_security_group(
@@ -209,24 +224,48 @@
             security_groups_list.append(group_create_body['security_group']
                                         ['id'])
         # Create a port
+        sec_grp_name = data_utils.rand_name('secgroup')
+        security_group = self.client.create_security_group(name=sec_grp_name)
+        self.addCleanup(self.client.delete_security_group,
+                        security_group['security_group']['id'])
+        post_body = {
+            "name": data_utils.rand_name('port-'),
+            "security_groups": [security_group['security_group']['id']],
+            "network_id": self.network['id'],
+            "admin_state_up": True,
+            "fixed_ips": fixed_ip_1}
         body = self.client.create_port(**post_body)
         self.addCleanup(self.client.delete_port, body['port']['id'])
         port = body['port']
+
         # Update the port with security groups
-        update_body = {"security_groups": security_groups_list}
+        subnet_2 = self.create_subnet(self.network)
+        fixed_ip_2 = [{'subnet_id': subnet_2['id']}]
+        update_body = {"name": data_utils.rand_name('port-'),
+                       "admin_state_up": False,
+                       "fixed_ips": fixed_ip_2,
+                       "security_groups": security_groups_list}
         body = self.client.update_port(port['id'], **update_body)
-        # Verify the security groups updated to port
         port_show = body['port']
+        # Verify the security groups and other attributes updated to port
+        exclude_keys = set(port_show).symmetric_difference(update_body)
+        exclude_keys.add('fixed_ips')
+        exclude_keys.add('security_groups')
+        self.assertThat(port_show, custom_matchers.MatchesDictExceptForKeys(
+                        update_body, exclude_keys))
+        self.assertEqual(fixed_ip_2[0]['subnet_id'],
+                         port_show['fixed_ips'][0]['subnet_id'])
+
         for security_group in security_groups_list:
             self.assertIn(security_group, port_show['security_groups'])
 
     @test.attr(type='smoke')
-    def test_update_port_with_security_group(self):
+    def test_update_port_with_security_group_and_extra_attributes(self):
         self._update_port_with_security_groups(
             [data_utils.rand_name('secgroup')])
 
     @test.attr(type='smoke')
-    def test_update_port_with_two_security_groups(self):
+    def test_update_port_with_two_security_groups_and_extra_attributes(self):
         self._update_port_with_security_groups(
             [data_utils.rand_name('secgroup'),
              data_utils.rand_name('secgroup')])
@@ -251,23 +290,14 @@
     @test.attr(type='smoke')
     def test_create_port_with_no_securitygroups(self):
         network = self.create_network()
-        self.create_subnet(network)
+        self.addCleanup(self.client.delete_network, network['id'])
+        subnet = self.create_subnet(network)
+        self.addCleanup(self.client.delete_subnet, subnet['id'])
         port = self.create_port(network, security_groups=[])
+        self.addCleanup(self.client.delete_port, port['id'])
         self.assertIsNotNone(port['security_groups'])
         self.assertEmpty(port['security_groups'])
 
-    @test.attr(type='smoke')
-    def test_update_port_with_no_securitygroups(self):
-        network = self.create_network()
-        self.create_subnet(network)
-        port = self.create_port(network)
-        # Verify that port is created with default security group
-        self.assertIsNotNone(port['security_groups'])
-        self.assertNotEmpty(port['security_groups'])
-        updated_port = self.update_port(port, security_groups=[])
-        self.assertIsNotNone(updated_port['security_groups'])
-        self.assertEmpty(updated_port['security_groups'])
-
 
 class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
     _interface = 'json'
diff --git a/tempest/clients.py b/tempest/clients.py
index 8d59742..de03f1d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -139,9 +139,9 @@
         # super cares for credentials validation
         super(Manager, self).__init__(credentials=credentials)
 
-        self._set_compute_clients(self.interface)
-        self._set_identity_clients(self.interface)
-        self._set_volume_clients(self.interface)
+        self._set_compute_clients()
+        self._set_identity_clients()
+        self._set_volume_clients()
 
         self.baremetal_client = BaremetalClientJSON(self.auth_provider)
         self.network_client = NetworkClientJSON(self.auth_provider)
@@ -176,17 +176,12 @@
         self.data_processing_client = DataProcessingClient(
             self.auth_provider)
 
-    def _set_compute_clients(self, type):
-        self._set_compute_json_clients()
-
-        # Common compute clients
+    def _set_compute_clients(self):
         self.agents_client = AgentsClientJSON(self.auth_provider)
         self.networks_client = NetworksClientJSON(self.auth_provider)
         self.migrations_client = MigrationsClientJSON(self.auth_provider)
         self.security_group_default_rules_client = (
             SecurityGroupDefaultRulesClientJSON(self.auth_provider))
-
-    def _set_compute_json_clients(self):
         self.certificates_client = CertificatesClientJSON(self.auth_provider)
         self.servers_client = ServersClientJSON(self.auth_provider)
         self.limits_client = LimitsClientJSON(self.auth_provider)
@@ -213,10 +208,7 @@
         self.instance_usages_audit_log_client = \
             InstanceUsagesAuditLogClientJSON(self.auth_provider)
 
-    def _set_identity_clients(self, type):
-        self._set_identity_json_clients()
-
-    def _set_identity_json_clients(self):
+    def _set_identity_clients(self):
         self.identity_client = IdentityClientJSON(self.auth_provider)
         self.identity_v3_client = IdentityV3ClientJSON(self.auth_provider)
         self.endpoints_client = EndPointClientJSON(self.auth_provider)
@@ -228,20 +220,12 @@
             self.token_v3_client = V3TokenClientJSON()
         self.credentials_client = CredentialsClientJSON(self.auth_provider)
 
-    def _set_volume_clients(self, type):
-        self._set_volume_json_clients()
-        # Common volume clients
-        # NOTE : As XML clients are not implemented for Qos-specs.
-        # So, setting the qos_client here. Once client are implemented,
-        # qos_client would be moved to its respective if/else.
-        # Bug : 1312553
+    def _set_volume_clients(self):
         self.volume_qos_client = QosSpecsClientJSON(self.auth_provider)
         self.volume_qos_v2_client = QosSpecsV2ClientJSON(
             self.auth_provider)
         self.volume_services_v2_client = VolumesServicesV2ClientJSON(
             self.auth_provider)
-
-    def _set_volume_json_clients(self):
         self.backups_client = BackupsClientJSON(self.auth_provider)
         self.backups_v2_client = BackupsClientV2JSON(self.auth_provider)
         self.snapshots_client = SnapshotsClientJSON(self.auth_provider)
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index e7590b7..a663931 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -124,7 +124,7 @@
             self._assign_user_role(tenant, user, swift_operator_role)
         if admin:
             self._assign_user_role(tenant, user, CONF.identity.admin_role)
-        for role in CONF.identity.tempest_roles:
+        for role in CONF.auth.tempest_roles:
             self._assign_user_role(tenant, user, role)
         return self._get_credentials(user, tenant)
 
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index d8bfef8..6e61c55 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -97,6 +97,10 @@
         cmd = "/bin/ip addr | awk '/ether/ {print $2}'"
         return self.exec_command(cmd)
 
+    def get_nic_name(self, address):
+        cmd = "/bin/ip -o addr | awk '/%s/ {print $2}'" % address
+        return self.exec_command(cmd)
+
     def get_ip_list(self):
         cmd = "/bin/ip address"
         return self.exec_command(cmd)
@@ -116,3 +120,47 @@
         # Get pid(s) of a process/program
         cmd = "ps -ef | grep %s | grep -v 'grep' | awk {'print $1'}" % pr_name
         return self.exec_command(cmd).split('\n')
+
+    def get_dns_servers(self):
+        cmd = 'cat /etc/resolv.conf'
+        resolve_file = self.exec_command(cmd).strip().split('\n')
+        entries = (l.split() for l in resolve_file)
+        dns_servers = [l[1] for l in entries
+                       if len(l) and l[0] == 'nameserver']
+        return dns_servers
+
+    def send_signal(self, pid, signum):
+        cmd = 'sudo /bin/kill -{sig} {pid}'.format(pid=pid, sig=signum)
+        return self.exec_command(cmd)
+
+    def _renew_lease_udhcpc(self, fixed_ip=None):
+        """Renews DHCP lease via udhcpc client. """
+        file_path = '/var/run/udhcpc.'
+        nic_name = self.get_nic_name(fixed_ip)
+        nic_name = nic_name.strip().lower()
+        pid = self.exec_command('cat {path}{nic}.pid'.
+                                format(path=file_path, nic=nic_name))
+        pid = pid.strip()
+        self.send_signal(pid, 'USR1')
+
+    def _renew_lease_dhclient(self, fixed_ip=None):
+        """Renews DHCP lease via dhclient client. """
+        cmd = "sudo /sbin/dhclient -r && /sbin/dhclient"
+        self.exec_command(cmd)
+
+    def renew_lease(self, fixed_ip=None):
+        """Wrapper method for renewing DHCP lease via given client
+
+        Supporting:
+        * udhcpc
+        * dhclient
+        """
+        # TODO(yfried): add support for dhcpcd
+        suported_clients = ['udhcpc', 'dhclient']
+        dhcp_client = CONF.scenario.dhcp_client
+        if dhcp_client not in suported_clients:
+            raise exceptions.InvalidConfiguration('%s DHCP client unsupported'
+                                                  % dhcp_client)
+        if dhcp_client == 'udhcpc' and not fixed_ip:
+            raise ValueError("need to set 'fixed_ip' for udhcpc client")
+        return getattr(self, '_renew_lease_' + dhcp_client)(fixed_ip=fixed_ip)
\ No newline at end of file
diff --git a/tempest/config.py b/tempest/config.py
index 4858fda..dd693e5 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -59,6 +59,9 @@
                      "It requires at least `2 * CONC` distinct accounts "
                      "configured in `test_accounts_file`, with CONC == the "
                      "number of concurrent test processes."),
+    cfg.ListOpt('tempest_roles',
+                help="Roles to assign to all users created by tempest",
+                default=[])
 ]
 
 identity_group = cfg.OptGroup(name='identity',
@@ -131,9 +134,6 @@
     cfg.StrOpt('admin_domain_name',
                help="Admin domain name for authentication (Keystone V3)."
                     "The same domain applies to user and project"),
-    cfg.ListOpt('tempest_roles',
-                help="Roles to assign to all users created by tempest",
-                default=[])
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -860,7 +860,14 @@
         'large_ops_number',
         default=0,
         help="specifies how many resources to request at once. Used "
-        "for large operations testing.")
+        "for large operations testing."),
+    # TODO(yfried): add support for dhcpcd
+    cfg.StrOpt('dhcp_client',
+               default='udhcpc',
+               choices=["udhcpc", "dhclient"],
+               help='DHCP client used by images to renew DCHP lease. '
+                    'If left empty, update operation will be skipped. '
+                    'Supported clients: "udhcpc", "dhclient"')
 ]
 
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 220a7e7..1fdd26a 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1012,12 +1012,16 @@
         router.update(admin_state_up=admin_state_up)
         self.assertEqual(admin_state_up, router.admin_state_up)
 
-    def create_networks(self, client=None, tenant_id=None):
+    def create_networks(self, client=None, tenant_id=None,
+                        dns_nameservers=None):
         """Create a network with a subnet connected to a router.
 
         The baremetal driver is a special case since all nodes are
         on the same shared network.
 
+        :param client: network client to create resources with.
+        :param tenant_id: id of tenant to create resources in.
+        :param dns_nameservers: list of dns servers to send to subnet.
         :returns: network, subnet, router
         """
         if CONF.baremetal.driver_enabled:
@@ -1033,7 +1037,12 @@
         else:
             network = self._create_network(client=client, tenant_id=tenant_id)
             router = self._get_router(client=client, tenant_id=tenant_id)
-            subnet = self._create_subnet(network=network, client=client)
+
+            subnet_kwargs = dict(network=network, client=client)
+            # use explicit check because empty list is a valid option
+            if dns_nameservers is not None:
+                subnet_kwargs['dns_nameservers'] = dns_nameservers
+            subnet = self._create_subnet(**subnet_kwargs)
             subnet.add_to_router(router.id)
         return network, subnet, router
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 30c3b9d..2cfec14 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -100,10 +100,10 @@
         self.keypairs = {}
         self.servers = []
 
-    def _setup_network_and_servers(self):
+    def _setup_network_and_servers(self, **kwargs):
         self.security_group = \
             self._create_security_group(tenant_id=self.tenant_id)
-        self.network, self.subnet, self.router = self.create_networks()
+        self.network, self.subnet, self.router = self.create_networks(**kwargs)
         self.check_networks()
 
         name = data_utils.rand_name('server-smoke')
@@ -425,3 +425,62 @@
         self.check_public_network_connectivity(
             should_connect=True, msg="after updating "
             "admin_state_up of router to True")
+
+    def _check_dns_server(self, ssh_client, dns_servers):
+        servers = ssh_client.get_dns_servers()
+        self.assertEqual(set(dns_servers), set(servers),
+                         'Looking for servers: {trgt_serv}. '
+                         'Retrieved DNS nameservers: {act_serv} '
+                         'From host: {host}.'
+                         .format(host=ssh_client.ssh_client.host,
+                                 act_serv=servers,
+                                 trgt_serv=dns_servers))
+
+    @testtools.skipUnless(CONF.scenario.dhcp_client,
+                          "DHCP client is not available.")
+    @test.attr(type='smoke')
+    @test.services('compute', 'network')
+    def test_subnet_details(self):
+        """Tests that subnet's extra configuration details are affecting
+        the VMs
+
+         NOTE: Neutron subnets push data to servers via dhcp-agent, so any
+         update in subnet requires server to actively renew its DHCP lease.
+
+         1. Configure subnet with dns nameserver
+         2. retrieve the VM's configured dns and verify it matches the one
+         configured for the subnet.
+         3. update subnet's dns
+         4. retrieve the VM's configured dns and verify it matches the new one
+         configured for the subnet.
+
+         TODO(yfried): add host_routes
+
+         any resolution check would be testing either:
+            * l3 forwarding (tested in test_network_basic_ops)
+            * Name resolution of an external DNS nameserver - out of scope for
+            Tempest
+        """
+        # this test check only updates (no actual resolution) so using
+        # arbitrary ip addresses as nameservers, instead of parsing CONF
+        initial_dns_server = '1.2.3.4'
+        alt_dns_server = '9.8.7.6'
+        self._setup_network_and_servers(dns_nameservers=[initial_dns_server])
+        self.check_public_network_connectivity(should_connect=True)
+
+        floating_ip, server = self.floating_ip_tuple
+        ip_address = floating_ip.floating_ip_address
+        private_key = self._get_server_key(server)
+        ssh_client = self._ssh_to_server(ip_address, private_key)
+
+        self._check_dns_server(ssh_client, [initial_dns_server])
+
+        self.subnet.update(dns_nameservers=[alt_dns_server])
+        # asserts that Neutron DB has updated the nameservers
+        self.assertEqual([alt_dns_server], self.subnet.dns_nameservers,
+                         "Failed to update subnet's nameservers")
+
+        # server needs to renew its dhcp lease in order to get the new dns
+        # definitions from subnet
+        ssh_client.renew_lease(fixed_ip=floating_ip['fixed_ip_address'])
+        self._check_dns_server(ssh_client, [alt_dns_server])
diff --git a/tempest/services/network/resources.py b/tempest/services/network/resources.py
index 513d2cf..4d45515 100644
--- a/tempest/services/network/resources.py
+++ b/tempest/services/network/resources.py
@@ -82,8 +82,10 @@
         self._router_ids = set()
 
     def update(self, *args, **kwargs):
-        result = self.client.update_subnet(subnet=self.id, *args, **kwargs)
-        super(DeletableSubnet, self).update(**result['subnet'])
+        result = self.client.update_subnet(self.id,
+                                           *args,
+                                           **kwargs)
+        return super(DeletableSubnet, self).update(**result['subnet'])
 
     def add_to_router(self, router_id):
         self._router_ids.add(router_id)