class Resource(models.Resource): cli_help = 'Manage organizations within Ansible Tower.' endpoint = '/organizations/' deprecated_methods = ['associate_project', 'disassociate_project'] name = models.Field(unique=True) description = models.Field(required=False, display=False) users = models.ManyToManyField('user', method_name='') admins = models.ManyToManyField('user', method_name='admin') instance_groups = models.ManyToManyField('instance_group', method_name='ig')
def test_method_docs(self): f = models.ManyToManyField('user') class Foo(models.BaseResource): admins = f endpoint = '/foos/' expect_text = """Associate an admin with this foo. =====API DOCS===== Associate an admin with this foo. :param foo: Primary key or name of the foo to associate to. :type foo: str :param user: Primary key or name of the user to be associated. :type user: str :returns: Dictionary of only one key "changed", which indicates whether the associate succeeded. :rtype: dict =====API DOCS===== """.strip(' ') self.assertEqual(Foo.associate_admin.__doc__.strip(' '), expect_text) self.assertEqual( Foo.disassociate_admin.__doc__.strip(' '), expect_text.replace('associate', 'disassociate').replace( 'Associate', 'Disassociate'))
def test_configure_model(self): f = models.ManyToManyField('user') self.assertEqual(f.res_name, None) self.assertEqual(f.relationship, None) f.configure_model({'endpoint': '/organizations/'}, 'auditors') self.assertEqual(f.res_name, 'organization') self.assertEqual(f.relationship, 'auditors')
def test_resource_configuration(self): f = models.ManyToManyField('user') class Foo(models.BaseResource): admins = f endpoint = '/foos/' self.assertEqual(Foo.m2m_fields, [f]) self.assertTrue(hasattr(Foo, 'associate_admin')) self.assertTrue(hasattr(Foo, 'disassociate_admin'))
class Resource(models.Resource): """A resource for teams.""" cli_help = 'Manage teams within Ansible Tower.' endpoint = '/teams/' identity = ('organization', 'name') name = models.Field(unique=True) organization = models.Field(type=types.Related('organization')) description = models.Field(required=False, display=False) users = models.ManyToManyField('user', method_name='')
def test_disassociate_method(self): f = models.ManyToManyField('user', res_name='organization', relationship='admins') method = f.disassociate_method class Resource: def _disassoc(self): pass res = Resource() with mock.patch.object(res, '_disassoc') as mock_disassoc: method(res, organization=1, user=2) mock_disassoc.assert_called_once_with('admins', 1, 2)
class Resource(models.Resource): """A resource for inventories.""" cli_help = 'Manage inventory within Ansible Tower.' endpoint = '/inventories/' identity = ('organization', 'name') dependencies = ['organization'] related = ['host', 'group', 'inventory_source'] name = models.Field(unique=True) description = models.Field(required=False, display=False) organization = models.Field(type=types.Related('organization')) variables = models.Field(type=types.Variables(), required=False, display=False, help_text='Inventory variables, use "@" to get from file.') kind = models.Field(type=click.Choice(['', 'smart']), required=False, display=False, help_text='The kind field. Cannot be modified after created.') host_filter = models.Field(required=False, display=False, help_text='The host_filter field. Only useful when kind=smart.') insights_credential = models.Field(display=False, required=False, type=types.Related('credential')) instance_groups = models.ManyToManyField('instance_group', method_name='ig') @resources.command(ignore_defaults=True) def batch_update(self, pk=None, **kwargs): """Update all related inventory sources of the given inventory. Note global option --format is not available here, as the output would always be JSON-formatted. =====API DOCS===== Update all related inventory sources of the given inventory. :param pk: Primary key of the given inventory. :type pk: int :param `**kwargs`: Keyword arguments list of available fields used for searching resource objects. :returns: A JSON object of update status of the given inventory. :rtype: dict =====API DOCS===== """ res = self.get(pk=pk, **kwargs) url = self.endpoint + '%d/%s/' % (res['id'], 'update_inventory_sources') return client.post(url, data={}).json() batch_update.format_freezer = 'json'
class Resource(models.SurveyResource): """A resource for workflow job templates.""" cli_help = 'Manage workflow job templates.' endpoint = '/workflow_job_templates/' unified_job_type = '/workflow_jobs/' dependencies = ['organization'] related = ['survey_spec', 'workflow_nodes', 'schedules', 'labels'] workflow_node_types = ['success_nodes', 'failure_nodes', 'always_nodes'] name = models.Field(unique=True) description = models.Field(required=False, display=False) extra_vars = models.Field( type=types.Variables(), required=False, display=False, multiple=True, help_text='Extra variables used by Ansible in YAML or key=value ' 'format. Use @ to get YAML from a file. Use the option ' 'multiple times to add multiple extra variables.') organization = models.Field(type=types.Related('organization'), required=False) survey_enabled = models.Field( type=bool, required=False, display=False, help_text='Prompt user for job type on launch.') allow_simultaneous = models.Field(type=bool, required=False, display=False) survey_spec = models.Field( type=types.Variables(), required=False, display=False, help_text='On write commands, perform extra POST to the ' 'survey_spec endpoint.') labels = models.ManyToManyField('label', res_name='workflow') @staticmethod def _workflow_node_structure(node_results): ''' Takes the list results from the API in `node_results` and translates this data into a dictionary organized in a human-readable heirarchial structure ''' # Build list address translation, and create backlink lists node_list_pos = {} for i, node_result in enumerate(node_results): for rel in ['success', 'failure', 'always']: node_result['{0}_backlinks'.format(rel)] = [] node_list_pos[node_result['id']] = i # Populate backlink lists for node_result in node_results: for rel in ['success', 'failure', 'always']: for sub_node_id in node_result['{0}_nodes'.format(rel)]: j = node_list_pos[sub_node_id] node_results[j]['{0}_backlinks'.format(rel)].append( node_result['id']) # Find the root nodes root_nodes = [] for node_result in node_results: is_root = True for rel in ['success', 'failure', 'always']: if node_result['{0}_backlinks'.format(rel)] != []: is_root = False break if is_root: root_nodes.append(node_result['id']) # Create network dictionary recursively from root nodes def branch_schema(node_id): i = node_list_pos[node_id] node_dict = node_results[i] ret_dict = {"id": node_id} for fd in NODE_STANDARD_FIELDS: val = node_dict.get(fd, None) if val is not None: if fd == 'unified_job_template': job_type = node_dict['summary_fields'][ 'unified_job_template']['unified_job_type'] ujt_key = JOB_TYPES[job_type] ret_dict[ujt_key] = val else: ret_dict[fd] = val for rel in ['success', 'failure', 'always']: sub_node_id_list = node_dict['{0}_nodes'.format(rel)] if len(sub_node_id_list) == 0: continue relationship_name = '{0}_nodes'.format(rel) ret_dict[relationship_name] = [] for sub_node_id in sub_node_id_list: ret_dict[relationship_name].append( branch_schema(sub_node_id)) return ret_dict schema_dict = [] for root_node_id in root_nodes: schema_dict.append(branch_schema(root_node_id)) return schema_dict def _get_schema(self, wfjt_id): """ Returns a dictionary that represents the node network of the workflow job template """ node_res = get_resource('node') node_results = node_res.list(workflow_job_template=wfjt_id, all_pages=True)['results'] return self._workflow_node_structure(node_results) @resources.command(use_fields_as_options=False) @click.argument('wfjt', type=types.Related('workflow')) @click.argument('node_network', type=types.Variables(), required=False) def schema(self, wfjt, node_network=None): """ Convert YAML/JSON content into workflow node objects if node_network param is given. If not, print a YAML representation of the node network. =====API DOCS===== Convert YAML/JSON content into workflow node objects if ``node_network`` param is given. If not, print a YAML representation of the node network. :param wfjt: Primary key or name of the workflow job template to run schema against. :type wfjt: str :param node_network: JSON- or YAML-formatted string representing the topology of the workflow job template be updated to. :type node_network: str :returns: The latest topology (possibly after modification) of the workflow job template. :rtype: dict =====API DOCS===== """ existing_network = self._get_schema(wfjt) if not isinstance(existing_network, list): existing_network = [] if node_network is None: if settings.format == 'human': settings.format = 'yaml' return existing_network if hasattr(node_network, 'read'): node_network = node_network.read() node_network = string_to_dict(node_network, allow_kv=False, require_dict=False) if not isinstance(node_network, list): node_network = [] _update_workflow( [TreeNode(x, wfjt, include_id=True) for x in existing_network], [TreeNode(x, wfjt) for x in node_network]) if settings.format == 'human': settings.format = 'yaml' return self._get_schema(wfjt) @resources.command(use_fields_as_options=False) @click.option('--workflow', type=types.Related('workflow')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of job template to relate to.') def associate_notification_template(self, workflow, notification_template, status): """Associate a notification template from this workflow. =====API DOCS===== Associate a notification template from this workflow job template. :param workflow: The workflow job template to associate to. :type workflow: str :param notification_template: The notification template to be associated. :type notification_template: str :param status: type of notification this notification template should be associated to. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc('notification_templates_%s' % status, workflow, notification_template) @resources.command(use_fields_as_options=False) @click.option('--workflow', type=types.Related('workflow')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of job template to relate to.') def disassociate_notification_template(self, workflow, notification_template, status): """Disassociate a notification template from this workflow. =====API DOCS===== Disassociate a notification template from this workflow job template. :param job_template: The workflow job template to disassociate from. :type job_template: str :param notification_template: The notification template to be disassociated. :type notification_template: str :param status: type of notification this notification template should be disassociated from. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc('notification_templates_%s' % status, workflow, notification_template)
class Resource(models.Resource): """A resource for schedules.""" cli_help = 'Manage schedules within Ansible Tower.' endpoint = '/schedules/' # General fields. name = models.Field(unique=True) description = models.Field(required=False, display=False) # Unified jt fields. note these fields will only be used during creation. # Plus, one and only one field should be provided. job_template = models.Field(type=types.Related('job_template'), required=False, display=False) inventory_source = models.Field(type=types.Related('inventory_source'), required=False, display=False) project = models.Field(type=types.Related('project'), required=False, display=False) # Schedule-specific fields. unified_job_template = models.Field(required=False, type=int, help_text='Integer used to display' ' unified job template in result, ' 'Do not use it for create/' 'modify.') enabled = models.Field(required=False, type=click.BOOL, default=True, help_text='Whether this schedule will be used.', show_default=True) rrule = models.Field(required=False, display=False, help_text='Schedule rules specifications which is' ' less than 255 characters.') # Prompts extra_data = models.Field(type=types.Variables(), required=False, display=False, help_text='Extra data for ' 'schedule rules in the form of a .json file.') inventory = models.Field(type=types.Related('inventory'), required=False, display=False) credentials = models.ManyToManyField('credential') job_type = models.Field(required=False, display=False) job_tags = models.Field(required=False, display=False) skip_tags = models.Field(required=False, display=False) limit = models.Field(required=False, display=False) diff_mode = models.Field(type=bool, required=False, display=False) verbosity = models.Field( display=False, type=types.MappedChoice([ (0, 'default'), (1, 'verbose'), (2, 'more_verbose'), (3, 'debug'), (4, 'connection'), (5, 'winrm'), ]), required=False, ) def _get_patch_url(self, url, pk): urlTokens = url.split('/') if len(urlTokens) > 3: # reconstruct url to prevent a rare corner case where resources # cannot be constructed independently. Open to modification if # API convention changes. url = '/'.join(urlTokens[:1] + urlTokens[-2:]) return super(Resource, self)._get_patch_url(url, pk)
class Resource(models.Resource): """A resource for credentials.""" cli_help = 'Manage hosts belonging to a group within an inventory.' endpoint = '/hosts/' identity = ('inventory', 'name') name = models.Field(unique=True) description = models.Field(required=False, display=False) inventory = models.Field(type=types.Related('inventory')) enabled = models.Field(type=bool, required=False) variables = models.Field( type=types.Variables(), required=False, display=False, help_text='Host variables, use "@" to get from file.') insights_system_id = models.Field(required=False, display=False) groups = models.ManyToManyField('group', method_name='') @resources.command(ignore_defaults=True, no_args_is_help=False) @click.option('--group', type=types.Related('group'), help='List hosts that are children of this group.') @click.option('--host-filter', help='List hosts filtered by this fact search query string.') def list(self, group=None, host_filter=None, **kwargs): """Return a list of hosts. =====API DOCS===== Retrieve a list of hosts. :param group: Primary key or name of the group whose hosts will be listed. :type group: 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 =====API DOCS===== """ if group: kwargs['query'] = kwargs.get('query', ()) + (('groups__in', group), ) if host_filter: kwargs['query'] = kwargs.get('query', ()) + (('host_filter', host_filter), ) return super(Resource, self).list(**kwargs) @resources.command(ignore_defaults=True) def list_facts(self, pk=None, **kwargs): """Return a JSON object of all available facts of the given host. Note global option --format is not available here, as the output would always be JSON-formatted. =====API DOCS===== List all available facts of the given host. :param pk: Primary key of the target host. :type pk: int :param `**kwargs`: Keyword arguments list of available fields used for searching resource objects. :returns: A JSON object of all available facts of the given host. :rtype: dict =====API DOCS===== """ res = self.get(pk=pk, **kwargs) url = self.endpoint + '%d/%s/' % (res['id'], 'ansible_facts') return client.get(url, params={}).json() list_facts.format_freezer = 'json' @resources.command(ignore_defaults=True) def insights(self, pk=None, **kwargs): """Return a JSON object of host insights. Note global option --format is not available here, as the output would always be JSON-formatted. =====API DOCS===== List host insights. :param pk: Primary key of the target host. :type pk: int :param `**kwargs`: Keyword arguments list of available fields used for searching resource objects. :returns: A JSON object of host insights. :rtype: dict =====API DOCS===== """ res = self.get(pk=pk, **kwargs) url = self.endpoint + '%d/%s/' % (res['id'], 'insights') return client.get(url, params={}).json() insights.format_freezer = 'json'
class Resource(models.Resource): """A resource for workflow nodes.""" cli_help = 'Manage nodes inside of a workflow job template.' endpoint = '/workflow_job_template_nodes/' identity = ('id', ) workflow_job_template = models.Field(key='-W', type=types.Related('workflow')) unified_job_template = models.Field(required=False) # Prompts extra_data = models.Field(type=types.Variables(), required=False, display=False, help_text='Extra data for ' 'schedule rules in the form of a .json file.') inventory = models.Field(type=types.Related('inventory'), required=False, display=False) credential = models.Field(type=types.Related('credential'), required=False, display=False) credentials = models.ManyToManyField('credential') job_type = models.Field(required=False, display=False) job_tags = models.Field(required=False, display=False) skip_tags = models.Field(required=False, display=False) limit = models.Field(required=False, display=False) diff_mode = models.Field(type=bool, required=False, display=False) verbosity = models.Field( display=False, type=types.MappedChoice([ (0, 'default'), (1, 'verbose'), (2, 'more_verbose'), (3, 'debug'), (4, 'connection'), (5, 'winrm'), ]), required=False, ) def __new__(cls, *args, **kwargs): for attr in ['create', 'modify', 'list']: setattr(cls, attr, unified_job_template_options(getattr(cls, attr))) return super(Resource, cls).__new__(cls, *args, **kwargs) @staticmethod def _forward_rel_name(rel): return '{0}_nodes'.format(rel) @staticmethod def _reverse_rel_name(rel): return 'workflowjobtemplatenodes_{0}'.format(rel) def _parent_filter(self, parent, relationship, **kwargs): """ Returns filtering parameters to limit a search to the children of a particular node by a particular relationship. """ if parent is None or relationship is None: return {} parent_filter_kwargs = {} query_params = ((self._reverse_rel_name(relationship), parent), ) parent_filter_kwargs['query'] = query_params if kwargs.get('workflow_job_template', None) is None: parent_data = self.read(pk=parent)['results'][0] parent_filter_kwargs['workflow_job_template'] = parent_data[ 'workflow_job_template'] return parent_filter_kwargs @unified_job_template_options def _get_or_create_child(self, parent, relationship, **kwargs): ujt_pk = kwargs.get('unified_job_template', None) if ujt_pk is None: raise exceptions.BadRequest( 'A child node must be specified by one of the options ' 'unified-job-template, job-template, project, or ' 'inventory-source') kwargs.update(self._parent_filter(parent, relationship, **kwargs)) response = self.read(fail_on_no_results=False, fail_on_multiple_results=False, **kwargs) if len(response['results']) == 0: debug.log('Creating new workflow node.', header='details') return client.post(self.endpoint, data=kwargs).json() else: return response['results'][0] def _assoc_or_create(self, relationship, parent, child, **kwargs): if child is None: child_data = self._get_or_create_child(parent, relationship, **kwargs) return child_data return self._assoc(self._forward_rel_name(relationship), parent, child) @resources.command @unified_job_template_options @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node'), required=False) def associate_success_node(self, parent, child=None, **kwargs): """Add a node to run on success. =====API DOCS===== Add a node to run on success. :param parent: Primary key of parent node to associate success node to. :type parent: int :param child: Primary key of child node to be associated. :type child: int :param `**kwargs`: Fields used to create child node if ``child`` is not provided. :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc_or_create('success', parent, child, **kwargs) @resources.command(use_fields_as_options=False) @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node')) def disassociate_success_node(self, parent, child): """Remove success node. The resulatant 2 nodes will both become root nodes. =====API DOCS===== Remove success node. :param parent: Primary key of parent node to disassociate success node from. :type parent: int :param child: Primary key of child node to be disassociated. :type child: int :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc(self._forward_rel_name('success'), parent, child) @resources.command @unified_job_template_options @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node'), required=False) def associate_failure_node(self, parent, child=None, **kwargs): """Add a node to run on failure. =====API DOCS===== Add a node to run on failure. :param parent: Primary key of parent node to associate failure node to. :type parent: int :param child: Primary key of child node to be associated. :type child: int :param `**kwargs`: Fields used to create child node if ``child`` is not provided. :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc_or_create('failure', parent, child, **kwargs) @resources.command(use_fields_as_options=False) @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node')) def disassociate_failure_node(self, parent, child): """Remove a failure node link. The resulatant 2 nodes will both become root nodes. =====API DOCS===== Remove a failure node link. :param parent: Primary key of parent node to disassociate failure node from. :type parent: int :param child: Primary key of child node to be disassociated. :type child: int :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc(self._forward_rel_name('failure'), parent, child) @resources.command @unified_job_template_options @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node'), required=False) def associate_always_node(self, parent, child=None, **kwargs): """Add a node to always run after the parent is finished. =====API DOCS===== Add a node to always run after the parent is finished. :param parent: Primary key of parent node to associate always node to. :type parent: int :param child: Primary key of child node to be associated. :type child: int :param `**kwargs`: Fields used to create child node if ``child`` is not provided. :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc_or_create('always', parent, child, **kwargs) @resources.command(use_fields_as_options=False) @click.argument('parent', type=types.Related('node')) @click.argument('child', type=types.Related('node')) def disassociate_always_node(self, parent, child): """Remove an always node link. The resultant 2 nodes will both become root nodes. =====API DOCS===== Remove an always node link. :param parent: Primary key of parent node to disassociate always node from. :type parent: int :param child: Primary key of child node to be disassociated. :type child: int :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc(self._forward_rel_name('always'), parent, child)
class Resource(models.SurveyResource): """A resource for job templates.""" cli_help = 'Manage job templates.' endpoint = '/job_templates/' dependencies = ['inventory', 'credential', 'project', 'vault_credential'] related = [ 'survey_spec', 'notification_templates', 'extra_credentials', 'schedules' ] name = models.Field(unique=True) description = models.Field(required=False, display=False) job_type = models.Field( required=False, display=False, type=click.Choice(['run', 'check']), ) inventory = models.Field(type=types.Related('inventory'), required=False) project = models.Field(type=types.Related('project')) playbook = models.Field() credential = models.Field(display=False, required=False, type=types.Related('credential')) vault_credential = models.Field(type=types.Related('credential'), required=False, display=False) forks = models.Field(type=int, required=False, display=False) limit = models.Field(required=False, display=False) verbosity = models.Field( display=False, type=types.MappedChoice([ (0, 'default'), (1, 'verbose'), (2, 'more_verbose'), (3, 'debug'), (4, 'connection'), (5, 'winrm'), ]), required=False, ) extra_vars = models.Field( type=types.Variables(), required=False, display=False, multiple=True, help_text='Extra variables used by Ansible in YAML or key=value ' 'format. Use @ to get YAML from a file.') job_tags = models.Field(required=False, display=False) force_handlers = models.Field(type=bool, required=False, display=False) skip_tags = models.Field(required=False, display=False) start_at_task = models.Field(required=False, display=False) timeout = models.Field( type=int, required=False, display=False, help_text= 'The amount of time (in seconds) to run before the task is canceled.') use_fact_cache = models.Field( type=bool, required=False, display=False, help_text='If enabled, Tower will act as an Ansible Fact Cache Plugin;' ' persisting facts at the end of a playbook run to the database' ' and caching facts for use by Ansible.') host_config_key = models.Field( required=False, display=False, help_text='Allow Provisioning Callbacks using this host config key') ask_diff_mode_on_launch = models.Field( type=bool, required=False, display=False, help_text='Ask diff mode on launch.') ask_variables_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for extra_vars on launch.') ask_limit_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for host limits on launch.') ask_tags_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for job tags on launch.') ask_skip_tags_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for tags to skip on launch.') ask_job_type_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for job type on launch.') ask_verbosity_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for verbosity on launch.') ask_inventory_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for inventory on launch.') ask_credential_on_launch = models.Field( type=bool, required=False, display=False, help_text='Prompt user for machine credential on launch.') survey_enabled = models.Field( type=bool, required=False, display=False, help_text='Prompt user for job type on launch.') become_enabled = models.Field(type=bool, required=False, display=False) diff_mode = models.Field( type=bool, required=False, display=False, help_text='If enabled, textual changes made to any templated files on' ' the host are shown in the standard output.') allow_simultaneous = models.Field(type=bool, required=False, display=False) survey_spec = models.Field( type=types.Variables(), required=False, display=False, help_text='On write commands, perform extra POST to the ' 'survey_spec endpoint.') labels = models.ManyToManyField('label') instance_groups = models.ManyToManyField('instance_group', method_name='ig') def write(self, *args, **kwargs): # Provide a default value for job_type, but only in creation of JT if (kwargs.get('create_on_missing', False) and (not kwargs.get('job_type', None))): kwargs['job_type'] = 'run' return super(Resource, self).write(*args, **kwargs) @resources.command(use_fields_as_options=False) @click.option('--job-template', type=types.Related('job_template')) @click.option('--credential', type=types.Related('credential')) def associate_credential(self, job_template, credential): """Associate a credential with this job template. =====API DOCS===== Associate a credential with this job template. :param job_template: The job template to associate to. :type job_template: str :param credential: The credential to be associated. :type credential: str :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc('extra_credentials', job_template, credential) @resources.command(use_fields_as_options=False) @click.option('--job-template', type=types.Related('job_template')) @click.option('--credential', type=types.Related('credential')) def disassociate_credential(self, job_template, credential): """Disassociate a credential with this job template. =====API DOCS===== Disassociate a credential from this job template. :param job_template: The job template to disassociate fom. :type job_template: str :param credential: The credential to be disassociated. :type credential: str :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc('extra_credentials', job_template, credential) @resources.command(use_fields_as_options=False) @click.option('--job-template', type=types.Related('job_template')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of job template to relate to.') def associate_notification_template(self, job_template, notification_template, status): """Associate a notification template from this job template. =====API DOCS===== Associate a notification template from this job template. :param job_template: The job template to associate to. :type job_template: str :param notification_template: The notification template to be associated. :type notification_template: str :param status: type of notification this notification template should be associated to. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc('notification_templates_%s' % status, job_template, notification_template) @resources.command(use_fields_as_options=False) @click.option('--job-template', type=types.Related('job_template')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of job template to relate to.') def disassociate_notification_template(self, job_template, notification_template, status): """Disassociate a notification template from this job template. =====API DOCS===== Disassociate a notification template from this job template. :param job_template: The job template to disassociate from. :type job_template: str :param notification_template: The notification template to be disassociated. :type notification_template: str :param status: type of notification this notification template should be disassociated from. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc('notification_templates_%s' % status, job_template, notification_template) @resources.command(use_fields_as_options=('extra_vars')) @click.option('--host-config-key', help='Job-template-specific string used to authenticate ' 'host during provisioning callback.') def callback(self, pk=None, host_config_key='', extra_vars=None): """Contact Tower and request a configuration update using this job template. =====API DOCS===== Contact Tower and request a provisioning callback using this job template. :param pk: Primary key of the job template to run provisioning callback against. :type pk: int :param host_config_key: Key string used to authenticate the callback host. :type host_config_key: str :param extra_vars: Extra variables that are passed to provisioning callback. :type extra_vars: array of str :returns: A dictionary of a single key "changed", which indicates whether the provisioning callback is successful. :rtype: dict =====API DOCS===== """ url = self.endpoint + '%s/callback/' % pk if not host_config_key: host_config_key = client.get(url).json()['host_config_key'] post_data = {'host_config_key': host_config_key} if extra_vars: post_data['extra_vars'] = parser.process_extra_vars( list(extra_vars), force_json=True) r = client.post(url, data=post_data, auth=None) if r.status_code == 201: return {'changed': True}
def test_method_name(self): f = models.ManyToManyField('user', relationship='auditors', res_name='organization') self.assertEqual(f.associate_method_name, 'associate_auditor') self.assertEqual(f.disassociate_method_name, 'disassociate_auditor')
class Resource(models.Resource): cli_help = 'Manage organizations within Ansible Tower.' endpoint = '/organizations/' deprecated_methods = ['associate_project', 'disassociate_project'] name = models.Field(unique=True) description = models.Field(required=False, display=False) users = models.ManyToManyField('user', method_name='') admins = models.ManyToManyField('user', method_name='admin') instance_groups = models.ManyToManyField('instance_group', method_name='ig') custom_virtualenv = models.Field(required=False, display=False) @resources.command(use_fields_as_options=False) @click.option('--organization', type=types.Related('organization')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of inventory_sync to relate to.') def associate_notification_template(self, organization, notification_template, status): """Associate a notification template from this organization. =====API DOCS===== Associate a notification template from this organization. :param organization: The organization to associate to. :type organization: str :param notification_template: The notification template to be associated. :type notification_template: str :param status: type of notification this notification template should be associated to. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the association succeeded. :rtype: dict =====API DOCS===== """ return self._assoc('notification_templates_%s' % status, organization, notification_template) @resources.command(use_fields_as_options=False) @click.option('--organization', type=types.Related('organization')) @click.option('--notification-template', type=types.Related('notification_template')) @click.option('--status', type=click.Choice(['any', 'error', 'success']), required=False, default='any', help='Specify job run status' ' of inventory_sync to relate to.') def disassociate_notification_template(self, organization, notification_template, status): """Disassociate a notification template from this organization. =====API DOCS===== Disassociate a notification template from this organization. :param organization: The organization to disassociate from. :type organization: str :param notification_template: The notification template to be disassociated. :type notification_template: str :param status: type of notification this notification template should be disassociated from. :type status: str :returns: Dictionary of only one key "changed", which indicates whether the disassociation succeeded. :rtype: dict =====API DOCS===== """ return self._disassoc('notification_templates_%s' % status, organization, notification_template)