From daf79de37eeb2e6b53f8b2b75a338b0abee7a35d Mon Sep 17 00:00:00 2001
From: Rafael Castillo <rcastill@redhat.com>
Date: Thu, 10 Nov 2022 21:30:33 -0700
Subject: [PATCH] Updates server_group for 2.0.0

Replaces the policies argument with policy. The policies attribute is
ancient and was superceded by policy a long time ago[0][1]. Since policy and
policies differ in type, we can't simply alias these and had to drop
policies to keep the implementation small.

[0] https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id59
[1] https://docs.openstack.org/api-ref/compute/#create-server-group

Change-Id: Icd658fb179ca0a96276b033130b9dddfedd84236
---
 .zuul.yaml                              |   1 +
 ci/roles/server_group/defaults/main.yml |  11 ++
 ci/roles/server_group/tasks/main.yml    |  72 ++++++++++++
 ci/run-collection.yml                   |   1 +
 plugins/modules/server_group.py         | 144 +++++++++++++-----------
 5 files changed, 163 insertions(+), 66 deletions(-)
 create mode 100644 ci/roles/server_group/defaults/main.yml
 create mode 100644 ci/roles/server_group/tasks/main.yml

diff --git a/.zuul.yaml b/.zuul.yaml
index 347958b..8ef9fb3 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -104,6 +104,7 @@
         security_group
         security_group_rule
         server
+        server_group
         server_metadata
         server_volume
         stack
diff --git a/ci/roles/server_group/defaults/main.yml b/ci/roles/server_group/defaults/main.yml
new file mode 100644
index 0000000..a7d6ed2
--- /dev/null
+++ b/ci/roles/server_group/defaults/main.yml
@@ -0,0 +1,11 @@
+expected_fields:
+  - id
+  - name
+  - policy
+  - policies
+  - member_ids
+  - metadata
+  - project_id
+  - rules
+  - user_id
+
diff --git a/ci/roles/server_group/tasks/main.yml b/ci/roles/server_group/tasks/main.yml
new file mode 100644
index 0000000..11889af
--- /dev/null
+++ b/ci/roles/server_group/tasks/main.yml
@@ -0,0 +1,72 @@
+- name: Create server group
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    policy: affinity
+  register: server_group
+
+- name: Assert changed
+  assert:
+    that: server_group is changed
+
+- name: Assert return values
+  assert:
+    that: item in server_group.server_group
+  loop: "{{ expected_fields }}"
+
+- name: Create server group again
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    policy: affinity
+  register: server_group
+
+- name: Assert not changed
+  assert:
+    that: server_group is not changed
+
+- name: Delete server group
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    state: absent
+  register: server_group
+
+- name: Assert changed
+  assert:
+    that: server_group is changed
+
+- name: Delete server group again
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    state: absent
+  register: server_group
+
+- name: Assert not changed
+  assert:
+    that: server_group is not changed
+
+- name: Create server group with rules
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    policy: anti-affinity
+    rules:
+      max_server_per_host: 2
+  register: server_group
+
+- name: Assert changed
+  assert:
+    that: server_group is changed
+
+- name: Assert return values
+  assert:
+    that: item in server_group.server_group
+  loop: "{{ expected_fields }}"
+
+- name: Delete server group
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
+    name: ansible_group
+    state: absent
diff --git a/ci/run-collection.yml b/ci/run-collection.yml
index 98c9afd..11c8ea8 100644
--- a/ci/run-collection.yml
+++ b/ci/run-collection.yml
@@ -57,6 +57,7 @@
     - { role: security_group, tags: security_group }
     - { role: security_group_rule, tags: security_group_rule }
     - { role: server, tags: server }
+    - { role: server_group, tags: server_group }
     - { role: server_metadata, tags: server_metadata }
     - { role: server_volume, tags: server_volume }
     - { role: stack, tags: stack }
diff --git a/plugins/modules/server_group.py b/plugins/modules/server_group.py
index 6c24b4e..2b0fa69 100644
--- a/plugins/modules/server_group.py
+++ b/plugins/modules/server_group.py
@@ -14,10 +14,9 @@ description:
 options:
    state:
      description:
-        - Indicate desired state of the resource. When I(state) is 'present',
-          then I(policies) is required.
+        - Indicate desired state of the resource. When I(state) is C(present),
+          then I(policy) is required.
      choices: ['present', 'absent']
-     required: false
      default: present
      type: str
    name:
@@ -25,15 +24,17 @@ options:
         - Server group name.
      required: true
      type: str
-   policies:
+   policy:
      description:
