Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions plugins/module_utils/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from ..module_utils.utils import PayloadMapper
from ..module_utils.rest_client import RestClient
from ..module_utils.typed_classes import TypedDNSConfigToAnsible
from ..module_utils.typed_classes import TypedRoleToAnsible

from typing import Union, Any

Expand All @@ -22,7 +22,7 @@ def __init__(self, uuid: str, name: str):
self.name = name

@classmethod
def from_ansible(cls, ansible_data: dict[Any, Any]) -> None:
def from_ansible(cls, ansible_data: Any) -> None:
pass

@classmethod
Expand All @@ -40,7 +40,7 @@ def from_hypercore(
def to_hypercore(self) -> None:
pass

def to_ansible(self) -> TypedDNSConfigToAnsible:
def to_ansible(self) -> TypedRoleToAnsible:
return dict(
uuid=self.uuid,
name=self.name,
Expand Down
27 changes: 25 additions & 2 deletions plugins/module_utils/typed_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,36 @@ class TypedSupportTunnelToAnsible(TypedDict):
code: Union[int, None]


# User to ansible return dict.
class TypedUserToAnsible(TypedDict):
uuid: str
username: str
full_name: str
roles: list[TypedRoleToAnsible]
session_limit: int


# Role to ansible return dict.
class TypedRoleToAnsible(TypedDict):
uuid: str
name: str


# Ansible module return Diff dict {before:{} after:{}}
class TypedDiff(TypedDict):
before: Union[
dict[Any, Any], TypedRegistrationToAnsible, TypedSupportTunnelToAnsible, None
dict[Any, Any],
TypedRegistrationToAnsible,
TypedSupportTunnelToAnsible,
TypedUserToAnsible,
None,
]
after: Union[
dict[Any, Any], TypedRegistrationToAnsible, TypedSupportTunnelToAnsible, None
dict[Any, Any],
TypedRegistrationToAnsible,
TypedSupportTunnelToAnsible,
TypedUserToAnsible,
None,
]


Expand Down
52 changes: 34 additions & 18 deletions plugins/module_utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
from __future__ import annotations

__metaclass__ = type

from ..module_utils.utils import PayloadMapper
from ..module_utils.role import Role
from ..module_utils.rest_client import RestClient
from ..module_utils.typed_classes import TypedUserToAnsible
from typing import Union


class User(PayloadMapper):
def __init__(self, uuid, username, full_name, role_uuids, session_limit):
def __init__(
self,
uuid: str,
username: str,
full_name: str,
role_uuids: list[str],
session_limit: int,
):
self.uuid = uuid
self.username = username
self.full_name = full_name
Expand All @@ -25,22 +35,22 @@ def from_ansible(cls):
pass

@classmethod
def from_hypercore(cls, hypercore_data):
user_dict = hypercore_data
if not user_dict: # In case for get_record, return None if no result is found
def from_hypercore(cls, hypercore_data: dict) -> Union[User, None]:
# In case for get_record, return None if no result is found
if not hypercore_data:
return None
return cls(
uuid=user_dict["uuid"],
username=user_dict["username"],
full_name=user_dict["fullName"],
role_uuids=user_dict["roleUUIDs"],
session_limit=user_dict["sessionLimit"],
uuid=hypercore_data["uuid"],
username=hypercore_data["username"],
full_name=hypercore_data["fullName"],
role_uuids=hypercore_data["roleUUIDs"],
session_limit=hypercore_data["sessionLimit"],
)

def to_hypercore(self):
pass

def to_ansible(self, rest_client: RestClient):
def to_ansible(self, rest_client: RestClient) -> TypedUserToAnsible:
return dict(
uuid=self.uuid,
username=self.username,
Expand All @@ -54,11 +64,13 @@ def to_ansible(self, rest_client: RestClient):
session_limit=self.session_limit,
)

def __eq__(self, other):
def __eq__(self, other: object) -> bool:
"""
One User is equal to another if it has ALL attributes exactly the same.
This method is used only in tests.
"""
if not isinstance(other, User):
return NotImplemented
return all(
(
self.uuid == other.uuid,
Expand All @@ -70,7 +82,9 @@ def __eq__(self, other):
)

@classmethod
def get_user_from_uuid(cls, user_uuid, rest_client: RestClient, must_exist=False):
def get_user_from_uuid(
cls, user_uuid, rest_client: RestClient, must_exist: bool = False
) -> Union[User, None]:
hypercore_dict = rest_client.get_record(
"/rest/v1/User/{0}".format(user_uuid), must_exist=must_exist
)
Expand All @@ -79,23 +93,25 @@ def get_user_from_uuid(cls, user_uuid, rest_client: RestClient, must_exist=False

@classmethod
def get_user_from_username(
cls, username, rest_client: RestClient, must_exist=False
):
cls, username, rest_client: RestClient, must_exist: bool = False
) -> Union[User, None]:
hypercore_dict = rest_client.get_record(
"/rest/v1/User", {"username": username}, must_exist=must_exist
)
user = cls.from_hypercore(hypercore_dict)
return user

def delete(self, rest_client: RestClient, check_mode=False):
def delete(self, rest_client: RestClient, check_mode: bool = False) -> None:
rest_client.delete_record(f"/rest/v1/User/{self.uuid}", check_mode)
# returned:
# {
# "taskTag": "",
# "createdUUID": ""
# }

def update(self, rest_client: RestClient, payload, check_mode=False):
def update(
self, rest_client: RestClient, payload, check_mode: bool = False
) -> None:
rest_client.update_record(f"/rest/v1/User/{self.uuid}", payload, check_mode)
# returned:
# {
Expand All @@ -104,12 +120,12 @@ def update(self, rest_client: RestClient, payload, check_mode=False):
# }

@classmethod
def create(cls, rest_client: RestClient, payload, check_mode=False):
def create(cls, rest_client: RestClient, payload, check_mode=False) -> User:
task_tag = rest_client.create_record("/rest/v1/User", payload, check_mode)
user = cls.get_user_from_uuid(
task_tag["createdUUID"], rest_client, must_exist=True
)
return user
return user # type: ignore # user is never None
# returned
# {
# "taskTag": "",
Expand Down
59 changes: 37 additions & 22 deletions plugins/modules/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
short_description: Creates, updates or deletes local hypercore user accounts.
description:
- Creates, updates or deletes local hypercore user accounts.
- The module in general is NOT idempotent. If C(password) needs to be changed, then module will report `changed=True`,
even if new password value is the same as old password value.
version_added: 1.2.0
extends_documentation_fragment:
- scale_computing.hypercore.cluster_instance
Expand Down Expand Up @@ -115,18 +117,22 @@
from ..module_utils.rest_client import CachedRestClient
from ..module_utils.user import User
from ..module_utils.role import Role
from ..module_utils.typed_classes import TypedUserToAnsible, TypedDiff
from typing import List, Tuple, Union, Dict, Any


def get_role_uuids(module, rest_client: RestClient):
def get_role_uuids(module: AnsibleModule, rest_client: RestClient) -> List[str]:
# CachedRestClient is beneficial here since we use for loop and make get requests to the same endpoint many times
role_uuids = []
for role_name in module.params["roles"]:
role = Role.get_role_from_name(role_name, rest_client, must_exist=True)
role_uuids.append(role.uuid)
role_uuids.append(role.uuid) # type: ignore # since must_exist=True, role is never None
return role_uuids


def data_for_create_user(module, rest_client: RestClient):
def data_for_create_user(
module: AnsibleModule, rest_client: RestClient
) -> Dict[Any, Any]:
data = {}
data["username"] = module.params[
"username"
Expand All @@ -142,7 +148,9 @@ def data_for_create_user(module, rest_client: RestClient):
return data


def create_user(module, rest_client: RestClient):
def create_user(
module: AnsibleModule, rest_client: RestClient
) -> Tuple[bool, TypedUserToAnsible, TypedDiff]:
data = data_for_create_user(module, rest_client)
user = User.create(rest_client, data).to_ansible(rest_client)
return (
Expand All @@ -152,7 +160,9 @@ def create_user(module, rest_client: RestClient):
)


def data_for_update_user(module, rest_client: RestClient, user):
def data_for_update_user(
module: AnsibleModule, rest_client: RestClient, user: User
) -> Dict[Any, Any]:
data = {}
if module.params["username_new"]:
if user.username != module.params["username_new"]:
Expand All @@ -174,35 +184,40 @@ def data_for_update_user(module, rest_client: RestClient, user):
return data


def update_user(module, rest_client: RestClient, user):
def update_user(
module: AnsibleModule, rest_client: RestClient, user: User
) -> Tuple[bool, TypedUserToAnsible, TypedDiff]:
data = data_for_update_user(module, rest_client, user)
if data:
user.update(rest_client, data)
user_after = User.get_user_from_uuid(
user.uuid, rest_client, must_exist=True
).to_ansible(rest_client)
user = user.to_ansible(rest_client)
user_after = User.get_user_from_uuid(user.uuid, rest_client, must_exist=True)
user_after_to_ansible = user_after.to_ansible(rest_client) # type: ignore # user_after is never None
user_to_ansible = user.to_ansible(rest_client)
return (
True,
user_after,
dict(before=user, after=user_after),
user_after_to_ansible,
dict(before=user_to_ansible, after=user_after_to_ansible),
)
user = user.to_ansible(rest_client)
user_to_ansible = user.to_ansible(rest_client)
return (
False,
user,
dict(before=user, after=user),
user_to_ansible,
dict(before=user_to_ansible, after=user_to_ansible),
)


def delete_user(rest_client: RestClient, user):
def delete_user(
rest_client: RestClient, user: Union[User, None]
) -> Tuple[bool, Union[TypedUserToAnsible, Dict[None, None]], TypedDiff]:
if not user:
return False, dict(), dict(before={}, after={})
return (False, dict(), dict(before={}, after={}))
user.delete(rest_client)
return True, dict(), dict(before=user.to_ansible(rest_client), after={})
return (True, dict(), dict(before=user.to_ansible(rest_client), after={}))


def run(module, rest_client: RestClient):
def run(
module: AnsibleModule, rest_client: RestClient
) -> Tuple[bool, Union[TypedUserToAnsible, Dict[None, None]], TypedDiff]:
user = User.get_user_from_username(
module.params["username"], rest_client, must_exist=False
)
Expand All @@ -211,11 +226,11 @@ def run(module, rest_client: RestClient):
return update_user(module, rest_client, user)
else:
return create_user(module, rest_client)
if module.params["state"] == "absent":
else:
return delete_user(rest_client, user)


def main():
def main() -> None:
module = AnsibleModule(
supports_check_mode=False,
argument_spec=dict(
Expand Down Expand Up @@ -249,7 +264,7 @@ def main():

try:
client = Client.get_client(module.params["cluster_instance"])
rest_client = CachedRestClient(client)
rest_client = CachedRestClient(client) # type: ignore
changed, record, diff = run(module, rest_client)
module.exit_json(changed=changed, record=record, diff=diff)
except errors.ScaleComputingError as e:
Expand Down
12 changes: 9 additions & 3 deletions plugins/modules/user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,25 @@
from ..module_utils.client import Client
from ..module_utils.user import User
from ..module_utils.utils import get_query
from ..module_utils.typed_classes import TypedUserToAnsible
from typing import List, Union


def run(module, rest_client):
def run(
module: AnsibleModule, rest_client: RestClient
) -> List[Union[TypedUserToAnsible, None]]:
query = get_query(
module.params, "username", ansible_hypercore_map=dict(username="username")
)
return [
User.from_hypercore(hypercore_data=hypercore_dict).to_ansible(rest_client)
User.from_hypercore(hypercore_data=hypercore_dict).to_ansible( # type: ignore
rest_client
)
for hypercore_dict in rest_client.list_records("/rest/v1/User", query)
]


def main():
def main() -> None:
module = AnsibleModule(
supports_check_mode=True,
argument_spec=dict(
Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ module = [
"plugins.modules.snapshot_schedule",
"plugins.modules.snapshot_schedule_info",
"plugins.modules.task_wait",
"plugins.modules.user_info",
"plugins.modules.user",
"plugins.modules.vm_boot_devices",
"plugins.modules.vm_clone",
"plugins.modules.vm_disk",
Expand Down
Loading