mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-26 12:21:26 -07:00
This is to avoid users connecting to the APIC using SSH, which is likely to fail. In the ACI documentation (linked from every module) we go into more details on how people are supposed to work with the modules..
309 lines
9.3 KiB
Python
309 lines
9.3 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: aci_config_rollback
|
|
short_description: Provides rollback and rollback preview functionality (config:ImportP)
|
|
description:
|
|
- Provides rollback and rollback preview functionality for Cisco ACI fabrics.
|
|
- Config Rollbacks are done using snapshots C(aci_snapshot) with the configImportP class.
|
|
notes:
|
|
- More information about the internal APIC class B(config:ImportP) from
|
|
L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
|
|
author:
|
|
- Jacob McGill (@jmcgill298)
|
|
version_added: '2.4'
|
|
options:
|
|
compare_export_policy:
|
|
description:
|
|
- The export policy that the C(compare_snapshot) is associated to.
|
|
compare_snapshot:
|
|
description:
|
|
- The name of the snapshot to compare with C(snapshot).
|
|
description:
|
|
description:
|
|
- The description for the Import Policy.
|
|
aliases: [ descr ]
|
|
export_policy:
|
|
description:
|
|
- The export policy that the C(snapshot) is associated to.
|
|
required: yes
|
|
fail_on_decrypt:
|
|
description:
|
|
- Determines if the APIC should fail the rollback if unable to decrypt secured data.
|
|
- The APIC defaults to C(yes) when unset.
|
|
type: bool
|
|
import_mode:
|
|
description:
|
|
- Determines how the import should be handled by the APIC.
|
|
- The APIC defaults to C(atomic) when unset.
|
|
choices: [ atomic, best-effort ]
|
|
import_policy:
|
|
description:
|
|
- The name of the Import Policy to use for config rollback.
|
|
import_type:
|
|
description:
|
|
- Determines how the current and snapshot configuration should be compared for replacement.
|
|
- The APIC defaults to C(replace) when unset.
|
|
choices: [ merge, replace ]
|
|
snapshot:
|
|
description:
|
|
- The name of the snapshot to rollback to, or the base snapshot to use for comparison.
|
|
- The C(aci_snapshot) module can be used to query the list of available snapshots.
|
|
required: yes
|
|
state:
|
|
description:
|
|
- Use C(preview) for previewing the diff between two snapshots.
|
|
- Use C(rollback) for reverting the configuration to a previous snapshot.
|
|
choices: [ preview, rollback ]
|
|
default: rollback
|
|
extends_documentation_fragment: aci
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
---
|
|
- name: Create a Snapshot
|
|
aci_config_snapshot:
|
|
host: apic
|
|
username: admin
|
|
password: SomeSecretPassword
|
|
export_policy: config_backup
|
|
state: present
|
|
delegate_to: localhost
|
|
|
|
- name: Query Existing Snapshots
|
|
aci_config_snapshot:
|
|
host: apic
|
|
username: admin
|
|
password: SomeSecretPassword
|
|
export_policy: config_backup
|
|
state: query
|
|
delegate_to: localhost
|
|
|
|
- name: Compare Snapshot Files
|
|
aci_config_rollback:
|
|
host: apic
|
|
username: admin
|
|
password: SomeSecretPassword
|
|
export_policy: config_backup
|
|
snapshot: run-2017-08-28T06-24-01
|
|
compare_export_policy: config_backup
|
|
compare_snapshot: run-2017-08-27T23-43-56
|
|
state: preview
|
|
delegate_to: localhost
|
|
|
|
- name: Rollback Configuration
|
|
aci_config_rollback:
|
|
host: apic
|
|
username: admin
|
|
password: SomeSecretPassword
|
|
import_policy: rollback_config
|
|
export_policy: config_backup
|
|
snapshot: run-2017-08-28T06-24-01
|
|
state: rollback
|
|
delegate_to: localhost
|
|
|
|
- name: Rollback Configuration
|
|
aci_config_rollback:
|
|
host: apic
|
|
username: admin
|
|
password: SomeSecretPassword
|
|
import_policy: rollback_config
|
|
export_policy: config_backup
|
|
snapshot: run-2017-08-28T06-24-01
|
|
description: Rollback 8-27 changes
|
|
import_mode: atomic
|
|
import_type: replace
|
|
fail_on_decrypt: yes
|
|
state: rollback
|
|
delegate_to: localhost
|
|
'''
|
|
|
|
RETURN = r'''
|
|
preview:
|
|
description: A preview between two snapshots
|
|
returned: when state is preview
|
|
type: string
|
|
error:
|
|
description: The error information as returned from the APIC
|
|
returned: failure
|
|
type: dict
|
|
sample:
|
|
{
|
|
"code": "122",
|
|
"text": "unknown managed object class foo"
|
|
}
|
|
raw:
|
|
description: The raw output returned by the APIC REST API (xml or json)
|
|
returned: parse error
|
|
type: string
|
|
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
|
|
filter_string:
|
|
description: The filter string used for the request
|
|
returned: failure or debug
|
|
type: string
|
|
sample: ?rsp-prop-include=config-only
|
|
method:
|
|
description: The HTTP method used for the request to the APIC
|
|
returned: failure or debug
|
|
type: string
|
|
sample: POST
|
|
response:
|
|
description: The HTTP response from the APIC
|
|
returned: failure or debug
|
|
type: string
|
|
sample: OK (30 bytes)
|
|
status:
|
|
description: The HTTP status from the APIC
|
|
returned: failure or debug
|
|
type: int
|
|
sample: 200
|
|
url:
|
|
description: The HTTP url used for the request to the APIC
|
|
returned: failure or debug
|
|
type: string
|
|
sample: https://10.11.12.13/api/mo/uni/tn-production.json
|
|
'''
|
|
|
|
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils._text import to_bytes
|
|
from ansible.module_utils.urls import fetch_url
|
|
|
|
# Optional, only used for rollback preview
|
|
try:
|
|
import lxml.etree
|
|
from xmljson import cobra
|
|
XML_TO_JSON = True
|
|
except ImportError:
|
|
XML_TO_JSON = False
|
|
|
|
|
|
def main():
|
|
argument_spec = aci_argument_spec()
|
|
argument_spec.update(
|
|
compare_export_policy=dict(type='str'),
|
|
compare_snapshot=dict(type='str'),
|
|
description=dict(type='str', aliases=['descr']),
|
|
export_policy=dict(type='str'),
|
|
fail_on_decrypt=dict(type='bool'),
|
|
import_mode=dict(type='str', choices=['atomic', 'best-effort']),
|
|
import_policy=dict(type='str'),
|
|
import_type=dict(type='str', choices=['merge', 'replace']),
|
|
snapshot=dict(type='str', required=True),
|
|
state=dict(type='str', default='rollback', choices=['preview', 'rollback']),
|
|
)
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=argument_spec,
|
|
supports_check_mode=False,
|
|
required_if=[
|
|
['state', 'preview', ['compare_export_policy', 'compare_snapshot']],
|
|
['state', 'rollback', ['import_policy']],
|
|
],
|
|
)
|
|
|
|
aci = ACIModule(module)
|
|
|
|
description = module.params['description']
|
|
export_policy = module.params['export_policy']
|
|
fail_on_decrypt = aci.boolean(module.params['fail_on_decrypt'])
|
|
import_mode = module.params['import_mode']
|
|
import_policy = module.params['import_policy']
|
|
import_type = module.params['import_type']
|
|
snapshot = module.params['snapshot']
|
|
state = module.params['state']
|
|
|
|
if state == 'rollback':
|
|
if snapshot.startswith('run-'):
|
|
snapshot = snapshot.replace('run-', '', 1)
|
|
|
|
if not snapshot.endswith('.tar.gz'):
|
|
snapshot += '.tar.gz'
|
|
|
|
filename = 'ce2_{0}-{1}'.format(export_policy, snapshot)
|
|
|
|
aci.construct_url(
|
|
root_class=dict(
|
|
aci_class='configImportP',
|
|
aci_rn='fabric/configimp-{0}'.format(import_policy),
|
|
module_object=import_policy,
|
|
target_filter={'name': import_policy},
|
|
),
|
|
)
|
|
|
|
aci.get_existing()
|
|
|
|
aci.payload(
|
|
aci_class='configImportP',
|
|
class_config=dict(
|
|
adminSt='triggered',
|
|
descr=description,
|
|
failOnDecryptErrors=fail_on_decrypt,
|
|
fileName=filename,
|
|
importMode=import_mode,
|
|
importType=import_type,
|
|
name=import_policy,
|
|
snapshot='yes',
|
|
),
|
|
)
|
|
|
|
aci.get_diff(aci_class='configImportP')
|
|
|
|
aci.post_config()
|
|
|
|
elif state == 'preview':
|
|
aci.url = '%(protocol)s://%(host)s/mqapi2/snapshots.diff.xml' % module.params
|
|
aci.filter_string = (
|
|
'?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-%(export_policy)s]/snapshot-%(snapshot)s&'
|
|
's2dn=uni/backupst/snapshots-[uni/fabric/configexp-%(compare_export_policy)s]/snapshot-%(compare_snapshot)s'
|
|
) % module.params
|
|
|
|
# Generate rollback comparison
|
|
get_preview(aci)
|
|
|
|
aci.exit_json()
|
|
|
|
|
|
def get_preview(aci):
|
|
'''
|
|
This function is used to generate a preview between two snapshots and add the parsed results to the aci module return data.
|
|
'''
|
|
uri = aci.url + aci.filter_string
|
|
resp, info = fetch_url(aci.module, uri, headers=aci.headers, method='GET', timeout=aci.module.params['timeout'], use_proxy=aci.module.params['use_proxy'])
|
|
aci.method = 'GET'
|
|
aci.response = info['msg']
|
|
aci.status = info['status']
|
|
|
|
# Handle APIC response
|
|
if info['status'] == 200:
|
|
xml_to_json(aci, resp.read())
|
|
else:
|
|
aci.result['raw'] = resp.read()
|
|
aci.fail_json(msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error)
|
|
|
|
|
|
def xml_to_json(aci, response_data):
|
|
'''
|
|
This function is used to convert preview XML data into JSON.
|
|
'''
|
|
if XML_TO_JSON:
|
|
xml = lxml.etree.fromstring(to_bytes(response_data))
|
|
xmldata = cobra.data(xml)
|
|
aci.result['preview'] = xmldata
|
|
else:
|
|
aci.result['preview'] = response_data
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|