ACI Config Rollback: Add new module to support config rollbacks in ACI (#28811)

* ACI Config Rollback: Add new module to support config rollbacks in ACI

* Fix EXAMPLES

* Fix whitespace
pull/28814/head
Jacob McGill 7 years ago committed by Dag Wieers
parent 42db02c249
commit 8ffb842963

@ -78,6 +78,7 @@ URL_MAPPING = dict(
fc_policy=dict(aci_class='fcIfPol', mo='infra/fcIfPol-', key='name'),
filter=dict(aci_class='vzFilter', mo='flt-', key='name'),
gateway_addr=dict(aci_class='fvSubnet', mo='subnet-', key='ip'),
import_policy=dict(aci_class='configImportP', mo='fabric/configimp-', key='name'),
l2_policy=dict(aci_class='l2IfPol', mo='infra/l2IfP-', key='name'),
lldp_policy=dict(aci_class='lldpIfPol', mo='infra/lldpIfP-', key='name'),
mcp=dict(aci_class='mcpIfPol', mo='infra/mcpIfP-', key='name'),

@ -0,0 +1,267 @@
#!/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 for Cisco ACI fabrics (config:ImportP)
description:
- Provides rollback and rollback preview functionality for Cisco ACI fabric.
- Config Rollbacks are done using snapshots C(aci_snapshot) with the configImportP class.
- More information from the internal APIC class
I(config:ImportP) at U(https://developer.cisco.com/media/mim-ref/MO-configImportP.html).
author:
- Swetha Chunduri (@schunduri)
- Dag Wieers (@dagwieers)
- Jacob McGill (@jmcgill298)
version_added: '2.4'
requirements:
- ACI Fabric 1.0(3f)+
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 new Import Policies to C(yes).
type: bool
default: 'yes'
import_mode:
description:
- Determines how the import should be handled by the APIC.
- The APIC defaults new Import Policies to C(atomic).
choices: [ atomic, best-effort ]
default: atomic
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 new Import Policies to C(replace).
choices: [ merge, replace ]
default: 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:
hostname: apic
username: admin
password: SomeSecretPassword
state: present
export_policy: config_backup
- name: Query Existing Snapshots
aci_config_snapshot:
hostname: apic
username: admin
password: SomeSecretPassword
state: query
export_policy: config_backup
- name: Compare Snapshot Files
aci_config_rollback:
hostname: apic
username: admin
password: SomeSecretPassword
state: preview
export_policy: config_backup
snapshot: 'run-2017-08-28T06-24-01'
compare_export_policy: config_backup
compare_snapshot: 'run-2017-08-27T23-43-56'
- name: Rollback Configuration
aci_config_rollback:
hostname: apic
username: admin
password: SomeSecretPassword
state: rollback
import_policy: rollback_config
export_policy: config_backup
snapshot: 'run-2017-08-28T06-24-01'
- name: Rollback Configuration
aci_config_rollback:
hostname: apic
username: admin
password: SomeSecretPassword
state: rollback
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
'''
RETURN = r'''
#
'''
from ansible.module_utils.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']],
],
)
description = module.params['description']
export_policy = module.params['export_policy']
fail_on_decrypt = module.params['fail_on_decrypt']
if fail_on_decrypt is True:
fail_on_decrypt = 'yes'
elif fail_on_decrypt is False:
fail_on_decrypt = 'no'
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']
aci = ACIModule(module)
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="import_policy")
aci.get_existing()
# Filter out module parameters with null values
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',
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='configImportP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'preview':
aci.result['url'] = '%(protocol)s://%(hostname)s/mqapi2/snapshots.diff.xml' % module.params
aci.result['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)
module.exit_json(**aci.result)
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.result['url'] + aci.result['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.result['response'] = info['msg']
aci.result['status'] = info['status']
aci.result['method'] = 'GET'
# Handle APIC response
if info['status'] == 200:
xml_to_json(aci, resp.read())
else:
aci.result['apic_response'] = resp.read()
aci.module.fail_json(msg='Request failed: %(error_code)s %(error_text)s' % aci.result, **aci.result)
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['diff'] = xmldata
else:
aci.result['diff'] = response_data
if __name__ == "__main__":
main()
Loading…
Cancel
Save