1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
5# Copyright: (c) 2020, Lionel Hercot <lhercot@cisco.com>
6# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
7# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
8
9from __future__ import absolute_import, division, print_function
10__metaclass__ = type
11
12DOCUMENTATION = r'''
13---
14module: aci_static_node_mgmt_address
15short_description: In band or Out of band management IP address
16description:
17- Cisco ACI Fabric Node IP address
18options:
19  epg:
20    description:
21    - The name of the end point group
22    type: str
23  pod_id:
24    description:
25    - The pod number of the leaf, spine or APIC
26    type: int
27  node_id:
28    description:
29    - ACI Fabric's node id of a leaf, spine or APIC
30    type: int
31  ipv4_address:
32    description:
33    - ipv4 address of in band/out of band mgmt
34    type: str
35    aliases: [ ip ]
36  ipv4_gw:
37    description:
38    - Gateway address of in band / out of band mgmt network
39    type: str
40    aliases: [ gw ]
41  ipv6_address:
42    description:
43    -  ipv6 address of in band/out of band  mgmt
44    type: str
45    aliases: [ ipv6 ]
46  ipv6_gw:
47    description:
48    - GW address of in band/out of band mgmt
49    type: str
50  type:
51    description:
52    - type of management interface
53    type: str
54    choices: [ in_band, out_of_band ]
55    required: true
56  state:
57    description:
58    - Use C(present) or C(absent) for adding or removing.
59    - Use C(query) for listing an object or multiple objects.
60    type: str
61    choices: [ absent, present, query ]
62    default: present
63extends_documentation_fragment:
64- cisco.aci.aci
65
66author:
67- Sudhakar Shet Kudtarkar (@kudtarkar1)
68- Lionel Hercot (@lhercot)
69- Shreyas Srish (@shrsr)
70'''
71
72EXAMPLES = r'''
73- name: Add ipv4 address to in band mgmt interface
74  cisco.aci.aci_static_node_mgmt_address:
75    host: "Host IP"
76    username: admin
77    password: SomeSecretePassword
78    epg: default
79    pod_id: 1
80    type: in_band
81    node_id: 1102
82    ipv4_address: "3.1.1.2/24"
83    ipv4_gw: "3.1.1.1"
84    state: present
85  delegate_to: localhost
86
87- name: Add ipv4 address to out of band mgmt interface
88  cisco.aci.aci_static_node_mgmt_address:
89    host: "Host IP"
90    username: admin
91    password: SomeSecretePassword
92    epg: default
93    pod_id: 1
94    band_type: out_of_band
95    node_id: 1102
96    ipv4_address: "3.1.1.2/24"
97    ipv4_gw: "3.1.1.1"
98    state: present
99  delegate_to: localhost
100
101- name: Remove ipv4 address to in band mgmt interface
102  cisco.aci.aci_static_node_mgmt_address:
103    host: "Host IP"
104    username: admin
105    password: SomeSecretePassword
106    epg: default
107    pod_id: 1
108    type: in_band
109    node_id: 1102
110    ipv4_address: "3.1.1.2/24"
111    ipv4_gw: "3.1.1.1"
112    state: absent
113  delegate_to: localhost
114
115- name: Query the in band mgmt ipv4 address
116  cisco.aci.aci_static_node_mgmt_address:
117    host: "Host IP"
118    username: admin
119    password: SomeSecretePassword
120    epg: default
121    pod_id: 1
122    type: in_band
123    node_id: 1102
124    ipv4_address: "3.1.1.2/24"
125    ipv4_gw: "3.1.1.1"
126    state: query
127  delegate_to: localhost
128
129- name: Query all addresses in epg out of band25wf
130  cisco.aci.aci_static_node_mgmt_address:
131    host: "Host IP"
132    username: admin
133    password: SomeSecretePassword
134    epg: default
135    type: out_of_band
136    state: query
137  delegate_to: localhost
138
139- name: Query all in band addresses
140  cisco.aci.aci_static_node_mgmt_address:
141    host: "Host IP"
142    username: admin
143    password: SomeSecretePassword
144    type: in_band
145    state: query
146  delegate_to: localhost
147'''
148
149RETURN = r'''
150   current:
151     description: The existing configuration from the APIC after the module has finished
152     returned: success
153     type: list
154     sample:
155       [
156           {
157               "fvTenant": {
158                   "attributes": {
159                       "descr": "Production environment",
160                       "dn": "uni/tn-production",
161                       "name": "production",
162                       "nameAlias": "",
163                       "ownerKey": "",
164                       "ownerTag": ""
165                   }
166               }
167           }
168       ]
169   error:
170     description: The error information as returned from the APIC
171     returned: failure
172     type: dict
173     sample:
174       {
175           "code": "122",
176           "text": "unknown managed object class foo"
177       }
178   raw:
179     description: The raw output returned by the APIC REST API (xml or json)
180     returned: parse error
181     type: str
182     sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></imdata>'
183   sent:
184     description: The actual/minimal configuration pushed to the APIC
185     returned: info
186     type: list
187     sample:
188       {
189           "fvTenant": {
190               "attributes": {
191                   "descr": "Production environment"
192               }
193           }
194       }
195   previous:
196     description: The original configuration from the APIC before the module has started
197     returned: info
198     type: list
199     sample:
200       [
201           {
202               "fvTenant": {
203                   "attributes": {
204                       "descr": "Production",
205                       "dn": "uni/tn-production",
206                       "name": "production",
207                       "nameAlias": "",
208                       "ownerKey": "",
209                       "ownerTag": ""
210                   }
211               }
212           }
213       ]
214   proposed:
215     description: The assembled configuration from the user-provided parameters
216     returned: info
217     type: dict
218     sample:
219       {
220           "fvTenant": {
221               "attributes": {
222                   "descr": "Production environment",
223                   "name": "production"
224               }
225           }
226       }
227   filter_string:
228     description: The filter string used for the request
229     returned: failure or debug
230     type: str
231     sample: ?rsp-prop-include=config-only
232   method:
233     description: The HTTP method used for the request to the APIC
234     returned: failure or debug
235     type: str
236     sample: POST
237   response:
238     description: The HTTP response from the APIC
239     returned: failure or debug
240     type: str
241     sample: class_map (30 bytes)
242   status:
243     description: The HTTP status from the APIC
244     returned: failure or debug
245     type: int
246     sample: 200
247   url:
248     description: The HTTP url used for the request to the APIC
249     returned: failure or debug
250     type: str
251     sample: https://10.11.12.13/api/mo/uni/tn-production.json
252   '''
253
254from ansible.module_utils.basic import AnsibleModule
255from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
256
257
258def main():
259    argument_spec = aci_argument_spec()
260    argument_spec.update(
261        node_id=dict(type='int'),
262        pod_id=dict(type='int'),
263        type=dict(type='str', choices=['in_band', 'out_of_band'], required=True),
264        epg=dict(type='str'),
265        ipv4_address=dict(type='str', aliases=['ip']),
266        ipv4_gw=dict(type='str', aliases=['gw']),
267        ipv6_address=dict(type='str', aliases=['ipv6']),
268        ipv6_gw=dict(type='str'),
269        state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
270    )
271
272    module = AnsibleModule(
273        argument_spec=argument_spec,
274        supports_check_mode=True,
275        required_if=[
276            ['state', 'absent', ['node_id', 'epg']],
277            ['state', 'present', ['node_id', 'epg', 'ipv4_address', 'ipv4_gw']]
278        ]
279    )
280
281    pod_id = module.params.get('pod_id')
282    node_id = module.params.get('node_id')
283    type = module.params.get('type')
284    epg = module.params.get('epg')
285    ipv4_address = module.params.get('ipv4_address')
286    ipv4_gw = module.params.get('ipv4_gw')
287    ipv6_address = module.params.get('ipv6_address')
288    ipv6_gw = module.params.get('ipv6_gw')
289    state = module.params.get('state')
290
291    class_map = dict(
292        in_band=list([
293            dict(aci_class='mgmtInb', aci_rn='inb-{0}'),
294            dict(aci_class='mgmtRsInBStNode', aci_rn='rsinBStNode-[{0}]')
295        ]),
296        out_of_band=list([
297            dict(aci_class='mgmtOob', aci_rn='oob-{0}'),
298            dict(aci_class='mgmtRsOoBStNode', aci_rn='rsooBStNode-[{0}]')
299        ]),
300    )
301
302    static_path = None
303    if pod_id is not None and node_id is not None:
304        static_path = 'topology/pod-{0}/node-{1}'.format(pod_id, node_id)
305
306    aci = ACIModule(module)
307    aci.construct_url(
308        root_class=dict(
309            aci_class='fvTenant',
310            aci_rn='tn-mgmt',
311            module_object='mgmt',
312            target_filter={'name': 'mgmt'},
313        ),
314        subclass_1=dict(
315            aci_class='mgmtMgmtP',
316            aci_rn='mgmtp-default',
317            module_object='default',
318            target_filter={'name': 'default'},
319        ),
320        subclass_2=dict(
321            aci_class=class_map.get(type)[0]['aci_class'],
322            aci_rn=class_map.get(type)[0]['aci_rn'].format(epg),
323            module_object=epg,
324            target_filter={'name': epg},
325        ),
326        subclass_3=dict(
327            aci_class=class_map.get(type)[1]['aci_class'],
328            aci_rn=class_map.get(type)[1]['aci_rn'].format(static_path),
329            module_object=static_path,
330            target_filter={'name': static_path},
331        ),
332    )
333
334    aci.get_existing()
335
336    if state == 'present':
337        aci.payload(
338            aci_class=class_map.get(type)[1]['aci_class'],
339            class_config=dict(
340                addr=ipv4_address,
341                gw=ipv4_gw,
342                v6Addr=ipv6_address,
343                v6Gw=ipv6_gw
344            ),
345        )
346        aci.get_diff(aci_class=class_map.get(type)[1]['aci_class'])
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