1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3# (c) 2018, Sean O'Keeffe <seanokeeffe797@gmail.com>
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18from __future__ import absolute_import, division, print_function
19__metaclass__ = type
20
21
22DOCUMENTATION = '''
23---
24module: content_view_filter
25version_added: 1.0.0
26short_description: Manage Content View Filters
27description:
28    - Create and manage content View filters
29author: "Sean O'Keeffe (@sean797)"
30options:
31  architecture:
32    description:
33      - package architecture
34    type: str
35  name:
36    description:
37      - Name of the Content View Filter
38    type: str
39    required: true
40  description:
41    description:
42      - Description of the Content View Filter
43    type: str
44  content_view:
45    description:
46      - Name of the content view
47    required: true
48    type: str
49  filter_state:
50    description:
51      - State of the content view filter
52    default: present
53    choices:
54      - present
55      - absent
56    type: str
57  repositories:
58    description:
59      - List of repositories that include name and product
60      - An empty Array means all current and future repositories
61    default: []
62    type: list
63    elements: dict
64  rule_state:
65    description:
66      - State of the content view filter rule
67    default: present
68    choices:
69      - present
70      - absent
71    type: str
72  filter_type:
73    description:
74      - Content view filter type
75    required: true
76    choices:
77      - rpm
78      - package_group
79      - erratum
80      - docker
81    type: str
82  rule_name:
83    description:
84      - Content view filter rule name or package name
85      - If omitted, the value of I(name) will be used if necessary
86    aliases:
87      - package_name
88      - package_group
89      - tag
90    type: str
91  date_type:
92    description:
93      - Search using the 'Issued On' or 'Updated On'
94      - Only valid on I(filter_type=erratum).
95    default: updated
96    choices:
97      - issued
98      - updated
99    type: str
100  end_date:
101    description:
102      - erratum end date (YYYY-MM-DD)
103    type: str
104  start_date:
105    description:
106      - erratum start date (YYYY-MM-DD)
107    type: str
108  errata_id:
109    description:
110      - erratum id
111    type: str
112  max_version:
113    description:
114      - package maximum version
115    type: str
116  min_version:
117    description:
118      - package minimum version
119    type: str
120  types:
121    description:
122      - erratum types (enhancement, bugfix, security)
123    default: ["bugfix", "enhancement", "security"]
124    type: list
125    elements: str
126  version:
127    description:
128      - package version
129    type: str
130  inclusion:
131    description:
132      - Create an include filter
133    default: False
134    type: bool
135  original_packages:
136    description:
137      - Include all RPMs with no errata
138    type: bool
139extends_documentation_fragment:
140  - theforeman.foreman.foreman
141  - theforeman.foreman.foreman.organization
142'''
143
144EXAMPLES = '''
145- name: Exclude csh
146  theforeman.foreman.content_view_filter:
147    username: "admin"
148    password: "changeme"
149    server_url: "https://foreman.example.com"
150    name: "package filter 1"
151    organization: "Default Organization"
152    content_view: Web Servers
153    filter_type: "rpm"
154    package_name: tcsh
155
156- name: Include newer csh versions
157  theforeman.foreman.content_view_filter:
158    username: "admin"
159    password: "changeme"
160    server_url: "https://foreman.example.com"
161    name: "package filter 1"
162    organization: "Default Organization"
163    content_view: Web Servers
164    filter_type: "rpm"
165    package_name: tcsh
166    min_version: 6.20.00
167    inclusion: True
168'''
169
170RETURN = '''
171entity:
172  description: Final state of the affected entities grouped by their type.
173  returned: success
174  type: dict
175  contains:
176    content_view_filters:
177      description: List of content view filters.
178      type: list
179      elements: dict
180'''
181
182from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import KatelloMixin, ForemanStatelessEntityAnsibleModule
183
184content_filter_spec = {
185    'id': {},
186    'name': {},
187    'description': {},
188    'repositories': {'type': 'entity_list'},
189    'inclusion': {},
190    'content_view': {'type': 'entity'},
191    'filter_type': {'flat_name': 'type'},
192    'original_packages': {},
193}
194
195content_filter_rule_erratum_spec = {
196    'id': {},
197    'date_type': {},
198    'end_date': {},
199    'start_date': {},
200    'types': {'type': 'list'},
201}
202
203content_filter_rule_erratum_id_spec = {
204    'id': {},
205    'errata_id': {},
206    'date_type': {},
207}
208
209content_filter_rule_rpm_spec = {
210    'id': {},
211    'rule_name': {'flat_name': 'name'},
212    'end_date': {},
213    'max_version': {},
214    'min_version': {},
215    'version': {},
216    'architecture': {},
217}
218
219content_filter_rule_package_group_spec = {
220    'id': {},
221    'rule_name': {'flat_name': 'name'},
222    'uuid': {},
223}
224
225content_filter_rule_docker_spec = {
226    'id': {},
227    'rule_name': {'flat_name': 'name'},
228}
229
230
231class KatelloContentViewFilterModule(KatelloMixin, ForemanStatelessEntityAnsibleModule):
232    pass
233
234
235def main():
236    module = KatelloContentViewFilterModule(
237        foreman_spec=dict(
238            name=dict(required=True),
239            description=dict(),
240            repositories=dict(type='list', default=[], elements='dict'),
241            inclusion=dict(type='bool', default=False),
242            original_packages=dict(type='bool'),
243            content_view=dict(type='entity', scope=['organization'], required=True),
244            filter_type=dict(required=True, choices=['rpm', 'package_group', 'erratum', 'docker']),
245            filter_state=dict(default='present', choices=['present', 'absent']),
246            rule_state=dict(default='present', choices=['present', 'absent']),
247            rule_name=dict(aliases=['package_name', 'package_group', 'tag']),
248            date_type=dict(default='updated', choices=['issued', 'updated']),
249            end_date=dict(),
250            errata_id=dict(),
251            max_version=dict(),
252            min_version=dict(),
253            start_date=dict(),
254            types=dict(default=["bugfix", "enhancement", "security"], type='list', elements='str'),
255            version=dict(),
256            architecture=dict(),
257        ),
258        entity_opts=dict(scope=['content_view']),
259    )
260
261    filter_state = module.foreman_params.pop('filter_state')
262    rule_state = module.foreman_params.pop('rule_state')
263
264    if module.foreman_params['filter_type'] == 'erratum':
265        module.foreman_params['rule_name'] = None
266    elif 'rule_name' not in module.foreman_params:
267        module.foreman_params['rule_name'] = module.foreman_params['name']
268
269    with module.api_connection():
270        scope = module.scope_for('organization')
271
272        cv_scope = module.scope_for('content_view')
273        if module.foreman_params['repositories']:
274            repositories = []
275            for repo in module.foreman_params['repositories']:
276                product = module.find_resource_by_name('products', repo['product'], params=scope, thin=True)
277                product_scope = {'product_id': product['id']}
278                repositories.append(module.find_resource_by_name('repositories', repo['name'], params=product_scope, thin=True))
279            module.foreman_params['repositories'] = repositories
280
281        entity = module.lookup_entity('entity')
282        content_view_filter = module.ensure_entity(
283            'content_view_filters',
284            module.foreman_params,
285            entity,
286            params=cv_scope,
287            state=filter_state,
288            foreman_spec=content_filter_spec,
289        )
290
291        if content_view_filter is not None:
292            cv_filter_scope = {'content_view_filter_id': content_view_filter['id']}
293            if 'errata_id' in module.foreman_params:
294                # should we try to find the errata the user is asking for? or just pass it blindly?
295                # errata = module.find_resource('errata', 'id={0}'.format(module.foreman_params['errata_id']), params=scope)
296                rule_spec = content_filter_rule_erratum_id_spec
297                search_scope = {'errata_id': module.foreman_params['errata_id']}
298                search_scope.update(cv_filter_scope)
299                search = None
300            else:
301                rule_spec = globals()['content_filter_rule_%s_spec' % (module.foreman_params['filter_type'])]
302                search_scope = cv_filter_scope
303                if module.foreman_params['rule_name'] is not None:
304                    search = 'name="{0}"'.format(module.foreman_params['rule_name'])
305                else:
306                    search = None
307            # not using find_resource_by_name here, because not all filters (errata) have names
308            content_view_filter_rule = module.find_resource('content_view_filter_rules', search, params=search_scope, failsafe=True) if entity else None
309
310            if module.foreman_params['filter_type'] == 'package_group':
311                package_group = module.find_resource_by_name('package_groups', module.foreman_params['rule_name'], params=scope)
312                module.foreman_params['uuid'] = package_group['uuid']
313
314            # drop 'name' from the dict, as otherwise it might override 'rule_name'
315            rule_dict = module.foreman_params.copy()
316            rule_dict.pop('name', None)
317
318            module.ensure_entity(
319                'content_view_filter_rules',
320                rule_dict,
321                content_view_filter_rule,
322                params=cv_filter_scope,
323                state=rule_state,
324                foreman_spec=rule_spec,
325            )
326
327
328if __name__ == '__main__':
329    main()
330