From d0ae18f09deb3d9d81be93b1677cc9a7ab42d8b2 Mon Sep 17 00:00:00 2001
From: Paola Cernada <96987381+paolacernada@users.noreply.github.com>
Date: Thu, 6 Jun 2024 14:06:49 -0500
Subject: [PATCH] feat: Add user info module (#186)
### Description
This PR introduces the `metal_user_info.py` Ansible module, which
gathers detailed information about the current user in the Equinix Metal
environment.
### Changes Introduced
- **New Module:** `metal_user_info.py`
- **Functionality:** Retrieves user information including personal
details, account settings, and associated metadata.
- **API Integration:** Fetches data from the Equinix Metal API using the
provided API token.
- **Return Values:** Returns a dictionary containing user details such
as `id`, `full_name`, `email`, `default_organization_id`, and more.
Closes #137
---
README.md | 1 +
docs/modules/metal_user_info.md | 89 +++++++++
plugins/module_utils/metal/api_routes.py | 3 +
plugins/module_utils/metal/metal_api.py | 24 +++
plugins/modules/metal_user_info.py | 177 ++++++++++++++++++
requirements.txt | 1 +
.../targets/metal_user_info/tasks/main.yml | 59 ++++++
7 files changed, 354 insertions(+)
create mode 100644 docs/modules/metal_user_info.md
create mode 100644 plugins/modules/metal_user_info.py
create mode 100644 tests/integration/targets/metal_user_info/tasks/main.yml
diff --git a/README.md b/README.md
index 1606226..ed62a2e 100755
--- a/README.md
+++ b/README.md
@@ -61,6 +61,7 @@ Name | Description |
[equinix.cloud.metal_project_ssh_key_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_project_ssh_key_info.md)|Gather project SSH keys.|
[equinix.cloud.metal_reserved_ip_block_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_reserved_ip_block_info.md)|Gather list of reserved IP blocks|
[equinix.cloud.metal_ssh_key_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_ssh_key_info.md)|Gather personal SSH keys|
+[equinix.cloud.metal_user_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_user_info.md)|Gather information about the current user for Equinix Metal|
[equinix.cloud.metal_virtual_circuit_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_virtual_circuit_info.md)|Gather information about Equinix Metal Virtual Circuits|
[equinix.cloud.metal_vlan_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_vlan_info.md)|Gather VLANs.|
[equinix.cloud.metal_vrf_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/v0.6.2/docs/modules/metal_vrf_info.md)|Gather VRFs|
diff --git a/docs/modules/metal_user_info.md b/docs/modules/metal_user_info.md
new file mode 100644
index 0000000..68c8537
--- /dev/null
+++ b/docs/modules/metal_user_info.md
@@ -0,0 +1,89 @@
+# metal_user_info
+
+Gather information about the current user for Equinix Metal
+
+
+- [Examples](#examples)
+- [Parameters](#parameters)
+- [Return Values](#return-values)
+
+## Examples
+
+```yaml
+- name: Gather information about the current current user
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_user_info:
+ metal_api_token: "{{ lookup('env', 'METAL_API_TOKEN') }}"
+ register: result
+
+ - debug:
+ var: result
+
+```
+
+
+
+
+
+
+
+
+
+
+## Parameters
+
+| Field | Type | Required | Description |
+|-----------|------|----------|------------------------------------------------------------------------------|
+| `metal_api_token` |
`str` | **Required** | The Equinix Metal API token to use. |
+
+
+
+
+
+
+## Return Values
+
+
+
+### Sample Response for user
+```json
+{
+ "avatar_thumb_url": "https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm",
+ "avatar_url": "https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm",
+ "created_at": "2019-08-24T14:15:22Z",
+ "customdata": {},
+ "default_organization_id": "7498eaa8-62af-4757-81e0-959250fc9cd5",
+ "default_project_id": null,
+ "email": "john.doe@email.com",
+ "emails": [
+ {
+ "href": "string"
+ }
+ ],
+ "features": [],
+ "first_name": "John",
+ "full_name": "John Doe",
+ "href": "/metal/v1/users/497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "language": null,
+ "last_login_at": "2019-08-24T14:15:22Z",
+ "last_name": "Doe",
+ "mailing_address": null,
+ "max_projects": 0,
+ "number_of_ssh_keys": 0,
+ "opt_in": false,
+ "opt_in_updated_at": null,
+ "originating_idp": "Equinix",
+ "phone_number": null,
+ "restricted": false,
+ "short_id": "497f6eca",
+ "social_accounts": {},
+ "timezone": "America/New_York",
+ "two_factor_auth": "",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "verification_stage": "verified"
+}
+```
+
+
diff --git a/plugins/module_utils/metal/api_routes.py b/plugins/module_utils/metal/api_routes.py
index 6260fff..aabeb54 100644
--- a/plugins/module_utils/metal/api_routes.py
+++ b/plugins/module_utils/metal/api_routes.py
@@ -84,6 +84,9 @@ def get_routes(mpc):
('metal_virtual_circuit_vrf', action.GET): spec_types.Specs(
equinix_metal.InterconnectionsApi(mpc).get_virtual_circuit,
),
+ ('metal_user', action.GET): spec_types.Specs(
+ equinix_metal.UsersApi(mpc).find_current_user,
+ ),
# LISTERS
('metal_project_device', action.LIST): spec_types.Specs(
diff --git a/plugins/module_utils/metal/metal_api.py b/plugins/module_utils/metal/metal_api.py
index 8a82d23..a2ef23c 100644
--- a/plugins/module_utils/metal/metal_api.py
+++ b/plugins/module_utils/metal/metal_api.py
@@ -136,6 +136,27 @@ def extract_ids_from_projects_hrefs(resource: dict):
'ip_ranges': 'ip_ranges',
}
+METAL_USER_RESPONSE_ATTRIBUTE_MAP = {
+ 'avatar_thumb_url': 'avatar_thumb_url',
+ 'avatar_url': 'avatar_url',
+ 'created_at': 'created_at',
+ 'customdata': 'customdata',
+ 'default_organization_id': 'default_organization_id',
+ 'email': 'email',
+ 'emails': 'emails',
+ 'first_name': 'first_name',
+ 'full_name': 'full_name',
+ 'href': 'href',
+ 'id': 'id',
+ 'last_login_at': 'last_login_at',
+ 'last_name': 'last_name',
+ 'max_projects': 'max_projects',
+ 'short_id': 'short_id',
+ 'timezone': 'timezone',
+ 'two_factor_auth': 'two_factor_auth',
+ 'updated_at': 'updated_at'
+}
+
LIST_KEYS = [
'projects',
'devices',
@@ -153,6 +174,7 @@ def extract_ids_from_projects_hrefs(resource: dict):
'sessions', # metal_bgp_session_info
'plans',
'virtual_circuits',
+ 'users',
]
@@ -362,6 +384,8 @@ def get_attribute_mapper(resource_type):
return METAL_PLAN_RESPONSE_ATTRIBUTE_MAP
elif resource_type in virtual_circuit_resources:
return METAL_VIRTUAL_CIRCUIT_RESPONSE_ATTRIBUTE_MAP
+ elif resource_type == 'metal_user':
+ return METAL_USER_RESPONSE_ATTRIBUTE_MAP
else:
raise NotImplementedError("No mapper for resource type %s" % resource_type)
diff --git a/plugins/modules/metal_user_info.py b/plugins/modules/metal_user_info.py
new file mode 100644
index 0000000..ce8179e
--- /dev/null
+++ b/plugins/modules/metal_user_info.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# DOCUMENTATION, EXAMPLES, and RETURN are generated by
+# ansible_specdoc. Do not edit them directly.
+
+DOCUMENTATION = '''
+author: Equinix DevRel Team (@equinix)
+description: Gather information about the current user for Equinix Metal
+module: metal_user_info
+notes: []
+options:
+ metal_api_token:
+ description:
+ - The Equinix Metal API token to use.
+ required: true
+ type: str
+requirements: null
+short_description: Gather information about the current user for Equinix Metal
+'''
+EXAMPLES = '''
+- name: Gather information about the current current user
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_user_info:
+ metal_api_token: '{{ lookup(''env'', ''METAL_API_TOKEN'') }}'
+ register: result
+ - debug:
+ var: result
+'''
+RETURN = '''
+user:
+ description: Information about the current user.
+ returned: always
+ sample:
+ - avatar_thumb_url: https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm
+ avatar_url: https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm
+ created_at: '2019-08-24T14:15:22Z'
+ customdata: {}
+ default_organization_id: 7498eaa8-62af-4757-81e0-959250fc9cd5
+ email: john.doe@email.com
+ emails:
+ - href: string
+ first_name: John
+ full_name: John Doe
+ href: /metal/v1/users/497f6eca-6276-4993-bfeb-53cbbbba6f08
+ id: 497f6eca-6276-4993-bfeb-53cbbbba6f08
+ last_login_at: '2019-08-24T14:15:22Z'
+ last_name: Doe
+ max_projects: 0
+ short_id: 497f6eca
+ timezone: America/New_York
+ two_factor_auth: ''
+ updated_at: '2019-08-24T14:15:22Z'
+ type: dict
+'''
+
+# End of generated documentation
+
+from ansible.module_utils._text import to_native
+from ansible_specdoc.objects import (
+ SpecField,
+ FieldType,
+ SpecReturnValue,
+)
+import traceback
+
+from ansible_collections.equinix.cloud.plugins.module_utils.equinix import (
+ EquinixModule,
+ getSpecDocMeta,
+)
+
+# Define module specifications
+module_spec = dict(
+ metal_api_token=SpecField(
+ type=FieldType.string,
+ description=['The Equinix Metal API token to use.'],
+ required=True,
+ no_log=True,
+ ),
+ metal_api_url=SpecField(
+ type=FieldType.string,
+ description=['The Equinix Metal API URL to use.'],
+ required=True,
+ ),
+)
+
+# Define examples for the module documentation
+specdoc_examples = [
+ '''
+- name: Gather information about the current current user
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_user_info:
+ metal_api_token: "{{ lookup('env', 'METAL_API_TOKEN') }}"
+ register: result
+
+ - debug:
+ var: result
+''',
+]
+
+# Define return values for the module documentation
+return_values = [
+ {
+ "avatar_thumb_url": "https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm",
+ "avatar_url": "https://www.gravatar.com/avatar/49d55cbf53f2dae15bfa4c3a3fb884f9?d=mm",
+ "created_at": "2019-08-24T14:15:22Z",
+ "customdata": {},
+ "default_organization_id": "7498eaa8-62af-4757-81e0-959250fc9cd5",
+ "email": "john.doe@email.com",
+ "emails": [
+ {
+ "href": "string"
+ }
+ ],
+ "first_name": "John",
+ "full_name": "John Doe",
+ "href": "/metal/v1/users/497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "last_login_at": "2019-08-24T14:15:22Z",
+ "last_name": "Doe",
+ "max_projects": 0,
+ "short_id": "497f6eca",
+ "timezone": "America/New_York",
+ "two_factor_auth": "",
+ "updated_at": "2019-08-24T14:15:22Z"
+ }
+]
+
+# Define the metadata for SpecDoc
+SPECDOC_META = getSpecDocMeta(
+ short_description='Gather information about the current user for Equinix Metal',
+ description='Gather information about the current user for Equinix Metal',
+ examples=specdoc_examples,
+ options=module_spec,
+ return_values={
+ "user": SpecReturnValue(
+ description='Information about the current user.',
+ type=FieldType.dict,
+ sample=return_values,
+ ),
+ },
+)
+
+def main():
+ # Create an instance of EquinixModule with provided specifications
+ module = EquinixModule(
+ argument_spec=SPECDOC_META.ansible_spec,
+ is_info=True,
+ )
+ try:
+ # Check for syntax validity in provided parameters
+ module.params_syntax_check()
+
+ # Setting the id parameter to 'current_user' ensures the module fetches the correct information
+ # Since the id parameter is required by 'get_by_id', we need to set it to a value
+ module.params["id"] = "current_user"
+
+ # Fetch the current user's information using the get_by_id method
+ result = module.get_by_id("metal_user")
+
+ # Prepare the return value with the fetched user information
+ return_value = {"user": result}
+ except Exception as e:
+ # Capture any exception and fail the module execution with the error message
+ tr = traceback.format_exc()
+ module.fail_json(msg=to_native(e), exception=tr)
+
+ # Exit the module with the return value
+ module.exit_json(**return_value)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/requirements.txt b/requirements.txt
index deecf1c..cc5494f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
equinix-metal==0.9.0
ansible-specdoc>=0.0.13
pydantic >= 2
+requests
diff --git a/tests/integration/targets/metal_user_info/tasks/main.yml b/tests/integration/targets/metal_user_info/tasks/main.yml
new file mode 100644
index 0000000..32cfe3c
--- /dev/null
+++ b/tests/integration/targets/metal_user_info/tasks/main.yml
@@ -0,0 +1,59 @@
+- name: metal_user_info
+ module_defaults:
+ equinix.cloud.metal_user_info:
+ metal_api_token: '{{ metal_api_token }}'
+ equinix.cloud.metal_project:
+ metal_api_token: '{{ metal_api_token }}'
+ equinix.cloud.metal_project_info:
+ metal_api_token: '{{ metal_api_token }}'
+ metal_ua_prefix: '{{ metal_ua_prefix }}'
+ block:
+ - set_fact:
+ test_prefix: 'ansible-integration-test'
+
+ - name: gather current user info
+ equinix.cloud.metal_user_info:
+ metal_api_token: '{{ metal_api_token }}'
+ register: user_info
+
+ - debug:
+ msg: "User info: {{ user_info.user }}"
+
+ - assert:
+ that:
+ - user_info.user.avatar_thumb_url is defined
+ - user_info.user.avatar_url is defined
+ - user_info.user.created_at is defined
+ - user_info.user.customdata is defined
+ - user_info.user.default_organization_id is defined
+ - user_info.user.email is defined
+ - user_info.user.emails is defined
+ - user_info.user.first_name is defined
+ - user_info.user.full_name is defined
+ - user_info.user.href is defined
+ - user_info.user.id is defined
+ - user_info.user.last_login_at is defined
+ - user_info.user.last_name is defined
+ - user_info.user.max_projects is defined
+ - user_info.user.short_id is defined
+ - user_info.user.timezone is defined
+ - user_info.user.two_factor_auth is defined
+ - user_info.user.updated_at is defined
+
+ always:
+ - name: Announce teardown start
+ debug:
+ msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****"
+
+ - name: list test projects
+ equinix.cloud.metal_project_info:
+ metal_api_token: '{{ metal_api_token }}'
+ name: "{{ test_prefix }}"
+ register: test_projects_listed
+
+ - name: delete test projects
+ equinix.cloud.metal_project:
+ id: "{{ item.id }}"
+ state: absent
+ loop: "{{ test_projects_listed.resources }}"
+ ignore_errors: yes