Merge "Start ISCSI service explicity"
diff --git a/_states/ironicv1.py b/_states/ironicv1.py
index b6cd7df..2505f68 100644
--- a/_states/ironicv1.py
+++ b/_states/ironicv1.py
@@ -138,7 +138,7 @@
             try:
                 method_name = '{}_update'.format(resource)
                 resp = _ironicv1_call(
-                    method_name, name, properties=to_change,
+                    method_name, exact_resource['uuid'], properties=to_change,
                     microversion=microversion, cloud_name=cloud_name,
                 )
             except Exception as e:
@@ -177,6 +177,111 @@
         return _failed('find', name, resource)
 
 
+def volume_connector_present(name, node, volume_type, cloud_name,
+                             **kwargs):
+    """
+
+    :param name: alias for connector_id because of how salt works
+    :param node: node_ident
+    :param volume_type: type of volume
+    """
+    resource = 'volume_connector'
+    microversion = kwargs.pop('microversion', '1.32')
+    method_name = '{}_list'.format(resource)
+    exact_resource = filter(
+        lambda data: data['connector_id'] == name,
+        _ironicv1_call(method_name, node=node,
+                       cloud_name=cloud_name,
+                       microversion=microversion)['connectors'])
+    if len(exact_resource) == 0:
+        try:
+            method_name = 'node_get_details'
+            node_uuid = _ironicv1_call(
+                method_name, node, cloud_name=cloud_name,
+                microversion=microversion
+            )['uuid']
+        except Exception as e:
+            if 'Not Found' in str(e):
+                return _failed('not_found', node, 'node')
+            raise
+        try:
+            method_name = '{}_create'.format(resource)
+            resp = _ironicv1_call(
+                method_name, node_uuid, volume_type, name,
+                cloud_name=cloud_name, microversion=microversion, **kwargs)
+        except Exception as e:
+            log.exception('Ironic {0} create failed with {1}'.
+                          format('node', e))
+            return _failed('create', name, resource)
+        return _succeeded('create', name, resource, resp)
+    elif len(exact_resource) == 1:
+        exact_resource = exact_resource[0]
+        to_change = []
+        for prop in kwargs:
+            path = prop.replace('~', '~0').replace('/', '~1')
+            if prop in exact_resource:
+                if exact_resource[prop] != kwargs[prop]:
+                    to_change.append({
+                        'op': 'replace',
+                        'path': '/{}'.format(path),
+                        'value': kwargs[prop],
+                    })
+            else:
+                to_change.append({
+                    'op': 'add',
+                    'path': '/{}'.format(path),
+                    'value': kwargs[prop],
+                })
+        if to_change:
+            try:
+                method_name = '{}_update'.format(resource)
+                resp = _ironicv1_call(
+                    method_name, exact_resource['uuid'], properties=to_change,
+                    microversion=microversion, cloud_name=cloud_name,
+                )
+            except Exception as e:
+                log.exception(
+                    'Ironic {0} update failed with {1}'.format(resource,
+                                                               e))
+                return _failed('update', name, resource)
+            return _succeeded('update', name, resource, resp)
+        return _succeeded('no_changes', name, resource)
+    else:
+        return _failed('find', name, resource)
+
+
+def volume_connector_absent(name, cloud_name, node, **kwargs):
+    """
+
+    :param name: alias for connector_id because of how salt works
+    :param node: node ident
+    """
+    resource = 'volume_connector'
+    microversion = kwargs.pop('microversion', '1.32')
+    method_name = '{}_list'.format(resource)
+    exact_resource = filter(
+        lambda data: data['connector_id'] == name,
+        _ironicv1_call(method_name, node=node,
+                       cloud_name=cloud_name,
+                       microversion=microversion)['connectors'])
+    if len(exact_resource) == 0:
+            return _succeeded('absent', name, resource)
+    elif len(exact_resource) == 1:
+        connector_uuid = exact_resource[0]['uuid']
+        try:
+            method_name = '{}_delete'.format(resource)
+            _ironicv1_call(
+                method_name, connector_uuid, cloud_name=cloud_name,
+                microversion=microversion
+            )
+        except Exception as e:
+            log.error('Ironic delete {0} failed with {1}'.format(resource, e))
+            return _failed('delete', name, resource)
+        return _succeeded('delete', name, resource)
+    else:
+        return _failed('find', name, resource)
+
+
 def _succeeded(op, name, resource, changes=None):
     msg_map = {
         'create': '{0} {1} created',
@@ -199,7 +304,8 @@
         'create': '{0} {1} failed to create',
         'delete': '{0} {1} failed to delete',
         'update': '{0} {1} failed to update',
-        'find': '{0} {1} found multiple {0}'
+        'find': '{0} {1} found multiple {0}',
+        'not found': '{0} {1} not found',
     }
     changes_dict = {
         'name': name,
diff --git a/ironic/client.sls b/ironic/client.sls
index 7a12d23..142bb94 100644
--- a/ironic/client.sls
+++ b/ironic/client.sls
@@ -19,6 +19,9 @@
     {%- if node.network_interface is defined %}
     - network_interface: {{ node.network_interface }}
     {%- endif %}
+    {%- if node.storage_interface is defined %}
+    - storage_interface: {{ node.storage_interface }}
+    {%- endif %}
     {%- if node.microversion is defined %}
     - microversion: "{{ node.microversion }}"
     {%- endif %}
@@ -41,7 +44,18 @@
   {%- endfor %} # end for ports
   {%- endif %} # end if node.ports defined
 
+    {%- for vc_name, vc in node.get('volume_connectors', {}).iteritems() %}
+{{ node.name }}_volume_connector{{ vc_name }}_present:
+  ironicv1.volume_connector_present:
+    - name: {{ vc.get('name', vc_name) }}
+    - node: {{ node.name }}
+    - volume_type: {{ vc.volume_type }}
+    - cloud_name: {{ client.cloud_name }}
+
+    {%- endfor %} # end for volume_connectors
+
   {%- endfor %} # end for nodes
+
 {%- endfor %} # end client.nodes.iteritems
 
 {%- endif %} # end if client.enabled