1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9ANSIBLE_METADATA = {'metadata_version': '1.1',
10                    'status': ['preview'],
11                    'supported_by': 'certified'}
12
13DOCUMENTATION = r'''
14---
15module: aci_contract_subject
16short_description: Manage initial Contract Subjects (vz:Subj)
17description:
18- Manage initial Contract Subjects on Cisco ACI fabrics.
19version_added: '2.4'
20options:
21  tenant:
22    description:
23    - The name of the tenant.
24    type: str
25    aliases: [ tenant_name ]
26  subject:
27    description:
28    - The contract subject name.
29    type: str
30    aliases: [ contract_subject, name, subject_name ]
31  contract:
32    description:
33    - The name of the Contract.
34    type: str
35    aliases: [ contract_name ]
36  reverse_filter:
37    description:
38    - Determines if the APIC should reverse the src and dst ports to allow the
39      return traffic back, since ACI is stateless filter.
40    - The APIC defaults to C(yes) when unset during creation.
41    type: bool
42  priority:
43    description:
44    - The QoS class.
45    - The APIC defaults to C(unspecified) when unset during creation.
46    type: str
47    choices: [ level1, level2, level3, unspecified ]
48  dscp:
49    description:
50    - The target DSCP.
51    - The APIC defaults to C(unspecified) when unset during creation.
52    type: str
53    choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43,
54               CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ]
55    aliases: [ target ]
56  description:
57    description:
58    - Description for the contract subject.
59    type: str
60    aliases: [ descr ]
61  consumer_match:
62    description:
63    - The match criteria across consumers.
64    - The APIC defaults to C(at_least_one) when unset during creation.
65    type: str
66    choices: [ all, at_least_one, at_most_one, none ]
67  provider_match:
68    description:
69    - The match criteria across providers.
70    - The APIC defaults to C(at_least_one) when unset during creation.
71    type: str
72    choices: [ all, at_least_one, at_most_one, none ]
73  state:
74    description:
75    - Use C(present) or C(absent) for adding or removing.
76    - Use C(query) for listing an object or multiple objects.
77    type: str
78    choices: [ absent, present, query ]
79    default: present
80extends_documentation_fragment: aci
81notes:
82- The C(tenant) and C(contract) used must exist before using this module in your playbook.
83  The M(aci_tenant) and M(aci_contract) modules can be used for this.
84seealso:
85- module: aci_contract
86- module: aci_tenant
87- name: APIC Management Information Model reference
88  description: More information about the internal APIC class B(vz:Subj).
89  link: https://developer.cisco.com/docs/apic-mim-ref/
90author:
91- Swetha Chunduri (@schunduri)
92'''
93
94EXAMPLES = r'''
95- name: Add a new contract subject
96  aci_contract_subject:
97    host: apic
98    username: admin
99    password: SomeSecretPassword
100    tenant: production
101    contract: web_to_db
102    subject: default
103    description: test
104    reverse_filter: yes
105    priority: level1
106    dscp: unspecified
107    state: present
108  register: query_result
109
110- name: Remove a contract subject
111  aci_contract_subject:
112    host: apic
113    username: admin
114    password: SomeSecretPassword
115    tenant: production
116    contract: web_to_db
117    subject: default
118    state: absent
119  delegate_to: localhost
120
121- name: Query a contract subject
122  aci_contract_subject:
123    host: apic
124    username: admin
125    password: SomeSecretPassword
126    tenant: production
127    contract: web_to_db
128    subject: default
129    state: query
130  delegate_to: localhost
131  register: query_result
132
133- name: Query all contract subjects
134  aci_contract_subject:
135    host: apic
136    username: admin
137    password: SomeSecretPassword
138    state: query
139  delegate_to: localhost
140  register: query_result
141'''
142
143RETURN = r'''
144current:
145  description: The existing configuration from the APIC after the module has finished
146  returned: success
147  type: list
148  sample:
149    [
150        {
151            "fvTenant": {
152                "attributes": {
153                    "descr": "Production environment",
154                    "dn": "uni/tn-production",
155                    "name": "production",
156                    "nameAlias": "",
157                    "ownerKey": "",
158                    "ownerTag": ""
159                }
160            }
161        }
162    ]
163error:
164  description: The error information as returned from the APIC
165  returned: failure
166  type: dict
167  sample:
168    {
169        "code": "122",
170        "text": "unknown managed object class foo"
171    }
172raw:
173  description: The raw output returned by the APIC REST API (xml or json)
174  returned: parse error
175  type: str
176  sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
177sent:
178  description: The actual/minimal configuration pushed to the APIC
179  returned: info
180  type: list
181  sample:
182    {
183        "fvTenant": {
184            "attributes": {
185                "descr": "Production environment"
186            }
187        }
188    }
189previous:
190  description: The original configuration from the APIC before the module has started
191  returned: info
192  type: list
193  sample:
194    [
195        {
196            "fvTenant": {
197                "attributes": {
198                    "descr": "Production",
199                    "dn": "uni/tn-production",
200                    "name": "production",
201                    "nameAlias": "",
202                    "ownerKey": "",
203                    "ownerTag": ""
204                }
205            }
206        }
207    ]
208proposed:
209  description: The assembled configuration from the user-provided parameters
210  returned: info
211  type: dict
212  sample:
213    {
214        "fvTenant": {
215            "attributes": {
216                "descr": "Production environment",
217                "name": "production"
218            }
219        }
220    }
221filter_string:
222  description: The filter string used for the request
223  returned: failure or debug
224  type: str
225  sample: ?rsp-prop-include=config-only
226method:
227  description: The HTTP method used for the request to the APIC
228  returned: failure or debug
229  type: str
230  sample: POST
231response:
232  description: The HTTP response from the APIC
233  returned: failure or debug
234  type: str
235  sample: OK (30 bytes)
236status:
237  description: The HTTP status from the APIC
238  returned: failure or debug
239  type: int
240  sample: 200
241url:
242  description: The HTTP url used for the request to the APIC
243  returned: failure or debug
244  type: str
245  sample: https://10.11.12.13/api/mo/uni/tn-production.json
246'''
247
248from ansible.module_utils.basic import AnsibleModule
249from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
250
251MATCH_MAPPING = dict(
252    all='All',
253    at_least_one='AtleastOne',
254    at_most_one='AtmostOne',
255    none='None',
256)
257
258
259def main():
260    argument_spec = aci_argument_spec()
261    argument_spec.update(
262        contract=dict(type='str', aliases=['contract_name']),  # Not required for querying all objects
263        subject=dict(type='str', aliases=['contract_subject', 'name', 'subject_name']),  # Not required for querying all objects
264        tenant=dict(type='str', aliases=['tenant_name']),  # Not required for querying all objects
265        priority=dict(type='str', choices=['unspecified', 'level1', 'level2', 'level3']),
266        reverse_filter=dict(type='bool'),
267        dscp=dict(type='str', aliases=['target'],
268                  choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', 'AF43',
269                           'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified']),
270        description=dict(type='str', aliases=['descr']),
271        consumer_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']),
272        provider_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']),
273        state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
274        directive=dict(type='str', removed_in_version='2.4'),  # Deprecated starting from v2.4
275        filter=dict(type='str', aliases=['filter_name'], removed_in_version='2.4'),  # Deprecated starting from v2.4
276    )
277
278    module = AnsibleModule(
279        argument_spec=argument_spec,
280        supports_check_mode=True,
281        required_if=[
282            ['state', 'absent', ['contract', 'subject', 'tenant']],
283            ['state', 'present', ['contract', 'subject', 'tenant']],
284        ],
285    )
286
287    aci = ACIModule(module)
288
289    subject = module.params['subject']
290    priority = module.params['priority']
291    reverse_filter = aci.boolean(module.params['reverse_filter'])
292    contract = module.params['contract']
293    dscp = module.params['dscp']
294    description = module.params['description']
295    filter_name = module.params['filter']
296    directive = module.params['directive']
297    consumer_match = module.params['consumer_match']
298    if consumer_match is not None:
299        consumer_match = MATCH_MAPPING[consumer_match]
300    provider_match = module.params['provider_match']
301    if provider_match is not None:
302        provider_match = MATCH_MAPPING[provider_match]
303    state = module.params['state']
304    tenant = module.params['tenant']
305
306    if directive is not None or filter_name is not None:
307        module.fail_json(msg="Managing Contract Subjects to Filter bindings has been moved to module 'aci_subject_bind_filter'")
308
309    aci.construct_url(
310        root_class=dict(
311            aci_class='fvTenant',
312            aci_rn='tn-{0}'.format(tenant),
313            module_object=tenant,
314            target_filter={'name': tenant},
315        ),
316        subclass_1=dict(
317            aci_class='vzBrCP',
318            aci_rn='brc-{0}'.format(contract),
319            module_object=contract,
320            target_filter={'name': contract},
321        ),
322        subclass_2=dict(
323            aci_class='vzSubj',
324            aci_rn='subj-{0}'.format(subject),
325            module_object=subject,
326            target_filter={'name': subject},
327        ),
328    )
329
330    aci.get_existing()
331
332    if state == 'present':
333        aci.payload(
334            aci_class='vzSubj',
335            class_config=dict(
336                name=subject,
337                prio=priority,
338                revFltPorts=reverse_filter,
339                targetDscp=dscp,
340                consMatchT=consumer_match,
341                provMatchT=provider_match,
342                descr=description,
343            ),
344        )
345
346        aci.get_diff(aci_class='vzSubj')
347
348        aci.post_config()
349
350    elif state == 'absent':
351        aci.delete_config()
352
353    aci.exit_json()
354
355
356if __name__ == "__main__":
357    main()
358