Merge "Make local boot explicit on software raid tests"
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index 27b5ebe..ecb7218 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -9,14 +9,14 @@
 
 .. code-block:: ini
 
-    [service_enabled]
+    [service_available]
     ironic = True
 
 If introspection tests are needed, also enable support for ironic-inspector:
 
 .. code-block:: ini
 
-    [service_enabled]
+    [service_available]
     ironic_inspector = True
 
 See the following example configurations for more details:
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index 5fc9333..95b29b3 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -52,9 +52,8 @@
     cfg.StrOpt('catalog_type',
                default='baremetal',
                help="Catalog type of the baremetal provisioning service"),
-    # TODO(dtantsur): change to fake-hardware when Ocata is no longer supported
     cfg.StrOpt('driver',
-               default='fake',
+               default='fake-hardware',
                help="Driver name to use for API tests"),
     cfg.StrOpt('endpoint_type',
                default='publicURL',
diff --git a/ironic_tempest_plugin/manager.py b/ironic_tempest_plugin/manager.py
index c3d839d..2977740 100644
--- a/ironic_tempest_plugin/manager.py
+++ b/ironic_tempest_plugin/manager.py
@@ -81,10 +81,8 @@
         cls.security_group_rules_client = (
             cls.os_primary.security_group_rules_client)
 
-        if (CONF.volume_feature_enabled.api_v2 or
-            CONF.volume_feature_enabled.api_v3):
-            cls.volumes_client = cls.os_primary.volumes_client_latest
-            cls.snapshots_client = cls.os_primary.snapshots_client_latest
+        cls.volumes_client = cls.os_primary.volumes_client_latest
+        cls.snapshots_client = cls.os_primary.snapshots_client_latest
 
     # ## Test functions library
     #
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
index 1788463..db0d200 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
@@ -34,14 +34,13 @@
     def inner(*args, **kwargs):
         # TODO(vsaienko): make number of retries and delay between
         # them configurable in future.
-        e = None
         for att in range(10):
             try:
                 return func(*args, **kwargs)
-            except lib_exc.Conflict as e:
+            except lib_exc.Conflict:
+                if att == 9:
+                    raise
                 time.sleep(1)
-        raise lib_exc.Conflict(e)
-
     return inner
 
 
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
index eac82f3..4f8bfe8 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
@@ -17,6 +17,7 @@
 
 from oslo_utils import uuidutils
 from tempest import config
+from tempest.lib.common.utils.linux import remote_client
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
 from tempest.scenario import manager
@@ -600,3 +601,29 @@
         # NOTE(dtantsur): apparently cirros cannot boot from md devices :(
         # So we only move the node to active (verifying deployment).
         self.set_node_to_active()
+
+    def remove_root_device_hint(self):
+        patch = [{'path': '/properties/root_device',
+                  'op': 'remove'}]
+        self.update_node(self.node['uuid'], patch=patch)
+
+    def remove_raid_configuration(self):
+        self.baremetal_client.set_node_raid_config(self.node['uuid'], {})
+
+    def rescue_unrescue(self):
+        rescue_password = uuidutils.generate_uuid()
+        self.rescue_node(self.node['uuid'], rescue_password)
+        self.assertTrue(self.ping_ip_address(self.node_ip,
+                                             should_succeed=True))
+
+        # Open ssh connection to server
+        linux_client = remote_client.RemoteClient(
+            self.node_ip,
+            'rescue',
+            password=rescue_password,
+            ssh_timeout=CONF.baremetal.rescue_timeout)
+        linux_client.validate_authentication()
+
+        self.unrescue_node(self.node['uuid'])
+        self.assertTrue(self.ping_ip_address(self.node_ip,
+                                             should_succeed=True))
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
index ecd4463..ea6f62d 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
@@ -204,13 +204,7 @@
     @utils.services('image', 'network')
     def test_rescue_mode(self):
         self.set_node_to_active(self.image_ref)
-        self.rescue_node(self.node['uuid'], 'abc123')
-        self.assertTrue(self.ping_ip_address(self.node_ip,
-                                             should_succeed=True))
-
-        self.unrescue_node(self.node['uuid'])
-        self.assertTrue(self.ping_ip_address(self.node_ip,
-                                             should_succeed=True))
+        self.rescue_unrescue()
 
 
 class BaremetalIpmiRescuePartitioned(bsm.BaremetalStandaloneScenarioTest):
@@ -233,13 +227,7 @@
     @utils.services('image', 'network')
     def test_rescue_mode(self):
         self.set_node_to_active(self.image_ref)
-        self.rescue_node(self.node['uuid'], 'abc123')
-        self.assertTrue(self.ping_ip_address(self.node_ip,
-                                             should_succeed=True))
-
-        self.unrescue_node(self.node['uuid'])
-        self.assertTrue(self.ping_ip_address(self.node_ip,
-                                             should_succeed=True))
+        self.rescue_unrescue()
 
 
 class BaremetalIloDirectWholediskHttpLink(
@@ -336,3 +324,54 @@
     @utils.services('image', 'network')
     def test_ip_access_to_server(self):
         self.boot_and_verify_node()
+
+
+class BaremetalIloIPxeWholediskHttpLink(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'ilo'
+    deploy_interface = 'direct'
+    boot_interface = 'ilo-ipxe'
+    image_ref = CONF.baremetal.whole_disk_image_url
+    image_checksum = CONF.baremetal.whole_disk_image_checksum
+    wholedisk_image = True
+
+    @decorators.idempotent_id('d926c683-1a32-edbc-07dc-95cd74eefecb')
+    @utils.services('network')
+    def test_ip_access_to_server(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalRedfishDirectWholediskHttpLink(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'redfish'
+    deploy_interface = 'direct'
+    boot_interface = 'redfish-virtual-media'
+    image_ref = CONF.baremetal.whole_disk_image_url
+    image_checksum = CONF.baremetal.whole_disk_image_checksum
+    wholedisk_image = True
+
+    @decorators.idempotent_id('113acd0a-9872-4631-b3ee-54da7e3bb262')
+    @utils.services('network')
+    def test_ip_access_to_server(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalRedfishIPxeWholediskHttpLink(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'redfish'
+    deploy_interface = 'direct'
+    boot_interface = 'ipxe'
+    image_ref = CONF.baremetal.whole_disk_image_url
+    image_checksum = CONF.baremetal.whole_disk_image_checksum
+    wholedisk_image = True
+
+    @decorators.idempotent_id('113acd0a-9872-4631-b3ee-54da7e3bb262')
+    @utils.services('network')
+    def test_ip_access_to_server(self):
+        self.boot_and_verify_node()
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
index b1850fb..10b6663 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
@@ -108,6 +108,15 @@
     @utils.services('image', 'network')
     def test_software_raid(self):
         self.build_raid_and_verify_node()
+        # NOTE(TheJulia): tearing down/terminating the instance does not
+        # remove the root device hint, so it is best for us to go ahead
+        # and remove it before exiting the test.
+        self.remove_root_device_hint()
+        # Removes RAID configuration
+        # TODO(TheJulia): We _should_ tear the raid configuration down
+        # however bouncing neutron ports with known DHCP reload bugs
+        # is not a super great idea for tempest tests.
+        self.remove_raid_configuration()
 
 
 class SoftwareRaidDirect(bsm.BaremetalStandaloneScenarioTest):
@@ -142,3 +151,12 @@
     @utils.services('image', 'network')
     def test_software_raid(self):
         self.build_raid_and_verify_node()
+        # NOTE(TheJulia): tearing down/terminating the instance does not
+        # remove the root device hint, so it is best for us to go ahead
+        # and remove it before exiting the test.
+        self.remove_root_device_hint()
+        # Removes RAID configuration
+        # TODO(TheJulia): We _should_ tear the raid configuration down
+        # however bouncing neutron ports with known DHCP reload bugs
+        # is not a super great idea for tempest tests.
+        self.remove_raid_configuration()
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
index 4fa73ee..0cfbff0 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
@@ -133,16 +133,7 @@
 
     def validate_ports(self):
         node_uuid = self.node['uuid']
-        vifs = []
-        # TODO(vsaienko) switch to get_node_vifs() when all stable releases
-        # supports Ironic API 1.28
-        if self._is_version_supported('1.28'):
-            vifs = self.get_node_vifs(node_uuid)
-        else:
-            for port in self.get_ports(self.node['uuid']):
-                vif = port['extra'].get('vif_port_id')
-                if vif:
-                    vifs.append({'id': vif})
+        vifs = self.get_node_vifs(node_uuid)
 
         ir_ports = self.get_ports(node_uuid)
         ir_ports_addresses = [x['address'] for x in ir_ports]
@@ -160,21 +151,8 @@
         those set on the node. Does not assume that resource classes and traits
         are in use.
         """
-        # Try to get a node with resource class (1.21) and traits (1.37).
-        # TODO(mgoddard): Remove this when all stable releases support these
-        # API versions.
-        for version in ('1.37', '1.21'):
-            if self._is_version_supported(version):
-                node = self.get_node(instance_id=self.instance['id'],
-                                     api_version=version)
-                break
-        else:
-            # Neither API is supported - cannot test.
-            LOG.warning("Cannot validate resource class and trait based "
-                        "scheduling as these require API version 1.21 and "
-                        "1.37 respectively")
-            return
-
+        node = self.get_node(instance_id=self.instance['id'],
+                             api_version='1.37')
         f_id = self.instance['flavor']['id']
         extra_specs = self.flavors_client.list_flavor_extra_specs(f_id)
         extra_specs = extra_specs['extra_specs']
diff --git a/releasenotes/notes/drop-py-2-7-c81e7ff14950791b.yaml b/releasenotes/notes/drop-py-2-7-c81e7ff14950791b.yaml
new file mode 100644
index 0000000..c9401c4
--- /dev/null
+++ b/releasenotes/notes/drop-py-2-7-c81e7ff14950791b.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    Python 2.7 support has been dropped. Last release of ironic-tempest-plugin
+    to support py2.7 is OpenStack Train. The minimum version of Python now
+    supported by ironic-tempest-plugin is Python 3.6.
diff --git a/setup.cfg b/setup.cfg
index 53ca735..241739d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,10 +13,9 @@
     License :: OSI Approved :: Apache Software License
     Operating System :: POSIX :: Linux
     Programming Language :: Python
-    Programming Language :: Python :: 2
-    Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 3.5
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
 
 [files]
 packages =
diff --git a/test-requirements.txt b/test-requirements.txt
index fb97fc6..9292937 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,8 +4,7 @@
 
 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
 
-sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
-sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD
+sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
 openstackdocstheme>=1.20.0 # Apache-2.0
 
 reno>=2.5.0 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index 048d70b..60be8ec 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,11 @@
 [tox]
-minversion = 2.0
-envlist = py3,py27,pep8
+minversion = 3.1.0
+envlist = py3,pep8
 skipsdist = True
+ignore_basepython_conflict=true
 
 [testenv]
+basepython = python3
 usedevelop = True
 install_command = pip install {opts} {packages}
 setenv =
@@ -14,29 +16,23 @@
 commands = python setup.py test --slowest --testr-args='{posargs}'
 
 [testenv:pep8]
-basepython = python3
 commands = flake8 {posargs}
 
 [testenv:venv]
-basepython = python3
 commands = {posargs}
 
 [testenv:cover]
-basepython = python3
 commands = python setup.py test --coverage --testr-args='{posargs}'
 
 [testenv:docs]
-basepython = python3
 commands = python setup.py build_sphinx
 
 [testenv:pdf-docs]
-basepython = python3
 whitelist_externals = make
 commands = sphinx-build -b latex doc/source doc/build/pdf
            make -C doc/build/pdf
 
 [testenv:debug]
-basepython = python3
 commands = oslo_debug_helper {posargs}
 
 [flake8]
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index ffb57a5..a579a12 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -9,22 +9,12 @@
         - ironic-standalone
         - ironic-standalone-train
         - ironic-standalone-stein
-        - ironic-dsvm-standalone-rocky:
-            voting: false
-        - ironic-dsvm-standalone-queens:
-            voting: false
         - ironic-tempest-functional-python3
         - ironic-tempest-functional-python3-train
         - ironic-tempest-functional-python3-stein
-        - ironic-tempest-dsvm-functional-python3-rocky:
-            voting: false
         - ironic-inspector-tempest
         - ironic-inspector-tempest-train
         - ironic-inspector-tempest-stein
-        - ironic-tempest-dsvm-ironic-inspector-rocky:
-            voting: false
-        - ironic-tempest-dsvm-ironic-inspector-queens:
-            voting: false
         # NOTE(dtantsur): these jobs cover rarely changed tests and are quite
         # unstable, so keep them non-voting.
         - ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode:
@@ -33,19 +23,11 @@
             voting: false
         - ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode-stein:
             voting: false
-        - ironic-tempest-dsvm-ipa-wholedisk-agent_ipmitool-tinyipa-multinode-rocky:
-            voting: false
-        - ironic-tempest-dsvm-ipa-wholedisk-agent_ipmitool-tinyipa-multinode-queens:
-            voting: false
         - ironic-inspector-tempest-discovery
         - ironic-inspector-tempest-discovery-train:
             voting: false
         - ironic-inspector-tempest-discovery-stein:
             voting: false
-        - ironic-inspector-tempest-dsvm-discovery-rocky:
-            voting: false
-        - ironic-inspector-tempest-dsvm-discovery-queens:
-            voting: false
     gate:
       queue: ironic
       jobs:
diff --git a/zuul.d/stable-jobs.yaml b/zuul.d/stable-jobs.yaml
index bfb6706..ec95d55 100644
--- a/zuul.d/stable-jobs.yaml
+++ b/zuul.d/stable-jobs.yaml
@@ -1,187 +1,78 @@
 - job:
     name: ironic-standalone-train
     parent: ironic-standalone
-    override-branch: stable/train
+    override-checkout: stable/train
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-standalone-stein
     parent: ironic-standalone
-    override-branch: stable/stein
+    override-checkout: stable/stein
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-dsvm-standalone-rocky
-    parent: ironic-standalone
-    override-branch: stable/rocky
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-dsvm-standalone-queens
-    parent: ironic-standalone
-    override-branch: stable/queens
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        FIXED_NETWORK_SIZE: 4096
-        EBTABLES_RACE_FIX: True
-        IRONIC_USE_MOD_WSGI: True
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-tempest-functional-python3-train
     parent: ironic-tempest-functional-python3
-    override-branch: stable/train
+    override-checkout: stable/train
 
 - job:
     name: ironic-tempest-functional-python3-stein
     parent: ironic-tempest-functional-python3
-    override-branch: stable/stein
+    override-checkout: stable/stein
 
-- job:
-    name: ironic-tempest-dsvm-functional-python3-rocky
-    parent: ironic-tempest-functional-python3
-    override-branch: stable/rocky
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        IRONIC_RPC_TRANSPORT: ""
-      devstack_services:
-        rabbit: True
 
 - job:
     name: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode-train
     parent: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode
-    override-branch: stable/train
+    override-checkout: stable/train
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode-stein
     parent: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode
-    override-branch: stable/stein
+    override-checkout: stable/stein
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-tempest-dsvm-ipa-wholedisk-agent_ipmitool-tinyipa-multinode-rocky
-    parent: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode
-    override-branch: stable/rocky
-    nodeset: openstack-two-node-xenial
-    vars:
-      devstack_localrc:
-        IRONIC_DEFAULT_BOOT_OPTION: netboot
-        FIXED_NETWORK_SIZE: 4096
-        IRONIC_DEFAULT_RESCUE_INTERFACE: agent
-        EBTABLES_RACE_FIX: True
-        PUBLIC_BRIDGE: br_ironic_vxlan
-        OVS_BRIDGE_MAPPINGS: 'mynetwork:brbm,public:br_ironic_vxlan'
-        USE_PYTHON3: False
-    group-vars:
-      subnode:
-        devstack_localrc:
-          OVS_BRIDGE_MAPPINGS: 'mynetwork:sub1brbm,public:br_ironic_vxlan'
-
-- job:
-    name: ironic-tempest-dsvm-ipa-wholedisk-agent_ipmitool-tinyipa-multinode-queens
-    parent: ironic-tempest-ipa-wholedisk-direct-tinyipa-multinode
-    override-branch: stable/queens
-    nodeset: openstack-two-node-xenial
-    vars:
-      devstack_localrc:
-        IRONIC_DEFAULT_BOOT_OPTION: netboot
-        FIXED_NETWORK_SIZE: 4096
-        IRONIC_DEFAULT_RESCUE_INTERFACE: agent
-        EBTABLES_RACE_FIX: True
-        PUBLIC_BRIDGE: br_ironic_vxlan
-        OVS_BRIDGE_MAPPINGS: 'mynetwork:brbm,public:br_ironic_vxlan'
-        USE_PYTHON3: False
-    group-vars:
-      subnode:
-        devstack_localrc:
-          OVS_BRIDGE_MAPPINGS: 'mynetwork:sub1brbm,public:br_ironic_vxlan'
+        USE_PYTHON3: True
 
 - job:
     name: ironic-inspector-tempest-train
     parent: ironic-inspector-tempest
-    override-branch: stable/train
+    override-checkout: stable/train
     vars:
       devstack_localrc:
         FIXED_NETWORK_SIZE: 4096
         EBTABLES_RACE_FIX: True
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-inspector-tempest-stein
     parent: ironic-inspector-tempest
-    override-branch: stable/stein
+    override-checkout: stable/stein
     vars:
       devstack_localrc:
         FIXED_NETWORK_SIZE: 4096
         EBTABLES_RACE_FIX: True
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-tempest-dsvm-ironic-inspector-rocky
-    parent: ironic-inspector-tempest
-    override-branch: stable/rocky
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        FIXED_NETWORK_SIZE: 4096
-        EBTABLES_RACE_FIX: True
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-tempest-dsvm-ironic-inspector-queens
-    parent: ironic-inspector-tempest
-    override-branch: stable/queens
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        FIXED_NETWORK_SIZE: 4096
-        EBTABLES_RACE_FIX: True
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-inspector-tempest-discovery-train
     parent: ironic-inspector-tempest-discovery
-    override-branch: stable/train
+    override-checkout: stable/train
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
+        USE_PYTHON3: True
 
 - job:
     name: ironic-inspector-tempest-discovery-stein
     parent: ironic-inspector-tempest-discovery
-    override-branch: stable/stein
+    override-checkout: stable/stein
     vars:
       devstack_localrc:
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-inspector-tempest-dsvm-discovery-rocky
-    parent: ironic-inspector-tempest-discovery
-    override-branch: stable/rocky
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        USE_PYTHON3: False
-
-- job:
-    name: ironic-inspector-tempest-dsvm-discovery-queens
-    parent: ironic-inspector-tempest-discovery
-    override-branch: stable/queens
-    nodeset: openstack-single-node-xenial
-    vars:
-      devstack_localrc:
-        USE_PYTHON3: False
+        USE_PYTHON3: True