Repository: ansible-collections/community.general

Issue:
open_iscsi login multiple selective portals of same target
### Summary

I am trying to login to multiple portals of same target. 
For eg: 
target = 2021-05.org.example:target1
portals = 10.10.10.1:3260 and 10.10.11.1:3260 (multiple network paths)

But I am not being able to since 'portal' argument is single string, so I cannot specify multiple portals. 

Also, I cannot do so using 2 tasks each for different portals, since the module checks if target is logged in or not instead of portal session.
```yaml
    - name: iscsi target1 portal1
      community.general.open_iscsi:
        portal: '10.10.10.1'
        port: '3260'
        target: '2021-05.org.example:target1'
        auto_node_startup: yes
        login: yes
        discover: yes

    - name: iscsi target1 portal2
      community.general.open_iscsi:
        portal: '10.10.11.1'
        port: '3260'
        target: '2021-05.org.example:target1'
        auto_node_startup: yes
        login: yes
        discover: no
```

The second task is tagged as unchanged by ansible and portal login does not occur.


### Issue Type

Feature Idea

### Component Name

open_iscsi

### Additional Information


```yaml (paste below)
    - name: iscsi target1 portal1
      community.general.open_iscsi:
        discover: yes
        portal: '10.10.10.1'
        port: '3260'
        target: '2021-05.org.example:target1'
        auto_node_startup: yes
        login: yes

    - name: iscsi target1 portal2
      community.general.open_iscsi:
        discover: no
        portal: '10.10.11.1'
        port: '3260'
        target: '2021-05.org.example:target1'
        auto_node_startup: yes
        login: yes
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
https://github.com/ansible-collections/community.general/blob/bef3c04d1c7dfaf87d00f3777dd844c1e09e2a99/plugins/modules/system/open_iscsi.py#L336

I think  'target_loggedon' check should consider portal as well in addition to target
cc @Tatsh @cwollinger @srvg @wltjr
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/system/open_iscsi.py b/plugins/modules/system/open_iscsi.py
--- a/plugins/modules/system/open_iscsi.py
+++ b/plugins/modules/system/open_iscsi.py
@@ -57,6 +57,11 @@
         - Whether the target node should be automatically connected at startup.
         type: bool
         aliases: [ automatic ]
+    auto_portal_startup:
+        description:
+        - Whether the target node portal should be automatically connected at startup.
+        type: bool
+        version_added: 3.2.0
     discover:
         description:
         - Whether the list of target nodes on the portal should be
@@ -102,10 +107,18 @@
   community.general.open_iscsi:
     login: no
     target: iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d
+
+- name: Override and disable automatic portal login on specific portal
+  community.general.open_iscsi:
+    login: false
+    portal: 10.1.1.250
+    auto_portal_startup: false
+    target: iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d
 '''
 
 import glob
 import os
+import re
 import socket
 import time
 
@@ -158,12 +171,18 @@ def iscsi_discover(module, portal, port):
         module.fail_json(cmd=cmd, rc=rc, msg=err)
 
 
-def target_loggedon(module, target):
+def target_loggedon(module, target, portal=None, port=None):
     cmd = '%s --mode session' % iscsiadm_cmd
     (rc, out, err) = module.run_command(cmd)
 
+    if portal is None:
+        portal = ""
+    if port is None:
+        port = ""
+
     if rc == 0:
-        return target in out
+        search_re = "%s:%s.*%s" % (re.escape(portal), port, re.escape(target))
+        return re.search(search_re, out) is not None
     elif rc == 21:
         return False
     else:
@@ -219,8 +238,14 @@ def target_device_node(module, target):
     return devdisks
 
 
-def target_isauto(module, target):
+def target_isauto(module, target, portal=None, port=None):
     cmd = '%s --mode node --targetname %s' % (iscsiadm_cmd, target)
+
+    if portal is not None:
+        if port is not None:
+            portal = '%s:%s' % (portal, port)
+        cmd = '%s --portal %s' % (cmd, portal)
+
     (rc, out, err) = module.run_command(cmd)
 
     if rc == 0:
@@ -233,16 +258,28 @@ def target_isauto(module, target):
         module.fail_json(cmd=cmd, rc=rc, msg=err)
 
 
-def target_setauto(module, target):
+def target_setauto(module, target, portal=None, port=None):
     cmd = '%s --mode node --targetname %s --op=update --name node.startup --value automatic' % (iscsiadm_cmd, target)
+
+    if portal is not None:
+        if port is not None:
+            portal = '%s:%s' % (portal, port)
+        cmd = '%s --portal %s' % (cmd, portal)
+
     (rc, out, err) = module.run_command(cmd)
 
     if rc > 0:
         module.fail_json(cmd=cmd, rc=rc, msg=err)
 
 
-def target_setmanual(module, target):
+def target_setmanual(module, target, portal=None, port=None):
     cmd = '%s --mode node --targetname %s --op=update --name node.startup --value manual' % (iscsiadm_cmd, target)
+
+    if portal is not None:
+        if port is not None:
+            portal = '%s:%s' % (portal, port)
+        cmd = '%s --portal %s' % (cmd, portal)
+
     (rc, out, err) = module.run_command(cmd)
 
     if rc > 0:
@@ -265,6 +302,7 @@ def main():
             # actions
             login=dict(type='bool', aliases=['state']),
             auto_node_startup=dict(type='bool', aliases=['automatic']),
+            auto_portal_startup=dict(type='bool'),
             discover=dict(type='bool', default=False),
             show_nodes=dict(type='bool', default=False),
         ),
@@ -288,6 +326,7 @@ def main():
     port = module.params['port']
     login = module.params['login']
     automatic = module.params['auto_node_startup']
+    automatic_portal = module.params['auto_portal_startup']
     discover = module.params['discover']
     show_nodes = module.params['show_nodes']
 
@@ -333,7 +372,7 @@ def main():
         result['nodes'] = nodes
 
     if login is not None:
-        loggedon = target_loggedon(module, target)
+        loggedon = target_loggedon(module, target, portal, port)
         if (login and loggedon) or (not login and not loggedon):
             result['changed'] |= False
             if login:
@@ -368,6 +407,22 @@ def main():
             result['changed'] |= True
             result['automatic_changed'] = True
 
+    if automatic_portal is not None:
+        isauto = target_isauto(module, target, portal, port)
+        if (automatic_portal and isauto) or (not automatic_portal and not isauto):
+            result['changed'] |= False
+            result['automatic_portal_changed'] = False
+        elif not check:
+            if automatic_portal:
+                target_setauto(module, target, portal, port)
+            else:
+                target_setmanual(module, target, portal, port)
+            result['changed'] |= True
+            result['automatic_portal_changed'] = True
+        else:
+            result['changed'] |= True
+            result['automatic_portal_changed'] = True
+
     module.exit_json(**result)
 
 


Test patch: