Skip to content

Commit

Permalink
Add and remove ansible_role to host/hostgroup (#927) (#929)
Browse files Browse the repository at this point in the history
* Add and remove ansible_role to host/hostgroup

Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>

* Add unittests for add and remove ansible_roles

Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>

---------

Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>
(cherry picked from commit 183828a)
  • Loading branch information
Gauravtalreja1 authored Jun 14, 2023
1 parent 27932aa commit d425944
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 27 deletions.
137 changes: 136 additions & 1 deletion nailgun/entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -3585,11 +3585,17 @@ def path(self, which=None):
/api/hostgroups/:hostgroup_id/rebuild_config
smart_class_parameters
/api/hostgroups/:hostgroup_id/smart_class_parameters
assign_ansible_roles
/api/hostgroups/:hostgroup_id/assign_ansible_roles
ansible_roles
/api/hostgroups/:hostgroup_id/ansible_roles
Otherwise, call ``super``.
"""
if which in (
'assign_ansible_roles',
'ansible_roles',
'clone',
'puppetclass_ids',
'rebuild_config',
Expand Down Expand Up @@ -3702,6 +3708,88 @@ def rebuild_config(self, synchronous=True, timeout=None, **kwargs):
response = client.put(self.path('rebuild_config'), **kwargs)
return _handle_response(response, self._server_config, synchronous, timeout)

def assign_ansible_roles(self, synchronous=True, timeout=None, **kwargs):
"""Add an Ansible Role to a hostgroup
Here is an example of how to use this method::
hostgroup.assign_ansible_roles(data={'ansible_role_ids':
[ansible_role_id1, ansible_role_id2]})
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
response = client.post(self.path('assign_ansible_roles'), **kwargs)
return _handle_response(response, self._server_config, synchronous, timeout)

def list_ansible_roles(self, synchronous=True, timeout=None, **kwargs):
"""List all Ansible Roles assigned to a hostgroup
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
response = client.get(self.path('ansible_roles'), **kwargs)
return _handle_response(response, self._server_config, synchronous, timeout)

def add_ansible_role(self, synchronous=True, timeout=None, **kwargs):
"""Add single Ansible Role to a hostgroup
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
path = f'{self.path("ansible_roles")}/{kwargs["data"].pop("ansible_role_id")}'
return _handle_response(
client.put(path, **kwargs), self._server_config, synchronous, timeout
)

def remove_ansible_role(self, synchronous=True, timeout=None, **kwargs):
"""Remove single Ansible Role assigned to a hostgroup
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
path = f'{self.path("ansible_roles")}/{kwargs["data"].pop("ansible_role_id")}'
return _handle_response(
client.delete(path, **kwargs), self._server_config, synchronous, timeout
)


class HostPackage(Entity):
"""A representation of a Host Package entity."""
Expand Down Expand Up @@ -4727,6 +4815,46 @@ def list_ansible_roles(self, synchronous=True, timeout=None, **kwargs):
response = client.get(self.path('ansible_roles'), **kwargs)
return _handle_response(response, self._server_config, synchronous, timeout)

def add_ansible_role(self, synchronous=True, timeout=None, **kwargs):
"""Add single Ansible Role to a host
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
path = f'{self.path("ansible_roles")}/{kwargs["data"].pop("ansible_role_id")}'
return _handle_response(
client.put(path, **kwargs), self._server_config, synchronous, timeout
)

def remove_ansible_role(self, synchronous=True, timeout=None, **kwargs):
"""Remove single Ansible Role assigned to a host
:param synchronous: What should happen if the server returns an HTTP
202 (accepted) status code? Wait for the task to complete if
``True``. Immediately return the server's response otherwise.
:param timeout: Maximum number of seconds to wait until timing out.
Defaults to ``nailgun.entity_mixins.TASK_TIMEOUT``.
:param kwargs: Arguments to pass to requests.
:returns: The server's response, with all JSON decoded.
:raises: ``requests.exceptions.HTTPError`` If the server responds with
an HTTP 4XX or 5XX message.
"""
kwargs = kwargs.copy()
kwargs.update(self._server_config.get_client_kwargs())
path = f'{self.path("ansible_roles")}/{kwargs["data"].pop("ansible_role_id")}'
return _handle_response(
client.delete(path, **kwargs), self._server_config, synchronous, timeout
)

def list_provisioning_templates(self, synchronous=True, timeout=None, **kwargs):
"""List all Provisioning templates assigned to a Host
Expand Down Expand Up @@ -8520,7 +8648,14 @@ def sync(self, synchronous=True, timeout=None, **kwargs):
return _handle_response(response, self._server_config, synchronous, timeout)


class AnsibleRoles(Entity):
class AnsibleRoles(
Entity,
EntityCreateMixin,
EntityDeleteMixin,
EntityReadMixin,
EntitySearchMixin,
EntityUpdateMixin,
):
"""A representation of Ansible Roles entity."""

def __init__(self, server_config=None, **kwargs):
Expand Down
120 changes: 94 additions & 26 deletions tests/test_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ def test_id_and_which(self):
(entities.Host, 'smart_class_parameters'),
(entities.Host, 'ansible_roles'),
(entities.Host, 'assign_ansible_roles'),
(entities.HostGroup, 'ansible_roles'),
(entities.HostGroup, 'assign_ansible_roles'),
(entities.HostGroup, 'clone'),
(entities.HostGroup, 'puppetclass_ids'),
(entities.HostGroup, 'rebuild_config'),
Expand Down Expand Up @@ -2158,11 +2160,13 @@ def setUpClass(cls):
(entities.ForemanTask(cfg).summary, 'get'),
(entities.Organization(**generic).download_debug_certificate, 'get'),
(entities.Host(**generic).add_puppetclass, 'post'),
(entities.Host(**generic).assign_ansible_roles, 'post'),
(entities.Host(**generic).enc, 'get'),
(entities.Host(**generic).errata, 'get'),
(entities.Host(**generic).errata_apply, 'put'),
(entities.Host(**generic).get_facts, 'get'),
(entities.Host(**generic).install_content, 'put'),
(entities.Host(**generic).list_ansible_roles, 'get'),
(entities.Host(**generic).list_scparams, 'get'),
(entities.Host(**generic).module_streams, 'get'),
(entities.Host(**generic).packages, 'get'),
Expand All @@ -2174,7 +2178,9 @@ def setUpClass(cls):
(entities.Host(**generic).bulk_traces, 'post'),
(entities.Host(**generic).bulk_resolve_traces, 'put'),
(entities.HostGroup(**generic).add_puppetclass, 'post'),
(entities.HostGroup(**generic).assign_ansible_roles, 'post'),
(entities.HostGroup(**generic).clone, 'post'),
(entities.HostGroup(**generic).list_ansible_roles, 'get'),
(entities.HostGroup(**generic).list_scparams, 'get'),
(entities.HostSubscription(**hostsubscription).add_subscriptions, 'put'),
(entities.HostSubscription(**hostsubscription).remove_subscriptions, 'put'),
Expand Down Expand Up @@ -2899,33 +2905,64 @@ def test_read(self):
self.read_json_pacther.stop()
self.read_pacther.stop()

def test_delete_puppetclass(self):
def test_add_func_with_id(self):
"""Check that helper method is sane.
Assert that:
* Method has a correct signature.
* Method calls `client.*` once.
* Method passes the right arguments to `client.*` and special
argument 'puppetclass_id' removed from data dict.
argument 'ansible_role_id' removed from data dict.
* Method calls `entities._handle_response` once.
* The result of `_handle_response(…)` is the return value.
"""
entity = self.entity
entity.id = 1
func_param_dict = {entity.add_ansible_role: 'ansible_role_id'}
for func in func_param_dict.keys():
self.assertEqual(inspect.getfullargspec(func), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {func_param_dict[func]: gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'put') as client_request:
response = func(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn(func_param_dict[func], client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)

def test_delete_func_with_id(self):
"""Check that helper method is sane.
Assert that:
* Method has a correct signature.
* Method calls `client.*` once.
* Method passes the right arguments to `client.*` and special
argument 'puppetclass_id/ansible_role_id' removed from data dict.
* Method calls `entities._handle_response` once.
* The result of `_handle_response(…)` is the return value.
"""
entity = self.entity
entity.id = 1
self.assertEqual(inspect.getfullargspec(entity.delete_puppetclass), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {'puppetclass_id': gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'delete') as client_request:
response = entity.delete_puppetclass(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn('puppetclass_id', client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)
func_param_dict = {
entity.delete_puppetclass: 'puppetclass_id',
entity.remove_ansible_role: 'ansible_role_id',
}
for func in func_param_dict.keys():
self.assertEqual(inspect.getfullargspec(func), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {func_param_dict[func]: gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'delete') as client_request:
response = func(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn(func_param_dict[func], client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)

def test_clone_hostgroup(self):
"""Test for :meth:`nailgun.entities.HostGroup.clone`
Expand Down Expand Up @@ -3051,31 +3088,62 @@ def test_no_facet_attributes(self):
self.assertNotIn('content_facet_attributes', read.call_args[0][1])
self.assertIn('content_facet_attributes', read.call_args[0][2])

def test_delete_puppetclass(self):
def test_add_func_with_id(self):
"""Check that helper method is sane.
Assert that:
* Method has a correct signature.
* Method calls `client.*` once.
* Method passes the right arguments to `client.*` and special
argument 'puppetclass_id' removed from data dict.
argument 'ansible_role_id' removed from data dict.
* Method calls `entities._handle_response` once.
* The result of `_handle_response(…)` is the return value.
"""
entity = entities.Host(self.cfg, id=1)
func_param_dict = {entity.add_ansible_role: 'ansible_role_id'}
for func in func_param_dict.keys():
self.assertEqual(inspect.getfullargspec(func), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {func_param_dict[func]: gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'put') as client_request:
response = func(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn(func_param_dict[func], client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)

def test_delete_func_with_id(self):
"""Check that helper method is sane.
Assert that:
* Method has a correct signature.
* Method calls `client.*` once.
* Method passes the right arguments to `client.*` and special
argument 'puppetclass_id/ansible_role_id' removed from data dict.
* Method calls `entities._handle_response` once.
* The result of `_handle_response(…)` is the return value.
"""
entity = entities.Host(self.cfg, id=1)
self.assertEqual(inspect.getfullargspec(entity.delete_puppetclass), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {'puppetclass_id': gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'delete') as client_request:
response = entity.delete_puppetclass(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn('puppetclass_id', client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)
func_param_dict = {
entity.delete_puppetclass: 'puppetclass_id',
entity.remove_ansible_role: 'ansible_role_id',
}
for func in func_param_dict.keys():
self.assertEqual(inspect.getfullargspec(func), EXPECTED_ARGSPEC)
kwargs = {'kwarg': gen_integer(), 'data': {func_param_dict[func]: gen_integer()}}
with mock.patch.object(entities, '_handle_response') as handlr:
with mock.patch.object(client, 'delete') as client_request:
response = func(**kwargs)
self.assertEqual(client_request.call_count, 1)
self.assertEqual(len(client_request.call_args[0]), 1)
self.assertNotIn(func_param_dict[func], client_request.call_args[1]['data'])
self.assertEqual(client_request.call_args[1], kwargs)
self.assertEqual(handlr.call_count, 1)
self.assertEqual(handlr.return_value, response)

def test_disassociate(self):
"""Disassociate host"""
Expand Down

0 comments on commit d425944

Please sign in to comment.