From d02e4005c6f778cdc0f7ff8918e0af505d3a4a5a Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Sat, 25 Jul 2020 13:54:37 +0200 Subject: [PATCH 01/16] Change to cvprac lib for cv_facts - #219 Change import from module_utils to cvprac lib --- .../arista/cvp/plugins/modules/cv_facts.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py index 259b77509..3c9da3246 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py @@ -31,8 +31,12 @@ import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import Connection -from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient -from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError + +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError + from ansible_collections.arista.cvp.plugins.module_utils.tools_inventory import ( find_hostname_by_mac, find_containerName_by_containerId From f890e8b3c354ebdea24f805592ab91c4bcbaf541 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Sat, 25 Jul 2020 14:41:47 +0200 Subject: [PATCH 02/16] Change to cvprac lib for cv_configlet - #219 Validate with a playbook to upload / update configlets on CV server ``` ansible-playbook playbooks/dc1-upload-configlets.yml -i inventories/emea/inventory.yml PLAY [Build Switch configuration] *************************************************************************************************************** TASK [cvp_configlet_upload : generate intented variables] *************************************************************************************** Saturday 25 July 2020 14:40:34 +0200 (0:00:00.037) 0:00:00.037 ********* Saturday 25 July 2020 14:40:34 +0200 (0:00:00.037) 0:00:00.037 ********* ok: [cv_server] TASK [cvp_configlet_upload : collecting facts from CVP cv_server.] ****************************************************************************** Saturday 25 July 2020 14:40:36 +0200 (0:00:01.625) 0:00:01.663 ********* Saturday 25 July 2020 14:40:36 +0200 (0:00:01.625) 0:00:01.662 ********* ok: [cv_server] TASK [cvp_configlet_upload : create configlets on CVP cv_server.] ******************************************************************************* Saturday 25 July 2020 14:41:12 +0200 (0:00:36.053) 0:00:37.717 ********* Saturday 25 July 2020 14:41:12 +0200 (0:00:36.053) 0:00:37.716 ********* ok: [cv_server] PLAY RECAP ************************************************************************************************************************************** cv_server : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Saturday 25 July 2020 14:41:18 +0200 (0:00:06.047) 0:00:43.764 ********* =============================================================================== cvp_configlet_upload --------------------------------------------------- 43.73s ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ total ------------------------------------------------------------------ 43.73s Saturday 25 July 2020 14:41:18 +0200 (0:00:06.047) 0:00:43.763 ********* =============================================================================== cvp_configlet_upload : collecting facts from CVP cv_server. ----------------------------------------------------------------------------- 36.05s cvp_configlet_upload : create configlets on CVP cv_server. ------------------------------------------------------------------------------- 6.05s cvp_configlet_upload : generate intented variables --------------------------------------------------------------------------------------- 1.63s Playbook run took 0 days, 0 hours, 0 minutes, 43 seconds ``` --- .../arista/cvp/plugins/modules/cv_configlet.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py b/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py index b3fe65c7b..f878a3e5b 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py @@ -33,8 +33,11 @@ import traceback import logging from ansible.module_utils.basic import AnsibleModule -from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient -from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError + from ansible.module_utils.connection import Connection import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import DIFFLIB_IMP_ERR = None From cbc04dc30b8f16dedf55e9f1bc19b736713b1124 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Sat, 25 Jul 2020 15:21:33 +0200 Subject: [PATCH 03/16] Change to cvprac lib for cv_container - #219 --- .../arista/cvp/plugins/modules/cv_container.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_container.py b/ansible_collections/arista/cvp/plugins/modules/cv_container.py index f3a31fc0f..2e4fe7d3c 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_container.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_container.py @@ -37,8 +37,11 @@ import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import Connection -from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient -from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError, CvpApiError +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError + from ansible.module_utils.six import string_types TREELIB_IMP_ERR = None try: From 5cb28aa20e8fdc971dcb12effb83582261e8c775 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Sat, 25 Jul 2020 15:21:53 +0200 Subject: [PATCH 04/16] Change to cvprac lib for cv_device - #219 --- .../arista/cvp/plugins/modules/cv_device.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_device.py b/ansible_collections/arista/cvp/plugins/modules/cv_device.py index 33d6dd39b..106afb394 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_device.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_device.py @@ -32,10 +32,11 @@ import logging import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule -from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient -from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import ( - CvpLoginError -) +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError + from ansible.module_utils.connection import Connection DOCUMENTATION = r""" From e0261a1980cb64346e109f14306301475ca9a560 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Sat, 25 Jul 2020 16:05:42 +0200 Subject: [PATCH 05/16] Change to cvprac lib for cv_task - #219 --- ansible_collections/arista/cvp/plugins/modules/cv_task.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_task.py b/ansible_collections/arista/cvp/plugins/modules/cv_task.py index 186b38b39..de5a2748b 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_task.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_task.py @@ -32,8 +32,11 @@ import logging import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule -from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient -from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient +# from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError +from cvprac.cvp_client import CvpClient +from cvprac.cvp_client_errors import CvpLoginError + from ansible.module_utils.connection import Connection DOCUMENTATION = r''' From 3d86e35772ed669446ee04ddc9fc323f109b798a Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Mon, 27 Jul 2020 15:02:03 +0200 Subject: [PATCH 06/16] Ansible Sanity Checks --- .../cvp/plugins/modules/cv_configlet.py | 12 ++++++++-- .../cvp/plugins/modules/cv_container.py | 17 ++++++++++---- .../arista/cvp/plugins/modules/cv_device.py | 23 +++++++++++++------ .../arista/cvp/plugins/modules/cv_facts.py | 14 ++++++++--- .../arista/cvp/plugins/modules/cv_task.py | 13 +++++++++-- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py b/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py index f878a3e5b..2eeb189cd 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_configlet.py @@ -35,8 +35,13 @@ from ansible.module_utils.basic import AnsibleModule # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError -from cvprac.cvp_client import CvpClient -from cvprac.cvp_client_errors import CvpLoginError +try: + from cvprac.cvp_client import CvpClient + from cvprac.cvp_client_errors import CvpLoginError + HAS_CVPRAC = True +except ImportError: + HAS_CVPRAC = False + CVPRAC_IMP_ERR = traceback.format_exc() from ansible.module_utils.connection import Connection import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import @@ -700,6 +705,9 @@ def main(): if not HAS_DIFFLIB: module.fail_json(msg='difflib required for this module') + if not HAS_CVPRAC: + module.fail_json(msg='cvprac required for this module') + result = dict(changed=False, data={}) # messages = dict(issues=False) # Connect to CVP instance diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_container.py b/ansible_collections/arista/cvp/plugins/modules/cv_container.py index 2e4fe7d3c..f39de9b61 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_container.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_container.py @@ -39,8 +39,13 @@ from ansible.module_utils.connection import Connection # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError -from cvprac.cvp_client import CvpClient -from cvprac.cvp_client_errors import CvpLoginError +try: + from cvprac.cvp_client import CvpClient + from cvprac.cvp_client_errors import CvpLoginError, CvpApiError + HAS_CVPRAC = True +except ImportError: + HAS_CVPRAC = False + CVPRAC_IMP_ERR = traceback.format_exc() from ansible.module_utils.six import string_types TREELIB_IMP_ERR = None @@ -924,8 +929,8 @@ def configure_configlet_to_container(module, intended, facts): # If configlet is not in intended and does not match filter, ignore it # If filter is set to ['none'], we consider to NOT touch attachment in any situation. if (match_filter is False - and container_factinfo(container_name=container, facts=facts) is not None - and configlet not in container_factinfo(container_name=container, facts=facts)['configlets']): + and container_factinfo(container_name=container, facts=facts) is not None + and configlet not in container_factinfo(container_name=container, facts=facts)['configlets']): MODULE_LOGGER.warning('configlet does not match filter (%s) and is not in intended topology (%s), skipped', str( configlet_filter), str(container_info_cvp['configlets'])) continue @@ -1119,6 +1124,10 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) + + if not HAS_CVPRAC: + module.fail_json(msg='cvprac required for this module') + result = dict(changed=False, data={}) result['data']['taskIds'] = list() result['data']['tasks'] = list() diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_device.py b/ansible_collections/arista/cvp/plugins/modules/cv_device.py index 106afb394..174eff6e1 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_device.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_device.py @@ -30,12 +30,18 @@ } import logging +import traceback import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError -from cvprac.cvp_client import CvpClient -from cvprac.cvp_client_errors import CvpLoginError +try: + from cvprac.cvp_client import CvpClient + from cvprac.cvp_client_errors import CvpLoginError + HAS_CVPRAC = True +except ImportError: + HAS_CVPRAC = False + CVPRAC_IMP_ERR = traceback.format_exc() from ansible.module_utils.connection import Connection @@ -558,7 +564,7 @@ def build_new_devices_list(module): module=module, device_name=ansible_device_hostname ) if cvp_device is None: - module.fail_json(msg="Device not available on Cloudvision ("+ansible_device_hostname+")") + module.fail_json(msg="Device not available on Cloudvision (" + ansible_device_hostname + ")") if len(cvp_device) >= 0: if is_in_container(device=cvp_device, container="undefined_container"): device_info = { @@ -903,10 +909,9 @@ def devices_update(module, mode="override"): MODULE_LOGGER.debug(" * device_update - device facts: %s", str(device_facts)) MODULE_LOGGER.debug(" * device_update - var status for %s: add: %s / del: %s", - str(device_update["name"]), - str(configlets_add), - str(configlets_delete) - ) + str(device_update["name"]), + str(configlets_add), + str(configlets_delete)) # # Structure to list configlets to delete configlets_delete = list() # # Structure to list configlets to configure on device. @@ -1210,6 +1215,10 @@ def main(): choices=['merge', 'override', 'delete'])) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + if not HAS_CVPRAC: + module.fail_json(msg='cvprac required for this module') + # Connect to CVP instance module.client = connect(module) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py index 3c9da3246..250ab5130 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py @@ -28,14 +28,19 @@ 'supported_by': 'community' } import logging +import traceback import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.connection import Connection # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError - -from cvprac.cvp_client import CvpClient -from cvprac.cvp_client_errors import CvpLoginError +try: + from cvprac.cvp_client import CvpClient + from cvprac.cvp_client_errors import CvpLoginError + HAS_CVPRAC = True +except ImportError: + HAS_CVPRAC = False + CVPRAC_IMP_ERR = traceback.format_exc() from ansible_collections.arista.cvp.plugins.module_utils.tools_inventory import ( find_hostname_by_mac, @@ -500,6 +505,9 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + if not HAS_CVPRAC: + module.fail_json(msg='cvprac required for this module') + # Forge standard Ansible output result = dict(changed=False, ansible_facts={}) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_task.py b/ansible_collections/arista/cvp/plugins/modules/cv_task.py index de5a2748b..601fb5e42 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_task.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_task.py @@ -30,12 +30,18 @@ import time import logging +import traceback import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError -from cvprac.cvp_client import CvpClient -from cvprac.cvp_client_errors import CvpLoginError +try: + from cvprac.cvp_client import CvpClient + from cvprac.cvp_client_errors import CvpLoginError + HAS_CVPRAC = True +except ImportError: + HAS_CVPRAC = False + CVPRAC_IMP_ERR = traceback.format_exc() from ansible.module_utils.connection import Connection @@ -216,6 +222,9 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) + if not HAS_CVPRAC: + module.fail_json(msg='cvprac required for this module') + result = dict(changed=False) # Connect to CVP instance From 4d956ab54701f7ccbbc5524de24e4c2583b4b3e8 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Mon, 27 Jul 2020 22:06:20 +0200 Subject: [PATCH 07/16] Change to cvprac lib for cv_facts - #219 Update cv_facts functions to use cvprac primitive - get_net_element_info_by_device_id --> get_device_by_mac - get_devices_by_container_id --> get_devices_in_container --- ansible_collections/arista/cvp/plugins/modules/cv_facts.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py index 250ab5130..b956d0daa 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py @@ -196,7 +196,7 @@ def facts_devices(module, facts): # Add ImageBundle Info device['imageBundle'] = "" - deviceInfo = module.client.api.get_net_element_info_by_device_id(device['key']) + deviceInfo = module.client.api.get_device_by_mac(device['key']) if "imageBundleMapper" in deviceInfo: # There should only be one ImageBudle but its id is not decernable # If the Image is applied directly to the device its type will be 'netelement' @@ -356,7 +356,7 @@ def facts_containers(module, facts): MODULE_LOGGER.debug(' -> Working on %s', container['name']) container['devices'] = [] # Get list of devices attached to container. - applied_devices = module.client.api.get_devices_by_container_id(container['key']) + applied_devices = module.client.api.get_devices_in_container(container['key']) for device in applied_devices: container['devices'].append(device['fqdn']) @@ -505,6 +505,7 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + # TODO: Test CVPRAC version as well if not HAS_CVPRAC: module.fail_json(msg='cvprac required for this module') From 89a44ed0abaefec8660707c6a2f9a86b715d6077 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 29 Jul 2020 15:49:31 +0200 Subject: [PATCH 08/16] Initial AVD deploy/reset support - #219 --- .../cvp/plugins/module_utils/cv_tools.py | 38 +++++++++++++++++++ .../arista/cvp/plugins/modules/cv_device.py | 27 +++++++++---- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py b/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py index ace14770e..5ee17da3a 100644 --- a/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py +++ b/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py @@ -85,3 +85,41 @@ def match_filter(input, filter, default_always='all'): return True LOGGER.debug(" * is_in_filter - NOT matched") return False + +def cv_update_configlets_on_device(module, device_facts, add_configlets, del_configlets): + response = dict() + device_deletion = dict() + device_addition = dict() + # Initial Logging + LOGGER.debug(' * cv_update_configlets_on_device - add_configlets: %s', str(add_configlets)) + LOGGER.debug(' * cv_update_configlets_on_device - del_configlets: %s', str(del_configlets)) + # Work on delete configlet scenario + LOGGER.info(" * cv_update_configlets_on_device - start device deletion process") + if len(del_configlets) > 0: + LOGGER.debug(' * cv_update_configlets_on_device - DELETE configlets: %s', str(del_configlets)) + try: + device_deletion = module.client.api.remove_configlets_from_device( + app_name="Ansible", + dev=device_facts, + del_configlets=del_configlets, + create_task=True + ) + LOGGER.info(" * cv_update_configlets_on_device - device_deletion result: %s", str(device_deletion)) + response.update(device_deletion) + except Exception as error: + errorMessage = str(error) + LOGGER.debug('OK, something wrong happens, raise an exception: %s', str(message)) + # Work on Add configlet scenario + LOGGER.debug(" * cv_update_configlets_on_device - start device addition process") + if len(del_configlets) > 0: + LOGGER.debug(' * cv_update_configlets_on_device - ADD configlets: %s', str(add_configlets)) + device_addition = module.client.api.apply_configlets_to_device( + app_name="Ansible", + dev=device_facts, + add_configlets=add_configlets, + create_task=True + ) + LOGGER.debug(" * cv_update_configlets_on_device - device_addition result: %s", str(device_addition)) + response.update(device_addition) + LOGGER.debug(" * cv_update_configlets_on_device - final result: %s", str(response)) + return response \ No newline at end of file diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_device.py b/ansible_collections/arista/cvp/plugins/modules/cv_device.py index 174eff6e1..5992d0ab9 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_device.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_device.py @@ -33,6 +33,7 @@ import traceback import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import from ansible.module_utils.basic import AnsibleModule +from ansible_collections.arista.cvp.plugins.module_utils.cv_tools import cv_update_configlets_on_device # from ansible_collections.arista.cvp.plugins.module_utils.cv_client import CvpClient # from ansible_collections.arista.cvp.plugins.module_utils.cv_client_errors import CvpLoginError try: @@ -710,12 +711,13 @@ def devices_new(module): # Execute configlet update on device try: - device_action = module.client.api.provision_device( + MODULE_LOGGER.info('provision device using cvprac.api.deploy_device') + device_action = module.client.api.deploy_device( app_name="Ansible", device=device_facts, - container=container_facts, + container=container_facts['name'], configlets=configlets_add, - imageBundle=imageBundle_attached, + # imageBundle=imageBundle_attached, create_task=action_save_topology, ) except Exception as error: @@ -724,6 +726,7 @@ def devices_new(module): device_update["name"], errorMessage, ) + MODULE_LOGGER.debug('OK, something wrong happens, raise an exception: %s', str(message)) result_update.append({device_update["name"]: message}) else: # Capture and report error message sent by CV during update @@ -968,14 +971,24 @@ def devices_update(module, mode="override"): module.fail_json("Error - device does not exists on CV side.") # Execute configlet update on device + MODULE_LOGGER.debug(' * device_update - device_update configlets: %s', str(device_update["configlets"])) + MODULE_LOGGER.debug(' * device_update - cv_configlets configlets: %s', str(device_update["cv_configlets"])) if is_list_diff(device_update["configlets"], device_update["cv_configlets"]): try: - device_action = module.client.api.update_configlets_on_device( - app_name="Ansible", - device=device_facts, + # device_action = module.client.api.update_configlets_on_device( + # app_name="Ansible", + # device=device_facts, + # add_configlets=configlets_add, + # del_configlets=configlets_delete, + # ) + MODULE_LOGGER.debug("", str(configlets_add)) + device_action = cv_update_configlets_on_device( + module=module, + device_facts=device_facts, add_configlets=configlets_add, - del_configlets=configlets_delete, + del_configlets=configlets_delete ) + except Exception as error: errorMessage = str(error) message = "Device %s Configlets cannot be updated - %s" % ( From d45d2376353d909795211a7df7a27958903135c9 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 29 Jul 2020 21:10:01 +0200 Subject: [PATCH 09/16] Fix an issue with configlets binding in cv_device - #219 Fix an issue in cv_device where configlets where not correctly attached to devices. --- .../cvp/plugins/module_utils/cv_tools.py | 35 ++++++++++--------- .../arista/cvp/plugins/modules/cv_device.py | 5 ++- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py b/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py index 5ee17da3a..8dbe6e5e4 100644 --- a/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py +++ b/ansible_collections/arista/cvp/plugins/module_utils/cv_tools.py @@ -88,15 +88,14 @@ def match_filter(input, filter, default_always='all'): def cv_update_configlets_on_device(module, device_facts, add_configlets, del_configlets): response = dict() - device_deletion = dict() - device_addition = dict() + device_deletion = None + device_addition = None # Initial Logging LOGGER.debug(' * cv_update_configlets_on_device - add_configlets: %s', str(add_configlets)) LOGGER.debug(' * cv_update_configlets_on_device - del_configlets: %s', str(del_configlets)) # Work on delete configlet scenario LOGGER.info(" * cv_update_configlets_on_device - start device deletion process") if len(del_configlets) > 0: - LOGGER.debug(' * cv_update_configlets_on_device - DELETE configlets: %s', str(del_configlets)) try: device_deletion = module.client.api.remove_configlets_from_device( app_name="Ansible", @@ -104,22 +103,26 @@ def cv_update_configlets_on_device(module, device_facts, add_configlets, del_con del_configlets=del_configlets, create_task=True ) - LOGGER.info(" * cv_update_configlets_on_device - device_deletion result: %s", str(device_deletion)) - response.update(device_deletion) + response = device_deletion except Exception as error: errorMessage = str(error) - LOGGER.debug('OK, something wrong happens, raise an exception: %s', str(message)) + LOGGER.error('OK, something wrong happens, raise an exception: %s', str(errorMessage)) + LOGGER.info(" * cv_update_configlets_on_device - device_deletion result: %s", str(device_deletion)) # Work on Add configlet scenario LOGGER.debug(" * cv_update_configlets_on_device - start device addition process") - if len(del_configlets) > 0: + if len(add_configlets) > 0: LOGGER.debug(' * cv_update_configlets_on_device - ADD configlets: %s', str(add_configlets)) - device_addition = module.client.api.apply_configlets_to_device( - app_name="Ansible", - dev=device_facts, - add_configlets=add_configlets, - create_task=True - ) - LOGGER.debug(" * cv_update_configlets_on_device - device_addition result: %s", str(device_addition)) - response.update(device_addition) - LOGGER.debug(" * cv_update_configlets_on_device - final result: %s", str(response)) + try: + device_addition = module.client.api.apply_configlets_to_device( + app_name="Ansible", + dev=device_facts, + new_configlets=add_configlets, + create_task=True + ) + response.update(device_addition) + except Exception as error: + errorMessage = str(error) + LOGGER.error('OK, something wrong happens, raise an exception: %s', str(errorMessage)) + LOGGER.info(" * cv_update_configlets_on_device - device_addition result: %s", str(device_addition)) + LOGGER.info(" * cv_update_configlets_on_device - final result: %s", str(response)) return response \ No newline at end of file diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_device.py b/ansible_collections/arista/cvp/plugins/modules/cv_device.py index 5992d0ab9..94d2dea7a 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_device.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_device.py @@ -974,7 +974,9 @@ def devices_update(module, mode="override"): MODULE_LOGGER.debug(' * device_update - device_update configlets: %s', str(device_update["configlets"])) MODULE_LOGGER.debug(' * device_update - cv_configlets configlets: %s', str(device_update["cv_configlets"])) if is_list_diff(device_update["configlets"], device_update["cv_configlets"]): + MODULE_LOGGER.debug(' * device_update - call cv_update_configlets_on_device') try: + MODULE_LOGGER.debug(' * device_update - cv_configlets configlets: %s') # device_action = module.client.api.update_configlets_on_device( # app_name="Ansible", # device=device_facts, @@ -988,7 +990,7 @@ def devices_update(module, mode="override"): add_configlets=configlets_add, del_configlets=configlets_delete ) - + MODULE_LOGGER.debug(' * device_update - get response from cv_update_configlets_on_device: %s', str(device_action)) except Exception as error: errorMessage = str(error) message = "Device %s Configlets cannot be updated - %s" % ( @@ -1006,6 +1008,7 @@ def devices_update(module, mode="override"): result_update.append({device_update["name"]: message}) else: changed = True # noqa # pylint: disable=unused-variable + MODULE_LOGGER.debug(' * device_update - looking for taskIds in %s', str(device_action)) if "taskIds" in str(device_action): devices_updated += 1 for taskId in device_action["data"]["taskIds"]: From d28f63af0ee86ac70ca5f4563aa955c42098c28b Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Thu, 30 Jul 2020 13:26:43 +0200 Subject: [PATCH 10/16] Add more logging to cv_container --- .../arista/cvp/plugins/modules/cv_container.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_container.py b/ansible_collections/arista/cvp/plugins/modules/cv_container.py index f39de9b61..4f00f6ec1 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_container.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_container.py @@ -868,6 +868,8 @@ def configure_configlet_to_container(module, intended, facts): # Define wether we want to save topology or not # Force to True as per issue115 save_topology = True + # Structure to save CVP result for configlet changes + configlet_action = dict() # Configlet filter configlet_filter = module.params['configlet_filter'] # Read complete intended topology to locate devices @@ -904,6 +906,7 @@ def configure_configlet_to_container(module, intended, facts): new_configlets=configlet_list_attach, container=container_info_cvp, create_task=save_topology) + MODULE_LOGGER.debug('Get following response from cvprac for addition: %s', str(configlet_action)) # Release list of configlet to configure (#165) configlet_list_attach = list() if 'data' in configlet_action and configlet_action['data']['status'] == 'success': @@ -922,7 +925,7 @@ def configure_configlet_to_container(module, intended, facts): container_info_cvp), str(container_name)) if container_info_cvp is not None and 'configlets' in container_info_cvp: for configlet in container_info_cvp['configlets']: - # If configlet matchs filter, we just remove attachement. + # If configlet matchs filter, we just remove attachment. match_filter = cv_tools.match_filter( input=configlet, filter=configlet_filter, default_always='none') MODULE_LOGGER.info('Filter test has returned: %s - Filter is %s - input is %s', str(match_filter), str(configlet_filter), str(configlet)) @@ -963,6 +966,7 @@ def configure_configlet_to_container(module, intended, facts): del_configlets=configlet_list_detach, container=container_info_cvp, create_task=save_topology) + MODULE_LOGGER.debug('Get following response from cvprac for deletion: %s', str(configlet_action)) # Release list of configlet to configure (#165) configlet_list_detach = list() if 'data' in configlet_action and configlet_action['data']['status'] == 'success': @@ -979,6 +983,7 @@ def configure_configlet_to_container(module, intended, facts): result['changed'] = True result['attached_configlet'] = attached result['detached_configlet'] = detached + MODULE_LOGGER.debug('configure_configlet_to_container returns %s', str(result)) return result From 87af1fd0c9fc41c5558f3f31b098c067e8559614 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 12 Aug 2020 18:52:25 +0200 Subject: [PATCH 11/16] Change CVPRAC function to get_device_image_info --- ansible_collections/arista/cvp/plugins/modules/cv_facts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py index b956d0daa..e021022c2 100644 --- a/ansible_collections/arista/cvp/plugins/modules/cv_facts.py +++ b/ansible_collections/arista/cvp/plugins/modules/cv_facts.py @@ -196,7 +196,8 @@ def facts_devices(module, facts): # Add ImageBundle Info device['imageBundle'] = "" - deviceInfo = module.client.api.get_device_by_mac(device['key']) + deviceInfo = module.client.api.get_device_image_info( + device['key']) # get_device_image_info() from cvprac if "imageBundleMapper" in deviceInfo: # There should only be one ImageBudle but its id is not decernable # If the Image is applied directly to the device its type will be 'netelement' From 6f7fbb4f5507cf9920891ff421b88f0db3e003cb Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 26 Aug 2020 10:37:48 +0200 Subject: [PATCH 12/16] Update README documentation --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e9fa790b..364f43a40 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,9 @@ ansible_httpapi_port=443 This collection requires the following to be installed on the Ansible control machine: -- python `3.7` +- python `3.6` and higher - ansible >= `2.9.0` +- [cvprac](https://github.com/aristanetworks/cvprac) version `1.0.4` - requests >= `2.22.0` - treelib version `1.5.5` @@ -213,4 +214,4 @@ Support for this `arista.cvp` collection is provided by the community directly i ## License -Project is published under [Apache 2.0 License](LICENSE) \ No newline at end of file +Project is published under [Apache 2.0 License](LICENSE) From db7c4912be76d580be2b7318bc2784f7c1335d1d Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 26 Aug 2020 10:38:02 +0200 Subject: [PATCH 13/16] Update README documentation --- ansible_collections/arista/cvp/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ansible_collections/arista/cvp/README.md b/ansible_collections/arista/cvp/README.md index c231a5396..fe06c75d4 100644 --- a/ansible_collections/arista/cvp/README.md +++ b/ansible_collections/arista/cvp/README.md @@ -18,6 +18,7 @@ __Python:__ __Additional Python Libraries required:__ +- [cvprac](https://github.com/aristanetworks/cvprac) version `1.0.4` - requests >= `2.22.0` - treelib version `1.5.5` or later @@ -30,6 +31,7 @@ ansible 2.9 or later ```shell pip install requests>=2.22.0 pip install treelib>=1.5.5 +pip install cvprac==1.0.4 ``` Ansible galaxy hosts all stable version of this collection. Installation from ansible-galaxy is the most convenient approach for consuming `arista.cvp` content @@ -38,7 +40,7 @@ Ansible galaxy hosts all stable version of this collection. Installation from an $ ansible-galaxy collection install arista.cvp Process install dependency map Starting collection install process -Installing 'arista.cvp:1.0.3' to '~/.ansible/collections/ansible_collections/arista/cvp' +Installing 'arista.cvp:1.1.0' to '~/.ansible/collections/ansible_collections/arista/cvp' ``` ## Modules overview From 5404a574708f5d8052c19bd667f4b5795bb0a48a Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 26 Aug 2020 10:38:24 +0200 Subject: [PATCH 14/16] Add cvprac in requirements file --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 9d6ffd697..693c70e68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ ansible>=2.9.0rc4,<2.10 requests==2.22.0 treelib==1.5.5 paramiko==2.7.1 +cvprac==1.0.4 From 0ab53dfc08814414d5368325c9d90e410c3791a8 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Thu, 27 Aug 2020 09:05:38 +0200 Subject: [PATCH 15/16] Update CI to fix #223 --- .../{pull_request.yml => ansible_test.yml} | 16 +++++++++ Makefile | 36 +++++++++++++------ 2 files changed, 42 insertions(+), 10 deletions(-) rename .github/workflows/{pull_request.yml => ansible_test.yml} (57%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/ansible_test.yml similarity index 57% rename from .github/workflows/pull_request.yml rename to .github/workflows/ansible_test.yml index fe94161d8..48dfb604e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/ansible_test.yml @@ -8,6 +8,10 @@ jobs: 'ansible-test': name: Run ansible-test validation runs-on: ubuntu-latest + container: ubuntu:18.04 + env: + PY_COLORS: 1 # allows molecule colors to be passed to GitHub Actions + ANSIBLE_FORCE_COLOR: 1 # allows ansible colors to be passed to GitHub Actions steps: - uses: actions/checkout@master - name: Setup Python 3 on runner @@ -15,9 +19,21 @@ jobs: with: python-version: '3.x' + - name: update packages + run: apt-get update + + - name: 'Install sudo' + run: apt-get -q -y install sudo + + - name: 'Install build essentials' + run: sudo apt-get install -q -y build-essential + - name: 'Install requirements' run: make github-configure-ci + - name: 'Install docker' + run: make install-docker + - name: 'Install Python requirements' run: make install-requirements diff --git a/Makefile b/Makefile index 63d88cee9..84ec03a9d 100644 --- a/Makefile +++ b/Makefile @@ -104,19 +104,35 @@ pre-commit-all: ## Execute pre-commit validation for all files pre-commit run --all-files .PHONY: github-configure-ci -github-configure-ci: ## Configure CI environment to run GA (Ubuntu:latest LTS) +github-configure-ci: github-configure-ci-python3 github-configure-ci-ansible ## Configure CI environment to run GA (Ubuntu:latest LTS) + +.PHONY: github-configure-ci-ansible +github-configure-ci-ansible: ## Install Ansible Test 2.9 on GA (Ubuntu:latest LTS) sudo apt-get update sudo apt-get install -y gnupg2 sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367 - sudo echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu bionic main" | sudo tee /etc/apt/sources.list.d/ansible.list - sudo echo "deb-src http://ppa.launchpad.net/ansible/ansible/ubuntu bionic main" | sudo tee -a /etc/apt/sources.list.d/ansible.list + sudo echo "deb http://ppa.launchpad.net/ansible/ansible-2.9/ubuntu bionic main" | sudo tee /etc/apt/sources.list.d/ansible.list + sudo echo "deb-src http://ppa.launchpad.net/ansible/ansible-2.9/ubuntu bionic main" | sudo tee -a /etc/apt/sources.list.d/ansible.list + sudo apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install -q -y ansible-test + +.PHONY: github-configure-ci-python3 +github-configure-ci-python3: ## Configure Python3 environment to run GA (Ubuntu:latest LTS) sudo apt-get update - sudo apt-get install ansible-test - sudo pip install --upgrade wheel - sudo pip install -r requirements.txt + sudo apt-get upgrade -y + sudo apt-get install -y python3 python3-pip git python3-setuptools + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10 .PHONY: install-requirements -install-requirements: ## Install python requirements - pip install --upgrade wheel - pip install -r requirements.txt - pip install -r development/requirements-dev.txt \ No newline at end of file +install-requirements: ## Install python requirements for generic purpose + pip3 install --upgrade wheel + pip3 install -r development/requirements.txt + pip3 install -r development/requirements-dev.txt + +.PHONY: install-docker +install-docker: ## Install docker + sudo apt install -q -y apt-transport-https ca-certificates curl software-properties-common + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" + sudo apt update + sudo apt install -q -y docker-ce From 28ac494c0fdfc9c776c1763c6c52d1f38be5af02 Mon Sep 17 00:00:00 2001 From: Thomas Grimonet Date: Wed, 2 Sep 2020 18:05:26 +0200 Subject: [PATCH 16/16] Add disclaimer on READMEs --- README.md | 2 ++ ansible_collections/arista/cvp/README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 364f43a40..188fc56c6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # Ansible Modules for Arista CloudVision Platform +> All the CV communication are now managed by [__cvprac library__](https://github.com/aristanetworks/cvprac). So a new [requirements](#dependencies) __MUST__ be installed first before any code execution. + diff --git a/ansible_collections/arista/cvp/README.md b/ansible_collections/arista/cvp/README.md index fe06c75d4..007e720cf 100644 --- a/ansible_collections/arista/cvp/README.md +++ b/ansible_collections/arista/cvp/README.md @@ -1,5 +1,7 @@ # Ansible Modules for Arista CloudVision Platform +> All the CV communication are now managed by [__cvprac library__](https://github.com/aristanetworks/cvprac). So a new [requirements](#dependencies) __MUST__ be installed first before any code execution. + ## About [Arista Networks](https://www.arista.com/) supports Ansible for managing devices running the EOS operating system through [CloudVision platform (CVP)](https://www.arista.com/en/products/eos/eos-cloudvision). This roles includes a set of ansible modules that perform specific configuration tasks on CVP server. These tasks include: collecting facts, managing configlets, containers, build provisionning topology and running tasks.