# Copyright 2015, Ansible, Inc.
# Luke Sneeringer <lsneeringer@ansible.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import click
from tower_cli import get_resource, models, resources, exceptions as exc
from tower_cli.api import client
from tower_cli.cli import types
[docs]class Resource(models.Resource):
"""A resource for groups."""
cli_help = 'Manage groups belonging to an inventory.'
endpoint = '/groups/'
identity = ('inventory', 'name')
name = models.Field(unique=True)
description = models.Field(required=False, display=False)
inventory = models.Field(type=types.Related('inventory'))
variables = models.Field(type=types.Variables(), required=False, display=False,
help_text='Group variables, use "@" to get from file.')
def lookup_with_inventory(self, group, inventory=None):
group_res = get_resource('group')
if isinstance(group, int) or group.isdigit():
return group_res.get(int(group))
else:
return group_res.get(name=group, inventory=inventory)
def set_child_endpoint(self, parent, inventory=None):
parent_data = self.lookup_with_inventory(parent, inventory)
self.endpoint = '/groups/' + str(parent_data['id']) + '/children/'
return parent_data
[docs] @resources.command
@click.option('--parent', help='Parent group to nest this one inside of.')
def create(self, fail_on_found=False, force_on_exists=False, **kwargs):
"""Create a group.
=====API DOCS=====
Create a group.
:param parent: Primary key or name of the group which will be the parent of created group.
:type parent: str
:param fail_on_found: Flag that if set, the operation fails if an object matching the unique criteria
already exists.
:type fail_on_found: bool
:param force_on_exists: Flag that if set, then if a match is found on unique fields, other fields will
be updated to the provided values.; If unset, a match causes the request to be
a no-op.
:type force_on_exists: bool
:param `**kwargs`: Keyword arguments which, all together, will be used as POST body to create the
resource object.
:returns: A dictionary combining the JSON output of the created resource, as well as two extra fields:
"changed", a flag indicating if the resource is created successfully; "id", an integer which
is the primary key of the created object.
:rtype: dict
:raises tower_cli.exceptions.UsageError: When inventory is not provided in ``**kwargs`` and ``parent``
is not provided.
=====API DOCS=====
"""
if kwargs.get('parent', None):
parent_data = self.set_child_endpoint(parent=kwargs['parent'], inventory=kwargs.get('inventory', None))
kwargs['inventory'] = parent_data['inventory']
elif 'inventory' not in kwargs:
raise exc.UsageError('To create a group, you must provide a parent inventory or parent group.')
return super(Resource, self).create(fail_on_found=fail_on_found, force_on_exists=force_on_exists, **kwargs)
[docs] @resources.command(ignore_defaults=True, no_args_is_help=False)
@click.option('--root', is_flag=True, default=False,
help='Show only root groups (groups with no parent groups) within the given inventory.')
@click.option('--parent', help='Parent group to nest this one inside of.')
def list(self, root=False, **kwargs):
"""Return a list of groups.
=====API DOCS=====
Retrieve a list of groups.
:param root: Flag that if set, only root groups of a specific inventory will be listed.
:type root: bool
:param parent: Primary key or name of the group whose child groups will be listed.
:type parent: str
:param all_pages: Flag that if set, collect all pages of content from the API when returning results.
:type all_pages: bool
:param page: The page to show. Ignored if all_pages is set.
:type page: int
:param query: Contains 2-tuples used as query parameters to filter resulting resource objects.
:type query: list
:param `**kwargs`: Keyword arguments list of available fields used for searching resource objects.
:returns: A JSON object containing details of all resource objects returned by Tower backend.
:rtype: dict
:raises tower_cli.exceptions.UsageError: When ``root`` flag is on and ``inventory`` is not present in
``**kwargs``.
=====API DOCS=====
"""
# Option to list children of a parent group
if kwargs.get('parent', None):
self.set_child_endpoint(parent=kwargs['parent'], inventory=kwargs.get('inventory', None))
kwargs.pop('parent')
# Sanity check: If we got `--root` and no inventory, that's an error.
if root and not kwargs.get('inventory', None):
raise exc.UsageError('The --root option requires specifying an inventory also.')
# If we are tasked with getting root groups, do that.
if root:
inventory_id = kwargs['inventory']
r = client.get('/inventories/%d/root_groups/' % inventory_id)
return r.json()
# Return the superclass implementation.
return super(Resource, self).list(**kwargs)
[docs] @resources.command(use_fields_as_options=False)
@click.option('--group', help='The group to move.')
@click.option('--parent', help='Destination group to move into.')
@click.option('--inventory', type=types.Related('inventory'))
def associate(self, group, parent, **kwargs):
"""Associate this group with the specified group.
=====API DOCS=====
Associate this group with the specified group.
:param group: Primary key or name of the child group to associate.
:type group: str
:param parent: Primary key or name of the parent group to associate to.
:type parent: str
:param inventory: Primary key or name of the inventory the association should happen in.
:type inventory: str
:returns: Dictionary of only one key "changed", which indicates whether the association succeeded.
:rtype: dict
=====API DOCS=====
"""
parent_id = self.lookup_with_inventory(parent, kwargs.get('inventory', None))['id']
group_id = self.lookup_with_inventory(group, kwargs.get('inventory', None))['id']
return self._assoc('children', parent_id, group_id)
[docs] @resources.command(use_fields_as_options=False)
@click.option('--group', help='The group to move.')
@click.option('--parent', help='Destination group to move into.')
@click.option('--inventory', type=types.Related('inventory'))
def disassociate(self, group, parent, **kwargs):
"""Disassociate this group from the specified group.
=====API DOCS=====
Disassociate this group with the specified group.
:param group: Primary key or name of the child group to disassociate.
:type group: str
:param parent: Primary key or name of the parent group to disassociate from.
:type parent: str
:param inventory: Primary key or name of the inventory the disassociation should happen in.
:type inventory: str
:returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded.
:rtype: dict
=====API DOCS=====
"""
parent_id = self.lookup_with_inventory(parent, kwargs.get('inventory', None))['id']
group_id = self.lookup_with_inventory(group, kwargs.get('inventory', None))['id']
return self._disassoc('children', parent_id, group_id)