diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
commit | b643c52cf29ce5bbab738b43290af3556efa1ca9 (patch) | |
tree | 21d5c53d7a9b696627a255777cefdf6f78968824 /ansible_collections/theforeman/foreman/plugins | |
parent | Releasing progress-linux version 9.5.1+dfsg-1~progress7.99u1. (diff) | |
download | ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.tar.xz ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.zip |
Merging upstream version 10.0.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/theforeman/foreman/plugins')
11 files changed, 406 insertions, 193 deletions
diff --git a/ansible_collections/theforeman/foreman/plugins/inventory/foreman.py b/ansible_collections/theforeman/foreman/plugins/inventory/foreman.py index 1c165196b..076bf2171 100644 --- a/ansible_collections/theforeman/foreman/plugins/inventory/foreman.py +++ b/ansible_collections/theforeman/foreman/plugins/inventory/foreman.py @@ -32,7 +32,7 @@ DOCUMENTATION = ''' url: description: - URL of the Foreman server. - default: 'http://localhost:3000' + required: true env: - name: FOREMAN_SERVER - name: FOREMAN_SERVER_URL diff --git a/ansible_collections/theforeman/foreman/plugins/modules/compute_profile.py b/ansible_collections/theforeman/foreman/plugins/modules/compute_profile.py index dc2f3667f..5f87fdf22 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/compute_profile.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/compute_profile.py @@ -208,7 +208,7 @@ def main(): for volume in ca_module_params['vm_attrs']['volumes_attributes'].values(): if 'storage_pod' in volume: storage_pod = module.find_storage_pod(volume['storage_pod'], compute_resource, cluster) - volume['storage_pod'] = storage_pod['id'] + volume['storage_pod'] = storage_pod['name'] if 'storage_domain' in volume: storage_domain = module.find_storage_domain(volume['storage_domain'], compute_resource, cluster) volume['storage_domain'] = storage_domain['id'] diff --git a/ansible_collections/theforeman/foreman/plugins/modules/content_view_filter.py b/ansible_collections/theforeman/foreman/plugins/modules/content_view_filter.py index ae2d04f94..894784f83 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/content_view_filter.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/content_view_filter.py @@ -28,10 +28,6 @@ description: - Create and manage content View filters author: "Sean O'Keeffe (@sean797)" options: - architecture: - description: - - package architecture - type: str name: description: - Name of the Content View Filter @@ -46,7 +42,7 @@ options: - Name of the content view required: true type: str - filter_state: + state: description: - State of the content view filter default: present @@ -54,6 +50,8 @@ options: - present - absent type: str + aliases: + - filter_state repositories: description: - List of repositories that include name and product @@ -61,14 +59,6 @@ options: default: [] type: list elements: dict - rule_state: - description: - - State of the content view filter rule - default: present - choices: - - present - - absent - type: str filter_type: description: - Content view filter type @@ -81,54 +71,6 @@ options: - modulemd - deb type: str - rule_name: - description: - - Content view filter rule name or package name - - If omitted, the value of I(name) will be used if necessary - aliases: - - package_name - - package_group - - tag - type: str - date_type: - description: - - Search using the 'Issued On' or 'Updated On' - - Only valid on I(filter_type=erratum). - default: updated - choices: - - issued - - updated - type: str - end_date: - description: - - erratum end date (YYYY-MM-DD) - type: str - start_date: - description: - - erratum start date (YYYY-MM-DD) - type: str - errata_id: - description: - - erratum id - type: str - max_version: - description: - - package maximum version - type: str - min_version: - description: - - package minimum version - type: str - types: - description: - - erratum types (enhancement, bugfix, security) - default: ["bugfix", "enhancement", "security"] - type: list - elements: str - version: - description: - - package version - type: str inclusion: description: - Create an include filter @@ -187,57 +129,10 @@ entity: elements: dict ''' -from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import KatelloMixin, ForemanStatelessEntityAnsibleModule - -content_filter_spec = { - 'id': {}, - 'name': {}, - 'description': {}, - 'repositories': {'type': 'entity_list'}, - 'inclusion': {}, - 'content_view': {'type': 'entity'}, - 'filter_type': {'flat_name': 'type'}, - 'original_packages': {}, - 'original_module_streams': {}, -} - -content_filter_rule_erratum_spec = { - 'id': {}, - 'date_type': {}, - 'end_date': {}, - 'start_date': {}, - 'types': {'type': 'list'}, -} - -content_filter_rule_erratum_id_spec = { - 'id': {}, - 'errata_id': {}, - 'date_type': {}, -} +from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import KatelloMixin, ForemanEntityAnsibleModule -content_filter_rule_rpm_spec = { - 'id': {}, - 'rule_name': {'flat_name': 'name'}, - 'end_date': {}, - 'max_version': {}, - 'min_version': {}, - 'version': {}, - 'architecture': {}, -} -content_filter_rule_package_group_spec = { - 'id': {}, - 'rule_name': {'flat_name': 'name'}, - 'uuid': {}, -} - -content_filter_rule_docker_spec = { - 'id': {}, - 'rule_name': {'flat_name': 'name'}, -} - - -class KatelloContentViewFilterModule(KatelloMixin, ForemanStatelessEntityAnsibleModule): +class KatelloContentViewFilterModule(KatelloMixin, ForemanEntityAnsibleModule): pass @@ -246,36 +141,19 @@ def main(): foreman_spec=dict( name=dict(required=True), description=dict(), - repositories=dict(type='list', default=[], elements='dict'), + repositories=dict(type='entity_list', default=[], elements='dict'), inclusion=dict(type='bool', default=False), original_packages=dict(type='bool'), content_view=dict(type='entity', scope=['organization'], required=True), - filter_type=dict(required=True, choices=['rpm', 'package_group', 'erratum', 'docker', 'modulemd', 'deb']), - filter_state=dict(default='present', choices=['present', 'absent']), - rule_state=dict(default='present', choices=['present', 'absent']), - rule_name=dict(aliases=['package_name', 'package_group', 'tag']), - date_type=dict(default='updated', choices=['issued', 'updated']), - end_date=dict(), - errata_id=dict(), - max_version=dict(), - min_version=dict(), - start_date=dict(), - types=dict(default=["bugfix", "enhancement", "security"], type='list', elements='str'), - version=dict(), - architecture=dict(), + filter_type=dict(required=True, choices=['rpm', 'package_group', 'erratum', 'docker', 'modulemd', 'deb'], flat_name='type'), original_module_streams=dict(type='bool'), ), + argument_spec=dict( + state=dict(default='present', choices=['present', 'absent'], aliases=['filter_state']), + ), entity_opts=dict(scope=['content_view']), ) - filter_state = module.foreman_params.pop('filter_state') - rule_state = module.foreman_params.pop('rule_state') - - if module.foreman_params['filter_type'] == 'erratum': - module.foreman_params['rule_name'] = None - elif 'rule_name' not in module.foreman_params: - module.foreman_params['rule_name'] = module.foreman_params['name'] - with module.api_connection(): scope = module.scope_for('organization') @@ -288,52 +166,16 @@ def main(): repositories.append(module.find_resource_by_name('repositories', repo['name'], params=product_scope, thin=True)) module.foreman_params['repositories'] = repositories + if not module.desired_absent: + module.foreman_params.pop('organization') entity = module.lookup_entity('entity') - content_view_filter = module.ensure_entity( + module.ensure_entity( 'content_view_filters', module.foreman_params, entity, params=cv_scope, - state=filter_state, - foreman_spec=content_filter_spec, ) - if content_view_filter is not None and module.foreman_params['filter_type'] not in ['modulemd', 'deb']: - cv_filter_scope = {'content_view_filter_id': content_view_filter['id']} - if 'errata_id' in module.foreman_params: - # should we try to find the errata the user is asking for? or just pass it blindly? - # errata = module.find_resource('errata', 'id={0}'.format(module.foreman_params['errata_id']), params=scope) - rule_spec = content_filter_rule_erratum_id_spec - search_scope = {'errata_id': module.foreman_params['errata_id']} - search_scope.update(cv_filter_scope) - search = None - else: - rule_spec = globals()['content_filter_rule_%s_spec' % (module.foreman_params['filter_type'])] - search_scope = cv_filter_scope - if module.foreman_params['rule_name'] is not None: - search = 'name="{0}"'.format(module.foreman_params['rule_name']) - else: - search = None - # not using find_resource_by_name here, because not all filters (errata) have names - content_view_filter_rule = module.find_resource('content_view_filter_rules', search, params=search_scope, failsafe=True) if entity else None - - if module.foreman_params['filter_type'] == 'package_group': - package_group = module.find_resource_by_name('package_groups', module.foreman_params['rule_name'], params=scope) - module.foreman_params['uuid'] = package_group['uuid'] - - # drop 'name' from the dict, as otherwise it might override 'rule_name' - rule_dict = module.foreman_params.copy() - rule_dict.pop('name', None) - - module.ensure_entity( - 'content_view_filter_rules', - rule_dict, - content_view_filter_rule, - params=cv_filter_scope, - state=rule_state, - foreman_spec=rule_spec, - ) - if __name__ == '__main__': main() diff --git a/ansible_collections/theforeman/foreman/plugins/modules/content_view_version.py b/ansible_collections/theforeman/foreman/plugins/modules/content_view_version.py index 42567483e..f9ba6414c 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/content_view_version.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/content_view_version.py @@ -200,7 +200,7 @@ def main(): mutually_exclusive=[['current_lifecycle_environment', 'version']], ) - module.task_timeout = 60 * 60 + module.task_timeout = 180 * 60 if 'version' in module.foreman_params and not re.match(r'^\d+\.\d+$', module.foreman_params['version']): try: diff --git a/ansible_collections/theforeman/foreman/plugins/modules/host.py b/ansible_collections/theforeman/foreman/plugins/modules/host.py index 6c1f41d07..e0da3bbf3 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/host.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/host.py @@ -507,7 +507,7 @@ def main(): for volume in module.foreman_params['compute_attributes']['volumes_attributes'].values(): if 'storage_pod' in volume: storage_pod = module.find_storage_pod(volume['storage_pod'], compute_resource, cluster) - volume['storage_pod'] = storage_pod['id'] + volume['storage_pod'] = storage_pod['name'] if 'storage_domain' in volume: storage_domain = module.find_storage_domain(volume['storage_domain'], compute_resource, cluster) volume['storage_domain'] = storage_domain['id'] diff --git a/ansible_collections/theforeman/foreman/plugins/modules/job_template.py b/ansible_collections/theforeman/foreman/plugins/modules/job_template.py index df2fe732f..57ed1577e 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/job_template.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/job_template.py @@ -176,9 +176,9 @@ EXAMPLES = ''' - name: toDelete input_type: user locations: - - Gallifrey + - Gallifrey organizations: - - TARDIS INC + - TARDIS INC - name: "Create a Job Template from a file" theforeman.foreman.job_template: @@ -192,9 +192,9 @@ EXAMPLES = ''' input_type: user state: present locations: - - Gallifrey + - Gallifrey organizations: - - TARDIS INC + - TARDIS INC - name: "remove a job template's template inputs" theforeman.foreman.job_template: @@ -205,9 +205,9 @@ EXAMPLES = ''' template_inputs: [] state: present locations: - - Gallifrey + - Gallifrey organizations: - - TARDIS INC + - TARDIS INC - name: "Delete a Job Template" theforeman.foreman.job_template: @@ -226,9 +226,9 @@ EXAMPLES = ''' name: Wibbly Wobbly Template state: present locations: - - Gallifrey + - Gallifrey organizations: - - TARDIS INC + - TARDIS INC # Providing a name in this case wouldn't be very sensible. # Alternatively make use of with_filetree to parse recursively with filter. @@ -240,9 +240,9 @@ EXAMPLES = ''' file_name: "{{ item }}" state: present locations: - - SKARO + - SKARO organizations: - - DALEK INC + - DALEK INC with_fileglob: - "./arsenal_templates/*.erb" @@ -276,9 +276,9 @@ EXAMPLES = ''' name: "*" state: present organizations: - - DALEK INC - - sky.net - - Doc Brown's garage + - DALEK INC + - sky.net + - Doc Brown's garage ''' diff --git a/ansible_collections/theforeman/foreman/plugins/modules/partition_table.py b/ansible_collections/theforeman/foreman/plugins/modules/partition_table.py index 1ddccea76..669a74dd1 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/partition_table.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/partition_table.py @@ -140,8 +140,8 @@ EXAMPLES = ''' - SKARO organizations: - DALEK INC - with_fileglob: - - "./arsenal_templates/*.erb" + with_fileglob: + - "./arsenal_templates/*.erb" # If the templates are stored locally and the ansible module is executed on a remote host - name: Ensure latest version of all Ptable Community Templates diff --git a/ansible_collections/theforeman/foreman/plugins/modules/product.py b/ansible_collections/theforeman/foreman/plugins/modules/product.py index 9a99e0893..f2a2a0d30 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/product.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/product.py @@ -120,7 +120,6 @@ class KatelloProductModule(KatelloEntityAnsibleModule): def main(): module = KatelloProductModule( - entity_name='product', foreman_spec=dict( name=dict(required=True), label=dict(), diff --git a/ansible_collections/theforeman/foreman/plugins/modules/provisioning_template.py b/ansible_collections/theforeman/foreman/plugins/modules/provisioning_template.py index 0a52f8dec..e47da4d6a 100644 --- a/ansible_collections/theforeman/foreman/plugins/modules/provisioning_template.py +++ b/ansible_collections/theforeman/foreman/plugins/modules/provisioning_template.py @@ -167,8 +167,8 @@ EXAMPLES = ''' - SKARO organizations: - DALEK INC - with_fileglob: - - "./arsenal_templates/*.erb" + with_fileglob: + - "./arsenal_templates/*.erb" # If the templates are stored locally and the ansible module is executed on a remote host - name: Ensure latest version of all Provisioning Community Templates diff --git a/ansible_collections/theforeman/foreman/plugins/modules/registration_command.py b/ansible_collections/theforeman/foreman/plugins/modules/registration_command.py new file mode 100644 index 000000000..9262febc1 --- /dev/null +++ b/ansible_collections/theforeman/foreman/plugins/modules/registration_command.py @@ -0,0 +1,205 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) Evgeni Golov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: registration_command +version_added: 4.0.0 +short_description: Manage Registration Command +description: + - Manage Registration Command +author: + - "Evgeni Golov (@evgeni)" +options: + activation_keys: + description: + - Activation keys for subscription-manager client, required for CentOS and Red Hat + Enterprise Linux. + - Required only if host group has no activation keys. + required: false + type: list + elements: str + force: + description: + - "Clear any previous registration and run C(subscription-manager) with C(--force)." + required: false + type: bool + hostgroup: + description: + - Host group to register the host in. + required: false + type: str + ignore_subman_errors: + description: + - Ignore C(subscription-manager) errors for C(subscription-manager register) command. + required: false + type: bool + insecure: + description: + - Enable insecure argument for the initial C(curl). + required: false + type: bool + jwt_expiration: + description: + - Expiration of the authorization token (in hours). + required: false + type: int + lifecycle_environment: + description: + - Lifecycle environment for the host. + required: false + type: str + operatingsystem: + description: + - Operating System to register the host in. + - Operating system must have a C(host_init_config) template assigned. + required: false + type: str + packages: + description: + - Packages to install on the host when registered. + - Multiple packages are to be given as a space delimited string. + required: false + type: str + remote_execution_interface: + description: + - Identifier of the Host interface for Remote execution. + required: false + type: str + repo: + description: + - Repository URL (yum/dnf) or full sources.list entry (apt). + required: false + type: str + repo_gpg_key_url: + description: + - URL of the GPG key for the repository. + required: false + type: str + setup_insights: + description: + - If this is set to C(true), C(insights-client) will be installed + and registered on Red Hat family operating systems. + required: false + type: bool + setup_remote_execution: + description: + - If this is set to true, SSH keys will be installed on the host. + required: false + type: bool + setup_remote_execution_pull: + description: + - If this is set to true, pull provider client will be deployed on the host. + required: false + type: bool + smart_proxy: + description: + - Name of Smart Proxy. + - This Proxy must have both the C(Templates) and C(Registration) features enabled. + required: false + type: str + update_packages: + description: + - Update all packages on the host. + required: false + type: bool + organization: + description: + - Organization to register the host in. + required: false + type: str + location: + description: + - Location to register the host in. + required: false + type: str + +extends_documentation_fragment: + - theforeman.foreman.foreman +''' + +EXAMPLES = ''' +- name: "Generate registration command" + theforeman.foreman.registration_command: + username: "admin" + password: "changeme" + server_url: "https://foreman.example.com" + register: command + +- name: "Perform registration" + ansible.builtin.shell: + cmd: "{{ command.registration_command }}" +''' + +RETURN = ''' +registration_command: + description: The generated registration command. + returned: success + type: str +''' + +from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import ForemanAnsibleModule + + +class ForemanRegistrationCommandModule(ForemanAnsibleModule): + pass + + +def main(): + module = ForemanRegistrationCommandModule( + foreman_spec=dict( + hostgroup=dict(type='entity'), + operatingsystem=dict(type='entity'), + smart_proxy=dict(type='entity'), + setup_insights=dict(type='bool'), + setup_remote_execution=dict(type='bool'), + jwt_expiration=dict(type='int'), + insecure=dict(type='bool'), + packages=dict(type='str'), + update_packages=dict(type='bool'), + repo=dict(type='str'), + repo_gpg_key_url=dict(type='str', no_log=False), + remote_execution_interface=dict(type='str'), + setup_remote_execution_pull=dict(type='bool'), + activation_keys=dict(type='list', elements='str', no_log=False), + lifecycle_environment=dict(type='entity'), + force=dict(type='bool'), + ignore_subman_errors=dict(type='bool'), + organization=dict(type='entity'), + location=dict(type='entity'), + ), + required_plugins=[ + ('katello', ['activation_key', 'activation_keys', 'lifecycle_environment', 'ignore_subman_errors', 'force']), + ('remote_execution', ['remote_execution_interface', 'setup_remote_execution_pull']), + ], + ) + + with module.api_connection(): + module.auto_lookup_entities() + if not module.check_mode: + command = module.ensure_entity('registration_commands', module.foreman_params, None, state='present')['registration_command'] + else: + command = "curl | bash" + module.exit_json(registration_command=command) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/theforeman/foreman/plugins/modules/webhook.py b/ansible_collections/theforeman/foreman/plugins/modules/webhook.py new file mode 100644 index 000000000..0acbd12ad --- /dev/null +++ b/ansible_collections/theforeman/foreman/plugins/modules/webhook.py @@ -0,0 +1,167 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) 2023, Griffin Sullivan <gsulliva@redhat.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: webhook +version_added: 4.0.0 +short_description: Manage Webhooks +description: + - Manage Webhooks +author: + - "Griffin Sullivan (@Griffin-Sullivan)" +options: + name: + description: + - Name of the Webhook + required: true + type: str + target_url: + description: + - The URL to call when the webhook is triggered + - Required when creating a new webhook + type: str + http_method: + description: + - The HTTP method used in the webhook + choices: + - POST + - GET + - PUT + - DELETE + - PATCH + type: str + http_content_type: + description: + - The HTTP content type for the webhook + type: str + event: + description: + - Name of the event that shall trigger the webhook + - Required when creating a new webhook + type: str + webhook_template: + description: + - Name of the webhook template + type: str + enabled: + description: + - Enable or disable the webhook + type: bool + verify_ssl: + description: + - Verify SSL certs for the webhook + type: bool + ssl_ca_certs: + description: + - X509 Certification Authorities concatenated in PEM format + type: str + webhook_username: + description: + - Username for the webhook, if required + type: str + webhook_password: + description: + - Password for the webhook, if required + type: str + http_headers: + description: + - HTTP headers for the webhook + type: str + proxy_authorization: + description: + - Authorize with client certificate and validate CA from Settings + type: bool +extends_documentation_fragment: + - theforeman.foreman.foreman + - theforeman.foreman.foreman.entity_state + - theforeman.foreman.foreman.taxonomy +''' + +EXAMPLES = ''' +- name: 'Create Webhook' + theforeman.foreman.webhook: + username: 'admin' + password: 'secret_password' + server_url: 'https://foreman.example.com' + name: 'test-webhook' + target_url: 'https://google.com' + http_method: 'GET' + event: 'actions.katello.content_view.promote_succeeded' + enabled: true + organizations: + - 'MyOrg' + locations: + - 'DC1' + +- name: 'Remove Webhook' + theforeman.foreman.webhook: + username: 'admin' + password: 'secret_password' + server_url: 'https://foreman.example.com' + name: 'test-webhook' + state: 'absent' +''' + +RETURN = ''' +entity: + description: Final state of the affected entities grouped by their type. + returned: success + type: dict + contains: + webhooks: + description: List of webhooks. + type: list + elements: dict +''' + +from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import ForemanTaxonomicEntityAnsibleModule + + +class ForemanWebhookModule(ForemanTaxonomicEntityAnsibleModule): + pass + + +def main(): + module = ForemanWebhookModule( + foreman_spec=dict( + name=dict(required=True), + target_url=dict(), + http_method=dict(choices=['POST', 'GET', 'PUT', 'DELETE', 'PATCH']), + http_content_type=dict(), + event=dict(), + webhook_template=dict(type='entity'), + verify_ssl=dict(type='bool'), + enabled=dict(type='bool'), + ssl_ca_certs=dict(), + webhook_username=dict(flat_name='user'), + webhook_password=dict(no_log=True, flat_name='password'), + http_headers=dict(), + proxy_authorization=dict(type='bool'), + ), + ) + + with module.api_connection(): + module.run() + + +if __name__ == '__main__': + main() |