Add availability to ensure nodes moved from enroll to available

Starting from Ironic API microversion 1.11 newly-created nodes
added in enroll state. To move all nodes from enroll state to
available the following pillar might be used:

  ironic:
    client:
      node_state_transition:
        enabled: true
        enroll_to_available:
          provision_state: 'enroll'

Change-Id: If2249ba17eb3c4b89079a78fbd14b129ef80ed01
Related-prod: PROD-25757
(cherry picked from commit ebaa23c13fcc10fe23856e426e040ca33515dacd)
diff --git a/README.rst b/README.rst
index d37b93f..d7622b8 100644
--- a/README.rst
+++ b/README.rst
@@ -194,6 +194,22 @@
             strategy: ENCRYPT
             secret_key: secret
 
+Ensure nodes are in target state (available).
+Starting from 1.11 API microversion newly-created node is moved to `enroll`
+state. To move it to available the following client pillar might be used:
+
+.. code-block:: yaml
+
+  ironic:
+    client:
+      node_state_transition:
+        enabled: true
+        enroll_to_available:
+          provision_state: 'enroll'
+          pool_size: 5
+          sleep_time: 10
+          timeout: 15
+
 Change default options using configmap template settings
 ========================================================
 
diff --git a/_states/ironicv1.py b/_states/ironicv1.py
index 53f7f36..150cc15 100644
--- a/_states/ironicv1.py
+++ b/_states/ironicv1.py
@@ -284,18 +284,32 @@
         return _failed('find', name, resource)
 
 
-def enroll_to_state(name, node_names, cloud_name,
-                    pool_size=3, sleep_time=5, timeout=600, **kwargs):
+def ensure_target_state(name, cloud_name, node_names=None,
+                    provision_state=None, pool_size=3, sleep_time=5,
+                    timeout=600, **kwargs):
     """
+    Ensures nodes are moved to target state. As node distinguisher might take
+    either list of nodes specified in node names param or provision state.
+    Is designed to move nodes from enroll to available state for now.
 
     :param name: name of target state
     :param node_names: list of node names
-    :param cloud_name:
+    :param provision_state: current provision_state to filter nodes by.
+    :param cloud_name: the mane of cloud in clouds.yml
     :param pool_size: max size of nodes to change state in one moment
     :param sleep_time: time between checking states
     :param timeout: global timeout
     """
+
     microversion = kwargs.pop('microversion', '1.32')
+
+    if node_names is None:
+        nodes = _ironicv1_call('node_list', provision_state=provision_state,
+                                    cloud_name=cloud_name,
+                                    fields='name',
+                                    microversion=microversion)['nodes']
+        node_names = [n['name'] for n in nodes]
+
     Transition = collections.namedtuple('Transition',
                                         ('action', 'success', 'failures'))
     transition_map = {
@@ -305,7 +319,7 @@
     }
     nodes = [
         {'name': node, 'status': 'new', 'result': None,
-         'current_state': 'enroll'}
+         'current_state': provision_state or 'enroll'}
         for node in node_names
     ]
     counter = 0
diff --git a/ironic/client.sls b/ironic/client.sls
index 142bb94..f962315 100644
--- a/ironic/client.sls
+++ b/ironic/client.sls
@@ -53,9 +53,32 @@
     - cloud_name: {{ client.cloud_name }}
 
     {%- endfor %} # end for volume_connectors
-
   {%- endfor %} # end for nodes
-
 {%- endfor %} # end client.nodes.iteritems
 
+  {%- if client.get('node_state_transition', {}).get('enabled') %}
+    {%- if client.node_state_transition.enroll_to_available is defined %}
+    {# Ensure nodes are moved to available state, might take some time when automated cleaning is enabled #}
+ironic_nodes_in_available_state:
+  ironicv1.ensure_target_state:
+    - name: 'available'
+    {%- if client.node_state_transition.enroll_to_available.node_names is defined %}
+    - node_names: {{ client.node_state_transition.enroll_to_available.node_names }}
+    {%- endif %}
+    {%- if client.node_state_transition.enroll_to_available.provision_state is defined %}
+    - provision_state: {{ client.node_state_transition.enroll_to_available.provision_state }}
+    {%- endif %}
+    - cloud_name: {{ client.cloud_name }}
+    {%- if client.node_state_transition.enroll_to_available.pool_size is defined %}
+    - pool_size: {{ client.node_state_transition.enroll_to_available.pool_size }}
+    {%- endif %}
+    {%- if client.node_state_transition.enroll_to_available.sleep_time is defined %}
+    - sleep_time: {{ client.node_state_transition.enroll_to_available.sleep_time }}
+    {%- endif %}
+    {%- if client.node_state_transition.enroll_to_available.timeout is defined %}
+    - timeout: {{ client.node_state_transition.enroll_to_available.timeout }}
+    {%- endif %}
+    {%- endif %} # enroll_to_available
+  {%- endif %} #  node_state_transition.enabled
+
 {%- endif %} # end if client.enabled