-        - A list of one or more policy names to associate with the server
-          group. The list must contain at least one policy name. The current
-          valid policy names are anti-affinity, affinity, soft-anti-affinity
-          and soft-affinity.
-     required: false
-     type: list
-     elements: str
+        - Represents the current name of the policy.
+     choices: ['anti-affinity', 'affinity', 'soft-anti-affinity', 'soft-affinity']
+     type: str
+   rules:
+     description:
+        - Rules to be applied to the policy. Currently, only the
+          C(max_server_per_host) rule is supported for the C(anti-affinity)
+          policy.
+     type: dict
 requirements:
     - "python >= 3.6"
     - "openstacksdk"
@@ -43,58 +44,68 @@ extends_documentation_fragment:
 '''
 
 EXAMPLES = '''
-# Create a server group with 'affinity' policy.
-- openstack.cloud.server_group:
+- name: Create a server group with 'affinity' policy.
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
     state: present
-    auth:
-      auth_url: https://identity.example.com
-      username: admin
-      password: admin
-      project_name: admin
     name: my_server_group
-    policies:
-      - affinity
+    policy: affinity
 
-# Delete 'my_server_group' server group.
-- openstack.cloud.server_group:
+- name: Delete 'my_server_group' server group.
+  openstack.cloud.server_group:
+    cloud: "{{ cloud }}"
     state: absent
-    auth:
-      auth_url: https://identity.example.com
-      username: admin
-      password: admin
-      project_name: admin
     name: my_server_group
 '''
 
 RETURN = '''
-id:
-    description: Unique UUID.
-    returned: success
-    type: str
-name:
-    description: The name of the server group.
-    returned: success
-    type: str
-policies:
-    description: A list of one or more policy names of the server group.
-    returned: success
-    type: list
-members:
-    description: A list of members in the server group.
-    returned: success
-    type: list
-metadata:
-    description: Metadata key and value pairs.
-    returned: success
+server_group:
+    description: Object representing the server group
+    returned: On success when I(state) is present
     type: dict
-project_id:
-    description: The project ID who owns the server group.
-    returned: success
-    type: str
-user_id:
-    description: The user ID who owns the server group.
-    returned: success
-    type: str
+    contains:
+        id:
+            description: Unique UUID.
+            returned: always
+            type: str
+        name:
+            description: The name of the server group.
+            returned: always
+            type: str
+        policies:
+            description: |
+                A list of exactly one policy name to associate with the group.
+                Available until microversion 2.63
+            returned: always
+            type: list
+        policy:
+            description: |
+                Represents the name of the policy. Available from version 2.64 on.
+            returned: always
+            type: str
+        member_ids:
+            description: The list of members in the server group
+            returned: always
+            type: list
+        metadata:
+            description: Metadata key and value pairs.
+            returned: always
+            type: dict
+        project_id:
+            description: The project ID who owns the server group.
+            returned: always
+            type: str
+        rules:
+            description: |
+                The rules field, applied to the policy. Currently, only the
+                C(max_server_per_host) rule is supported for the
+                C(anti-affinity) policy.
+            returned: always
+            type: dict
+        user_id:
+            description: The user ID who owns the server group.
+            returned: always
+            type: str
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
@@ -103,12 +114,17 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O
 class ServerGroupModule(OpenStackModule):
     argument_spec = dict(
         name=dict(required=True),
-        policies=dict(type='list', elements='str'),
+        policy=dict(choices=['anti-affinity', 'affinity', 'soft-anti-affinity',
+                             'soft-affinity']),
         state=dict(default='present', choices=['absent', 'present']),
+        rules=dict(type='dict')
     )
 
     module_kwargs = dict(
         supports_check_mode=True,
+        required_if=[
+            ('state', 'present', ['policy'])
+        ],
     )
 
     def _system_state_change(self, state, server_group):
@@ -121,10 +137,9 @@ class ServerGroupModule(OpenStackModule):
 
     def run(self):
         name = self.params['name']
-        policies = self.params['policies']
         state = self.params['state']
 
-        server_group = self.conn.get_server_group(name)
+        server_group = self.conn.compute.find_server_group(name)
 
         if self.ansible.check_mode:
             self.exit_json(
@@ -134,22 +149,19 @@ class ServerGroupModule(OpenStackModule):
         changed = False
         if state == 'present':
             if not server_group:
-                if not policies:
-                    self.fail_json(
-                        msg="Parameter 'policies' is required in Server Group "
-                            "Create"
-                    )
-                server_group = self.conn.create_server_group(name, policies)
+                kwargs = {k: self.params[k]
+                          for k in ['name', 'policy', 'rules']
+                          if self.params[k] is not None}
+                server_group = self.conn.compute.create_server_group(**kwargs)
                 changed = True
 
             self.exit_json(
                 changed=changed,
-                id=server_group['id'],
-                server_group=server_group
+                server_group=server_group.to_dict(computed=False)
             )
         if state == 'absent':
             if server_group:
-                self.conn.delete_server_group(server_group['id'])
+                self.conn.compute.delete_server_group(server_group)
                 changed = True
             self.exit_json(changed=changed)
 
-- 
GitLab