├── .gitignore ├── .gitmodules ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── README.md ├── defaults └── main.yml ├── files └── virl2_client-0.8.2+b4d055d25-py3-none-any.whl ├── handlers └── main.yml ├── library ├── iperf3.py ├── vmanage_central_policy.py ├── vmanage_central_policy_facts.py ├── vmanage_certificate.py ├── vmanage_device.py ├── vmanage_device_action_status.py ├── vmanage_device_attachment.py ├── vmanage_device_bootstrap.py ├── vmanage_device_certificate.py ├── vmanage_device_facts.py ├── vmanage_device_template.py ├── vmanage_device_template_facts.py ├── vmanage_feature_template.py ├── vmanage_feature_template_facts.py ├── vmanage_fileupload.py ├── vmanage_nping.py ├── vmanage_policy_definition.py ├── vmanage_policy_definition_facts.py ├── vmanage_policy_list.py ├── vmanage_policy_list_facts.py ├── vmanage_settings.py ├── vmanage_software_facts.py ├── vmanage_software_upgrade.py ├── vmanage_software_upload.py ├── vmanage_template_export.py ├── vmanage_template_facts.py └── vmanage_template_import.py ├── meta └── main.yml ├── module_utils └── viptela.py ├── plugins └── httpapi │ └── vmanage.py ├── requirements.txt ├── tasks ├── add-controller.yml ├── bootstrap-vedge.yml ├── device-template.yml ├── facts.yml ├── feature-aaa.yml ├── feature-banner.yml ├── feature-bfd.yml ├── feature-interface.yml ├── feature-logging.yml ├── feature-system.yml ├── feature-vpn.yml ├── generic-template-workflow │ ├── README.md │ ├── create-template-aggregate.yml │ ├── create-template-single.yml │ ├── delete-template-aggregate.yml │ ├── init-datastructures.yml │ ├── pretty-json.yml │ └── render.yml ├── get-cookie.yml ├── get-csr.yml ├── install-cert.yml ├── install-serials.yml ├── main.yml ├── ping-cedge.yml ├── ping-test.yml ├── ping-vedge.yml ├── push-certs.yml ├── set-org.yml ├── set-rootca.yml └── set-vbond.yml ├── tests ├── inventory └── test.yml └── vars └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | files/rendered_templates -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "templates/sdwan-templates"] 2 | path = templates/sdwan-templates 3 | url = https://github.com/CiscoDevNet/sdwan-templates.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 2 | 3 | COPY requirements.txt /tmp/requirements.txt 4 | RUN echo "===> Installing GCC ****" && \ 5 | apk add --no-cache gcc musl-dev make && \ 6 | \ 7 | \ 8 | echo "===> Installing Python ****" && \ 9 | apk add --no-cache python3 && \ 10 | if [ ! -e /usr/bin/python ]; then ln -sf python3 /usr/bin/python ; fi && \ 11 | \ 12 | \ 13 | echo "**** Installing pip ****" && \ 14 | python3 -m ensurepip && \ 15 | rm -r /usr/lib/python*/ensurepip && \ 16 | pip3 install --no-cache --upgrade pip setuptools wheel && \ 17 | if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi && \ 18 | \ 19 | \ 20 | echo "===> Installing dependancies..." && \ 21 | apk --update add sshpass libffi-dev libxml2-dev libxslt-dev python3-dev openssl-dev openssh-keygen && \ 22 | \ 23 | \ 24 | echo "===> Installing PIP Requirements..." && \ 25 | pip install -r /tmp/requirements.txt 26 | 27 | COPY files/virl2_client-0.8.2+b4d055d25-py3-none-any.whl /tmp/virl2_client-0.8.2+b4d055d25-py3-none-any.whl 28 | RUN echo "===> Installing VIRL Client..." && \ 29 | pip install /tmp/virl2_client-0.8.2+b4d055d25-py3-none-any.whl 30 | 31 | RUN echo "===> Installing Terraform ****" && \ 32 | apk --update add wget unzip cdrkit curl && \ 33 | \ 34 | \ 35 | wget --quiet https://releases.hashicorp.com/terraform/0.12.12/terraform_0.12.12_linux_amd64.zip && \ 36 | unzip terraform_0.12.12_linux_amd64.zip && \ 37 | mv terraform /usr/bin && \ 38 | rm terraform_0.12.12_linux_amd64.zip 39 | 40 | # Define working directory. 41 | ENV ANSIBLE_HOST_KEY_CHECKING false 42 | ENV ANSIBLE_RETRY_FILES_ENABLED false 43 | ENV ANSIBLE_SSH_PIPELINING True 44 | 45 | WORKDIR /ansible 46 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | dockerfile { 4 | filename 'Dockerfile' 5 | args '-v /etc/passwd:/etc/passwd' 6 | } 7 | } 8 | options { 9 | disableConcurrentBuilds() 10 | lock resource: 'jenkins_testing' 11 | } 12 | environment { 13 | VIRL_USERNAME = credentials('cpn-virl-username') 14 | VIRL_PASSWORD = credentials('cpn-virl-password') 15 | VIRL_HOST = credentials('cpn-virl-host') 16 | VIRL_LAB = "jenkins_ansible-viptela" 17 | VMANAGE_ORG = credentials('viptela-org') 18 | LICENSE_TOKEN = credentials('license-token') 19 | HOME = "${WORKSPACE}" 20 | DEFAULT_LOCAL_TMP = "${WORKSPACE}/ansible" 21 | } 22 | stages { 23 | stage('Checkout Code') { 24 | steps { 25 | checkout([ 26 | $class: 'GitSCM', 27 | branches: [[name: '*/master']], 28 | doGenerateSubmoduleConfigurations: false, 29 | extensions: scm.extensions + [[$class: 'SubmoduleOption', 30 | parentCredentials: true, 31 | disableSubmodules: true, 32 | recursiveSubmodules: false, 33 | reference: '', 34 | trackingSubmodules: false]], 35 | /* userRemoteConfigs: scm.userRemoteConfigs */ 36 | userRemoteConfigs: [[url: 'https://github.com/ciscodevnet/sdwan-devops']] 37 | ]) 38 | checkout([$class: 'GitSCM', 39 | branches: [[name: '*/master']], 40 | doGenerateSubmoduleConfigurations: false, 41 | extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'roles/ansible-virl']], 42 | submoduleCfg: [], 43 | userRemoteConfigs: [[url: 'https://github.com/ciscodevnet/ansible-virl']] 44 | ]) 45 | checkout([$class: 'GitSCM', 46 | branches: scm.branches, 47 | doGenerateSubmoduleConfigurations: false, 48 | extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'roles/ansible-viptela']], 49 | submoduleCfg: [], 50 | userRemoteConfigs: scm.userRemoteConfigs 51 | ]) 52 | } 53 | } 54 | stage('Build VIRL Topology') { 55 | steps { 56 | echo 'Create local CA...' 57 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'build-ca.yml' 58 | echo 'Running build.yml...' 59 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'build-virl.yml' 60 | } 61 | } 62 | stage('Configure SD-WAN Fabric') { 63 | steps { 64 | echo 'Configure Viptela Control Plane...' 65 | withCredentials([file(credentialsId: 'viptela-serial-file', variable: 'VIPTELA_SERIAL_FILE')]) { 66 | ansiblePlaybook disableHostKeyChecking: true, extras: '-e sdwan_serial_file=${VIPTELA_SERIAL_FILE}', playbook: 'config-virl.yml' 67 | } 68 | echo 'Deploying edges...' 69 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'deploy-virl.yml' 70 | } 71 | } 72 | stage('Running Tests') { 73 | steps { 74 | echo 'Check SD-WAN...' 75 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'waitfor-sync.yml' 76 | echo 'Check SD-WAN...' 77 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'check-sdwan.yml' 78 | } 79 | } 80 | } 81 | post { 82 | always { 83 | ansiblePlaybook disableHostKeyChecking: true, playbook: 'clean-virl.yml' 84 | cleanWs() 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CISCO SAMPLE CODE LICENSE 2 | Version 1.0 3 | Copyright (c) 2017 Cisco and/or its affiliates 4 | 5 | These terms govern this Cisco example or demo source code and its 6 | associated documentation (together, the "Sample Code"). By downloading, 7 | copying, modifying, compiling, or redistributing the Sample Code, you 8 | accept and agree to be bound by the following terms and conditions (the 9 | "License"). If you are accepting the License on behalf of an entity, you 10 | represent that you have the authority to do so (either you or the entity, 11 | "you"). Sample Code is not supported by Cisco TAC and is not tested for 12 | quality or performance. This is your only license to the Sample Code and 13 | all rights not expressly granted are reserved. 14 | 15 | 1. LICENSE GRANT: Subject to the terms and conditions of this License, 16 | Cisco hereby grants to you a perpetual, worldwide, non-exclusive, non- 17 | transferable, non-sublicensable, royalty-free license to copy and 18 | modify the Sample Code in source code form, and compile and 19 | redistribute the Sample Code in binary/object code or other executable 20 | forms, in whole or in part, solely for use with Cisco products and 21 | services. For interpreted languages like Java and Python, the 22 | executable form of the software may include source code and 23 | compilation is not required. 24 | 25 | 2. CONDITIONS: You shall not use the Sample Code independent of, or to 26 | replicate or compete with, a Cisco product or service. Cisco products 27 | and services are licensed under their own separate terms and you shall 28 | not use the Sample Code in any way that violates or is inconsistent 29 | with those terms (for more information, please visit: 30 | www.cisco.com/go/terms. 31 | 32 | 3. OWNERSHIP: Cisco retains sole and exclusive ownership of the Sample 33 | Code, including all intellectual property rights therein, except with 34 | respect to any third-party material that may be used in or by the 35 | Sample Code. Any such third-party material is licensed under its own 36 | separate terms (such as an open source license) and all use must be in 37 | full accordance with the applicable license. This License does not 38 | grant you permission to use any trade names, trademarks, service 39 | marks, or product names of Cisco. If you provide any feedback to Cisco 40 | regarding the Sample Code, you agree that Cisco, its partners, and its 41 | customers shall be free to use and incorporate such feedback into the 42 | Sample Code, and Cisco products and services, for any purpose, and 43 | without restriction, payment, or additional consideration of any kind. 44 | If you initiate or participate in any litigation against Cisco, its 45 | partners, or its customers (including cross-claims and counter-claims) 46 | alleging that the Sample Code and/or its use infringe any patent, 47 | copyright, or other intellectual property right, then all rights 48 | granted to you under this License shall terminate immediately without 49 | notice. 50 | 51 | 4. LIMITATION OF LIABILITY: CISCO SHALL HAVE NO LIABILITY IN CONNECTION 52 | WITH OR RELATING TO THIS LICENSE OR USE OF THE SAMPLE CODE, FOR 53 | DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DIRECT, INCIDENTAL, 54 | AND CONSEQUENTIAL DAMAGES, OR FOR ANY LOSS OF USE, DATA, INFORMATION, 55 | PROFITS, BUSINESS, OR GOODWILL, HOWEVER CAUSED, EVEN IF ADVISED OF THE 56 | POSSIBILITY OF SUCH DAMAGES. 57 | 58 | 5. DISCLAIMER OF WARRANTY: SAMPLE CODE IS INTENDED FOR EXAMPLE PURPOSES 59 | ONLY AND IS PROVIDED BY CISCO "AS IS" WITH ALL FAULTS AND WITHOUT 60 | WARRANTY OR SUPPORT OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY 61 | LAW, ALL EXPRESS AND IMPLIED CONDITIONS, REPRESENTATIONS, AND 62 | WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OR 63 | CONDITION OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON- 64 | INFRINGEMENT, SATISFACTORY QUALITY, NON-INTERFERENCE, AND ACCURACY, 65 | ARE HEREBY EXCLUDED AND EXPRESSLY DISCLAIMED BY CISCO. CISCO DOES NOT 66 | WARRANT THAT THE SAMPLE CODE IS SUITABLE FOR PRODUCTION OR COMMERCIAL 67 | USE, WILL OPERATE PROPERLY, IS ACCURATE OR COMPLETE, OR IS WITHOUT 68 | ERROR OR DEFECT. 69 | 70 | 6. GENERAL: This License shall be governed by and interpreted in 71 | accordance with the laws of the State of California, excluding its 72 | conflict of laws provisions. You agree to comply with all applicable 73 | United States export laws, rules, and regulations. If any provision of 74 | this License is judged illegal, invalid, or otherwise unenforceable, 75 | that provision shall be severed and the rest of the License shall 76 | remain in full force and effect. No failure by Cisco to enforce any of 77 | its rights related to the Sample Code or to a breach of this License 78 | in a particular situation will act as a waiver of such rights. In the 79 | event of any inconsistencies with any other terms, this License shall 80 | take precedence. 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ansible-viptela 2 | 3 | ** Note: The current version of the Ansible Viptela modules can be found here: https://github.com/ciscodevnet/python-viptela ** 4 | 5 | ## License 6 | 7 | CISCO SAMPLE CODE LICENSE 8 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | viptela_api_cookie_file: viptela_api_cookie 3 | serial_number_file: 'licenses/serialFile.viptela' 4 | validate_certs: no 5 | -------------------------------------------------------------------------------- /files/virl2_client-0.8.2+b4d055d25-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CiscoDevNet/ansible-viptela/0a44e1b7257ca5099d7719d3690b55c29f869f2f/files/virl2_client-0.8.2+b4d055d25-py3-none-any.whl -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for tower_api -------------------------------------------------------------------------------- /library/iperf3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | DOCUMENTATION = ''' 10 | --- 11 | module: iperf3_package 12 | 13 | short_description: This is my sample module 14 | 15 | version_added: "2.4" 16 | 17 | description: 18 | - "This is my longer description explaining my sample module" 19 | 20 | options: 21 | name: 22 | description: 23 | - The name of the package 24 | required: true 25 | state: 26 | description: 27 | - The state if the bridge ('present' or 'absent') (Default: 'present') 28 | required: false 29 | file: 30 | description: 31 | - The file name of the package 32 | required: false 33 | 34 | author: 35 | - Steven Carter 36 | ''' 37 | 38 | EXAMPLES = ''' 39 | # Upload and register a package 40 | - name: Package 41 | iperf3_package: 42 | host: 1.2.3.4 43 | user: admin 44 | password: cisco 45 | file: asav.tar.gz 46 | name: asav 47 | state: present 48 | 49 | # Deregister a package 50 | - name: Package 51 | iperf3_package: 52 | host: 1.2.3.4 53 | user: admin 54 | password: cisco 55 | name: asav 56 | state: absent 57 | ''' 58 | 59 | RETURN = ''' 60 | original_message: 61 | description: The original name param that was passed in 62 | type: str 63 | message: 64 | description: The output message that the sample module generates 65 | ''' 66 | 67 | # import requests 68 | from ansible.module_utils.basic import AnsibleModule, json 69 | from ansible.module_utils.iperf3 import iperf3Module, iperf3_argument_spec 70 | import iperf3 71 | 72 | def run_module(): 73 | # define available arguments/parameters a user can pass to the module 74 | argument_spec= (host=dict(type='str', required=True), 75 | duration=dict(type='int', defailt 1), 76 | bind_address=dict(type='str'), 77 | port=dict(type='int', default=5001), 78 | blksize=dict(type='int'), 79 | streams=dict(type='int', default=1), 80 | zerocopy=dict(type=True), 81 | verbose=dict(type='bool'), 82 | reverse=dict(type='bool', default=True), 83 | protocol=dict(type='str', choices=['tcp', 'udp'], default='tcp') 84 | ) 85 | 86 | # seed the result dict in the object 87 | # we primarily care about changed and state 88 | # change is if this module effectively modified the target 89 | # state will include any data that you want your module to pass back 90 | # for consumption, for example, in a subsequent task 91 | result = dict( 92 | changed=False, 93 | original_message='', 94 | message='' 95 | ) 96 | 97 | # the AnsibleModule object will be our abstraction working with Ansible 98 | # this includes instantiation, a couple of common attr would be the 99 | # args/params passed to the execution, as well as if the module 100 | # supports check mode 101 | module = AnsibleModule(argument_spec=argument_spec, 102 | supports_check_mode=True, 103 | ) 104 | 105 | client = iperf3.Client() 106 | client.server_hostname = module.params['host'] 107 | if module.params['duration']: 108 | client.duration = module.params['duration'] 109 | 110 | if module.params['port']: 111 | client.port = module.params['port'] 112 | 113 | if module.params['protocol']: 114 | client.protocol = module.params['protocol'] 115 | 116 | if module.params['bind_address']: 117 | client.bind_address = module.params['bind_address'] 118 | 119 | if module.params['blksize']: 120 | client.blksize = module.params['blksize'] 121 | 122 | if module.params['streams']: 123 | client.num_streams = module.params['streams'] 124 | 125 | if module.params['zerocopy']: 126 | client.zerocopy = module.params['zerocopy'] 127 | 128 | if module.params['reverse']: 129 | client.reverse = module.params['reverse'] 130 | 131 | result['stats'] = client.run() 132 | 133 | # if the user is working with this module in only check mode we do not 134 | # want to make any changes to the environment, just return the current 135 | # state with no modifications 136 | if module.check_mode: 137 | return result 138 | 139 | 140 | # if the user is working with this module in only check mode we do not 141 | # want to make any changes to the environment, just return the current 142 | # state with no modifications 143 | # FIXME: Work with iperf3 so they can implement a check mode 144 | if module.check_mode: 145 | module.exit_json(**iperf3.result) 146 | 147 | # execute checks for argument completeness 148 | 149 | # manipulate or modify the state as needed (this is going to be the 150 | # part where your module will do what it needs to do) 151 | 152 | # in the event of a successful module execution, you will want to 153 | # simple AnsibleModule.exit_json(), passing the key/value results 154 | module.exit_json(**iperf3.result) 155 | 156 | def main(): 157 | run_module() 158 | 159 | if __name__ == '__main__': 160 | main() -------------------------------------------------------------------------------- /library/vmanage_central_policy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import re 10 | from ansible.module_utils.basic import AnsibleModule 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(state=dict(type='str', choices=['absent', 'present', 'activated', 'deactivated'], default='present'), 18 | name = dict(type='str', alias='policyName'), 19 | description = dict(type='str', alias='policyDescription'), 20 | definition = dict(type='str', alias='policyDefinition'), 21 | type = dict(type='list', alias='policyType'), 22 | wait = dict(type='bool', default=False), 23 | aggregate=dict(type='list'), 24 | ) 25 | 26 | # seed the result dict in the object 27 | # we primarily care about changed and state 28 | # change is if this module effectively modified the target 29 | # state will include any data that you want your module to pass back 30 | # for consumption, for example, in a subsequent task 31 | result = dict( 32 | changed=False, 33 | ) 34 | 35 | # the AnsibleModule object will be our abstraction working with Ansible 36 | # this includes instantiation, a couple of common attr would be the 37 | # args/params passed to the execution, as well as if the module 38 | # supports check mode 39 | module = AnsibleModule(argument_spec=argument_spec, 40 | supports_check_mode=True, 41 | ) 42 | viptela = viptelaModule(module) 43 | 44 | # Always as an aggregate... make a list if just given a single entry 45 | if viptela.params['aggregate']: 46 | policy_list = viptela.params['aggregate'] 47 | else: 48 | if viptela.params['state'] == 'present': 49 | policy_list = [ 50 | { 51 | 'policyName': viptela.params['name'], 52 | 'policyDescription': viptela.params['description'], 53 | 'policyType': viptela.params['type'], 54 | 'policyDefinition': viptela.params['definition'], 55 | } 56 | ] 57 | else: 58 | policy_list = [ 59 | { 60 | 'policyName': viptela.params['name'], 61 | 'state': 'absent' 62 | } 63 | ] 64 | 65 | central_policy_dict = viptela.get_central_policy_dict(remove_key=False) 66 | 67 | compare_values = ['policyName', 'policyDescription', 'policyType', 'policyDefinition'] 68 | ignore_values = ["lastUpdatedOn", "lastUpdatedBy", "templateId", "createdOn", "createdBy"] 69 | 70 | for policy in policy_list: 71 | payload = { 72 | 'policyName': policy['policyName'] 73 | } 74 | if viptela.params['state'] == 'present': 75 | payload['policyDescription'] = policy['policyDescription'] 76 | payload['policyType'] = policy['policyType'] 77 | payload['policyDefinition'] = policy['policyDefinition'] 78 | # If a template by that name is already there 79 | if payload['policyName'] in central_policy_dict: 80 | viptela.result['changed'] = False 81 | # changed_items = viptela.compare_payloads(payload, device_template_dict[payload['templateName']], compare_values=compare_values) 82 | # if changed_items: 83 | # viptela.result['changed'] = True 84 | # viptela.result['what_changed'] = changed_items 85 | # viptela.result['old_payload'] = device_template_dict[payload['templateName']] 86 | # if not module.check_mode: 87 | # # 88 | # # Convert template names to template IDs 89 | # # 90 | # if payload['configType'] == 'template': 91 | # payload['generalTemplates'] = viptela.generalTemplates_to_id(device_template['generalTemplates']) 92 | # viptela.request('/dataservice/template/device/feature/{0}'.format(device_template_dict[payload['templateName']]['templateId']), 93 | # method='PUT', payload=payload) 94 | else: 95 | if not module.check_mode: 96 | # 97 | # Convert list and definition names to template IDs 98 | # 99 | regex = re.compile(r'^(?P.*)Lists$') 100 | for policy_item in policy['policyDefinition']['assembly']: 101 | definition_name = policy_item.pop('definitionName') 102 | policy_definition_dict = viptela.get_policy_definition_dict(policy_item['type']) 103 | if definition_name in policy_definition_dict: 104 | policy_item['definitionId'] = policy_definition_dict[definition_name]['definitionId'] 105 | else: 106 | viptela.fail_json(msg="Cannot find policy definition {0}".format(definition_name)) 107 | for entry in policy_item['entries']: 108 | for list_type, list in entry.items(): 109 | match = regex.search(list_type) 110 | if match: 111 | type = match.groups('type')[0] 112 | if type in viptela.POLICY_LIST_TYPES: 113 | policy_list_dict = viptela.get_policy_list_dict(type) 114 | for index, list_name in enumerate(list): 115 | list[index] = policy_list_dict[list_name]['listId'] 116 | else: 117 | viptela.fail_json(msg="Cannot find list type {0}".format(type)) 118 | 119 | viptela.request('/dataservice/template/policy/vsmart', method='POST', payload=payload) 120 | viptela.result['payload'] = payload 121 | viptela.result['changed'] = True 122 | elif viptela.params['state'] == 'activated': 123 | if policy['policyName'] in central_policy_dict: 124 | if not central_policy_dict[policy['policyName']]['isPolicyActivated']: 125 | viptela.result['changed'] = True 126 | if not module.check_mode: 127 | response = viptela.request('/dataservice/template/policy/vsmart/activate/{0}'.format(central_policy_dict[policy['policyName']]['policyId']), 128 | method='POST', payload=payload) 129 | if response.json: 130 | action_id = response.json['id'] 131 | else: 132 | viptela.fail_json(msg='Did not get action ID after attaching device to template.') 133 | if viptela.params['wait']: 134 | viptela.waitfor_action_completion(action_id) 135 | else: 136 | viptela.fail_json(msg="Cannot find central policy {0}".format(policy['policyName'])) 137 | if viptela.params['state'] in ['absent', 'deactivated']: 138 | if policy['policyName'] in central_policy_dict: 139 | if central_policy_dict[policy['policyName']]['isPolicyActivated']: 140 | viptela.result['changed'] = True 141 | if not module.check_mode: 142 | response = viptela.request('/dataservice/template/policy/vsmart/deactivate/{0}'.format(central_policy_dict[policy['policyName']]['policyId']), 143 | method='POST') 144 | if response.json: 145 | action_id = response.json['id'] 146 | else: 147 | viptela.fail_json(msg='Did not get action ID after attaching device to template.') 148 | if viptela.params['wait']: 149 | viptela.waitfor_action_completion(action_id) 150 | else: 151 | viptela.fail_json(msg="Cannot find central policy {0}".format(policy['policyName'])) 152 | elif viptela.params['state'] == 'absent': 153 | if device_template['templateName'] in device_template_dict: 154 | if not module.check_mode: 155 | viptela.request('/dataservice/template/policy/vsmart/{0}'.format(central_policy_dict[policy[policyName]]['policyId']), 156 | method='DELETE') 157 | viptela.result['changed'] = True 158 | 159 | viptela.exit_json(**viptela.result) 160 | 161 | def main(): 162 | run_module() 163 | 164 | if __name__ == '__main__': 165 | main() -------------------------------------------------------------------------------- /library/vmanage_central_policy_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | 18 | # seed the result dict in the object 19 | # we primarily care about changed and state 20 | # change is if this module effectively modified the target 21 | # state will include any data that you want your module to pass back 22 | # for consumption, for example, in a subsequent task 23 | result = dict( 24 | changed=False, 25 | ) 26 | 27 | # the AnsibleModule object will be our abstraction working with Ansible 28 | # this includes instantiation, a couple of common attr would be the 29 | # args/params passed to the execution, as well as if the module 30 | # supports check mode 31 | module = AnsibleModule(argument_spec=argument_spec, 32 | supports_check_mode=True, 33 | ) 34 | viptela = viptelaModule(module) 35 | 36 | viptela.result['central_policies'] = viptela.get_central_policy_list() 37 | 38 | viptela.exit_json(**viptela.result) 39 | 40 | def main(): 41 | run_module() 42 | 43 | if __name__ == '__main__': 44 | main() -------------------------------------------------------------------------------- /library/vmanage_certificate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | from collections import OrderedDict 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(organization=dict(type='str'), 17 | vbond=dict(type='str'), 18 | vbond_port=dict(type='int', default=12346), 19 | root_cert=dict(type='str'), 20 | push=dict(type='bool') 21 | ) 22 | 23 | # seed the result dict in the object 24 | # we primarily care about changed and state 25 | # change is if this module effectively modified the target 26 | # state will include any data that you want your module to pass back 27 | # for consumption, for example, in a subsequent task 28 | result = dict( 29 | changed=False, 30 | ) 31 | 32 | # the AnsibleModule object will be our abstraction working with Ansible 33 | # this includes instantiation, a couple of common attr would be the 34 | # args/params passed to the execution, as well as if the module 35 | # supports check mode 36 | module = AnsibleModule(argument_spec=argument_spec, 37 | supports_check_mode=True, 38 | ) 39 | viptela = viptelaModule(module) 40 | viptela.result['what_changed'] = [] 41 | 42 | if viptela.params['push']: 43 | viptela.push_certificates() 44 | 45 | if viptela.result['what_changed']: 46 | viptela.result['changed'] = True 47 | 48 | viptela.exit_json(**viptela.result) 49 | 50 | 51 | def main(): 52 | run_module() 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /library/vmanage_device.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | from collections import OrderedDict 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 17 | name=dict(type='str'), 18 | transport_ip=dict(type='str', aliases=['device_ip', 'system_ip']), 19 | uuid=dict(type='str', alias='deviceIP'), 20 | personality=dict(type='str', choices=['vmanage', 'vsmart', 'vbond', 'vedge'], default='vedge'), 21 | device_username=dict(type='str', alias='device_user'), 22 | device_password=dict(type='str') 23 | ) 24 | 25 | # seed the result dict in the object 26 | # we primarily care about changed and state 27 | # change is if this module effectively modified the target 28 | # state will include any data that you want your module to pass back 29 | # for consumption, for example, in a subsequent task 30 | result = dict( 31 | changed=False, 32 | ) 33 | 34 | # the AnsibleModule object will be our abstraction working with Ansible 35 | # this includes instantiation, a couple of common attr would be the 36 | # args/params passed to the execution, as well as if the module 37 | # supports check mode 38 | module = AnsibleModule(argument_spec=argument_spec, 39 | supports_check_mode=True, 40 | ) 41 | viptela = viptelaModule(module) 42 | viptela.result['what_changed'] = [] 43 | 44 | if viptela.params['personality'] == 'vedge': 45 | device_type = 'vedges' 46 | else: 47 | device_type = 'controllers' 48 | 49 | device = {} 50 | if viptela.params['uuid']: 51 | device = viptela.get_device_status(viptela.params['uuid'], key='uuid') 52 | # See if we can find the device by deviceIP 53 | if not device and viptela.params['transport_ip']: 54 | device = viptela.get_device_status(viptela.params['transport_ip']) 55 | # If we could not find the device by deviceIP, see if we can find it be (host)name 56 | if not device and viptela.params['name']: 57 | device = viptela.get_device_status(viptela.params['name'], key='host-name') 58 | 59 | viptela.result['device'] = device 60 | if viptela.params['state'] == 'present': 61 | if device: 62 | # Can't really change anything 63 | pass 64 | else: 65 | viptela.result['what_changed'].append('new') 66 | if not viptela.params['device_username'] or not viptela.params['device_password']: 67 | viptela.fail_json(msg="device_username and device_password must be specified when add a new device") 68 | if not module.check_mode: 69 | response = viptela.create_controller(viptela.params['transport_ip'], viptela.params['personality'], 70 | viptela.params['device_username'], viptela.params['device_password']) 71 | viptela.result['response'] = response 72 | else: 73 | if device and device_type == 'controllers': 74 | if not module.check_mode: 75 | viptela.delete_controller(device['uuid']) 76 | elif device and device_type == 'vedges': 77 | device_config = viptela.get_device_by_uuid(device['uuid'], type=device_type) 78 | if 'vedgeCertificateState' in device_config and device_config['vedgeCertificateState'] != 'tokengenerated': 79 | viptela.result['what_changed'].append('delete') 80 | if not module.check_mode: 81 | viptela.decommision_device(device['uuid']) 82 | 83 | if viptela.result['what_changed']: 84 | viptela.result['changed'] = True 85 | 86 | viptela.exit_json(**viptela.result) 87 | 88 | 89 | def main(): 90 | run_module() 91 | 92 | 93 | if __name__ == '__main__': 94 | main() 95 | -------------------------------------------------------------------------------- /library/vmanage_device_action_status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import time 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(id = dict(type='str', required=True), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | ) 28 | 29 | # the AnsibleModule object will be our abstraction working with Ansible 30 | # this includes instantiation, a couple of common attr would be the 31 | # args/params passed to the execution, as well as if the module 32 | # supports check mode 33 | module = AnsibleModule(argument_spec=argument_spec, 34 | supports_check_mode=True, 35 | ) 36 | viptela = viptelaModule(module) 37 | 38 | response = viptela.request('/dataservice/device/action/status/{0}'.format(viptela.params['id'])) 39 | if response.json: 40 | viptela.result['json'] = response.json 41 | 42 | viptela.exit_json(**viptela.result) 43 | 44 | def main(): 45 | run_module() 46 | 47 | if __name__ == '__main__': 48 | main() -------------------------------------------------------------------------------- /library/vmanage_device_attachment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import time 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | action_id = None 16 | action_status = None 17 | action_activity = None 18 | action_config = None 19 | 20 | # define available arguments/parameters a user can pass to the module 21 | argument_spec = viptela_argument_spec() 22 | argument_spec.update(state=dict(type='str', choices=['absent', 'present','query'], default='present'), 23 | device_name = dict(type='str', aliases=['device', 'host-name']), 24 | device_ip = dict(type='str', aliases=['system_ip']), 25 | site_id = dict(type='str'), 26 | uuid = dict(type='str'), 27 | personality=dict(type='str', choices=['vmanage', 'vsmart', 'vbond', 'vedge'], default='vedge'), 28 | template = dict(type='str'), 29 | variables=dict(type='dict', default={}), 30 | wait=dict(type='bool', default=False), 31 | ) 32 | 33 | # seed the result dict in the object 34 | # we primarily care about changed and state 35 | # change is if this module effectively modified the target 36 | # state will include any data that you want your module to pass back 37 | # for consumption, for example, in a subsequent task 38 | result = dict( 39 | changed=False, 40 | ) 41 | 42 | # the AnsibleModule object will be our abstraction working with Ansible 43 | # this includes instantiation, a couple of common attr would be the 44 | # args/params passed to the execution, as well as if the module 45 | # supports check mode 46 | module = AnsibleModule(argument_spec=argument_spec, 47 | supports_check_mode=True, 48 | ) 49 | viptela = viptelaModule(module) 50 | viptela.result['what_changed'] = [] 51 | 52 | if viptela.params['personality'] == 'vedge': 53 | device_type = 'vedges' 54 | else: 55 | device_type = 'controllers' 56 | 57 | if viptela.params['uuid']: 58 | device_data = viptela.get_device_by_uuid(viptela.params['uuid'], type=device_type) 59 | if 'uuid' not in device_data: 60 | viptela.fail_json(msg='Cannot find device with UUID: {0}.'.format(viptela.params['uuid'])) 61 | # If this is a preallocation, we need to set these things. 62 | if 'system-ip' not in device_data or len(device_data['system-ip']) == 0: 63 | if viptela.params['system_ip']: 64 | device_data['system-ip'] = viptela.params['system_ip'] 65 | else: 66 | viptela.fail_json(msg='system_ip is needed when pre-attaching templates') 67 | if 'deviceIP' not in device_data or len(device_data['deviceIP']) == 0: 68 | if viptela.params['system_ip']: 69 | device_data['deviceIP'] = viptela.params['system_ip'] 70 | else: 71 | viptela.fail_json(msg='system_ip is needed when pre-attaching templates') 72 | if 'site-id' not in device_data or len(device_data['site-id']) == 0: 73 | if viptela.params['site_id']: 74 | device_data['site-id'] = viptela.params['site_id'] 75 | else: 76 | viptela.fail_json(msg='site_id is needed when pre-attaching templates') 77 | if 'host-name' not in device_data or len(device_data['host-name']) == 0: 78 | if viptela.params['device_name']: 79 | device_data['host-name'] = viptela.params['device_name'] 80 | else: 81 | viptela.fail_json(msg='device_name is needed when pre-attaching templates') 82 | elif viptela.params['device_name']: 83 | device_status = viptela.get_device_status(viptela.params['device_name'], key='host-name') 84 | if 'uuid' in device_status: 85 | device_type = 'controllers' if device_status['personality'] in ['vmanage', 'vbond', 'vsmart'] else 'vedges' 86 | device_data = viptela.get_device_by_uuid(device_status['uuid'], type=device_type) 87 | else: 88 | viptela.fail_json(msg='Cannot find device with name: {0}.'.format(viptela.params['device'])) 89 | 90 | if viptela.params['state'] == 'present': 91 | if ('system-ip' not in device_data): 92 | viptela.fail_json(msg='system-ip must be defined for {0}.'.format(viptela.params['device'])) 93 | if ('site-id' not in device_data): 94 | viptela.fail_json(msg='site-id must be defined for {0}.'.format(viptela.params['device'])) 95 | 96 | # Get template data and see if it is a real template 97 | device_template_dict = viptela.get_device_template_dict(factory_default=True) 98 | if viptela.params['template']: 99 | if viptela.params['template'] not in device_template_dict: 100 | viptela.fail_json(msg='Template {0} not found.'.format(viptela.params['template'])) 101 | template_data = device_template_dict[viptela.params['template']] 102 | else: 103 | viptela.fail_json(msg='Must specify a template with state present') 104 | 105 | # Make sure they passed in the required variables 106 | # get_template_variables provides a variable name -> property mapping 107 | template_variables = viptela.get_template_variables(device_template_dict[viptela.params['template']]['templateId']) 108 | optional_template_variables = viptela.get_template_optional_variables(device_template_dict[viptela.params['template']]['templateId']) 109 | mandatory_template_variables = {k: template_variables[k] for k in set(template_variables) - set(optional_template_variables)} 110 | if mandatory_template_variables: 111 | if viptela.params['variables']: 112 | for variable in mandatory_template_variables: 113 | if variable not in viptela.params['variables']: 114 | viptela.fail_json(msg='Template {0} requires variables: {1}'.format(viptela.params['template'], ', '.join(template_variables))) 115 | 116 | viptela.result['template_variables'] = template_variables 117 | 118 | # Construct the variable payload 119 | device_template_variables = { 120 | "csv-status": "complete", 121 | "csv-deviceId": device_data['uuid'], 122 | "csv-deviceIP": device_data['deviceIP'], 123 | "csv-host-name": device_data['host-name'], 124 | '//system/host-name': device_data['host-name'], 125 | '//system/system-ip': device_data['system-ip'], 126 | '//system/site-id': device_data['site-id'], 127 | } 128 | 129 | # For each of the variables passed in, match them up with the names of the variables requires in the 130 | # templates and add them with the corresponding property. The the variables is not in template_variables, 131 | # just leave it out since it is not required. 132 | for key, value in viptela.params['variables'].items(): 133 | if key in template_variables: 134 | property = template_variables[key] 135 | device_template_variables[property] = viptela.params['variables'][key] 136 | 137 | # When dealing with optional parameters if we do not have explicitely set a value for it 138 | # we must add the optional parameter to the payload with { key: 'TEMPLATE_IGNORE'} 139 | for key, value in optional_template_variables.items(): 140 | property = template_variables[key] 141 | if property not in device_template_variables: 142 | device_template_variables[property] = 'TEMPLATE_IGNORE' 143 | 144 | attached_uuid_list = viptela.get_template_attachments(template_data['templateId'], key='uuid') 145 | 146 | if device_data['uuid'] in attached_uuid_list: 147 | # Add the template ID to the device's variable payload because we'll need it for comparison and update. 148 | # device_template_variables['csv-templateId'] = template_data['templateId'] 149 | # The device is already attached to the template. We need to see if any of the input changed, so we make 150 | # an API call to get the input on last attach 151 | payload = { 152 | "templateId": template_data['templateId'], 153 | "deviceIds": [device_data['uuid']], 154 | "isEdited": "true", 155 | "isMasterEdited": "false" 156 | } 157 | response = viptela.request('/dataservice/template/device/config/input/', method='POST', payload=payload) 158 | if response.json and 'data' in response.json: 159 | current_variables = response.json['data'][0] 160 | # viptela.result['old'] = current_variables 161 | # viptela.result['new'] = device_template_variables 162 | # Convert both to a string and compare. For some reason, there can be an int/str 163 | # mismatch. It might be indicative of a problem... 164 | for property in device_template_variables: 165 | if str(device_template_variables[property]) != str(current_variables[property]): 166 | viptela.result['changed'] = True 167 | else: 168 | viptela.result['changed'] = True 169 | 170 | if not module.check_mode and viptela.result['changed']: 171 | payload = { 172 | "deviceTemplateList": 173 | [ 174 | { 175 | "templateId": template_data['templateId'], 176 | "device": [device_template_variables], 177 | "isEdited": False, 178 | "isMasterEdited": False 179 | } 180 | ] 181 | } 182 | response = viptela.request('/dataservice/template/device/config/attachfeature', method='POST', payload=payload) 183 | if response.json: 184 | action_id = response.json['id'] 185 | else: 186 | viptela.fail_json(msg='Did not get action ID after attaching device to template.') 187 | elif viptela.params['state'] == 'absent': 188 | if 'templateId' in device_data: 189 | viptela.result['changed'] = True 190 | payload = { 191 | "deviceType": device_data['deviceType'], 192 | "devices":[ 193 | { 194 | "deviceId": device_data['uuid'], 195 | "deviceIP": device_data['deviceIP'] 196 | } 197 | ] 198 | } 199 | if not module.check_mode: 200 | response = viptela.request('/dataservice/template/config/device/mode/cli', method='POST', payload=payload) 201 | if response.json: 202 | action_id = response.json['id'] 203 | else: 204 | viptela.fail_json(msg='Did not get action ID after attaching device to template.') 205 | 206 | elif viptela.params['state'] == 'query': 207 | # Get template data and see if it is a real template 208 | device_template_dict = viptela.get_device_template_dict(factory_default=True) 209 | if viptela.params['template']: 210 | if viptela.params['template'] not in device_template_dict: 211 | viptela.fail_json(msg='Template {0} not found.'.format(viptela.params['template'])) 212 | template_data = device_template_dict[viptela.params['template']] 213 | else: 214 | viptela.fail_json(msg='Must specify a template with state query') 215 | 216 | # get_template_variables provides a variable name -> property mapping 217 | template_variables = viptela.get_template_variables(device_template_dict[viptela.params['template']]['templateId']) 218 | optional_template_variables = viptela.get_template_optional_variables(device_template_dict[viptela.params['template']]['templateId']) 219 | viptela.result['template_variables'] = template_variables 220 | viptela.result['optional_template_variables'] = optional_template_variables 221 | viptela.result['mandatory_template_variables'] = {k: template_variables[k] for k in set(template_variables) - set(optional_template_variables)} 222 | # If told, wait for the status of the request and report it 223 | if viptela.params['wait'] and action_id: 224 | viptela.waitfor_action_completion(action_id) 225 | 226 | viptela.logout() 227 | viptela.exit_json(**viptela.result) 228 | 229 | def main(): 230 | run_module() 231 | 232 | if __name__ == '__main__': 233 | main() 234 | -------------------------------------------------------------------------------- /library/vmanage_device_bootstrap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | 12 | def run_module(): 13 | # define available arguments/parameters a user can pass to the module 14 | argument_spec = viptela_argument_spec() 15 | argument_spec.update(name=dict(type='str'), 16 | device_ip=dict(type='str', alias='deviceIP'), 17 | uuid=dict(type='str'), 18 | model=dict(type='str'), 19 | ) 20 | 21 | # seed the result dict in the object 22 | # we primarily care about changed and state 23 | # change is if this module effectively modified the target 24 | # state will include any data that you want your module to pass back 25 | # for consumption, for example, in a subsequent task 26 | result = dict( 27 | changed=False, 28 | ) 29 | 30 | # the AnsibleModule object will be our abstraction working with Ansible 31 | # this includes instantiation, a couple of common attr would be the 32 | # args/params passed to the execution, as well as if the module 33 | # supports check mode 34 | module = AnsibleModule(argument_spec=argument_spec, 35 | supports_check_mode=True, 36 | ) 37 | viptela = viptelaModule(module) 38 | viptela.result['what_changed'] = [] 39 | viptela.result['bootstrap'] = {} 40 | device = {} 41 | uuid = None 42 | if viptela.params['uuid']: 43 | uuid = viptela.params['uuid'] 44 | device = viptela.get_device_by_uuid(viptela.params['uuid']) 45 | elif viptela.params['device_ip']: 46 | # See if we can find the device by deviceIP 47 | device = viptela.get_device_by_device_ip(viptela.params['device_ip']) 48 | if 'uuid' in device: 49 | uuid = device['uuid'] 50 | elif viptela.params['name']: 51 | device = viptela.get_device_by_name(viptela.params['name']) 52 | if 'uuid' in device: 53 | uuid = device['uuid'] 54 | 55 | if uuid: 56 | if device['vedgeCertificateState'] in ['tokengenerated', 'bootstrapconfiggenerated']: 57 | # We can only generate bootstrap in these two states. Otherwise, the 58 | # device is in-use and cannot be bootstrapped. 59 | viptela.result['what_changed'].append('bootstrap') 60 | if not module.check_mode: 61 | bootstrap = viptela.generate_bootstrap(uuid) 62 | viptela.result['uuid'] = uuid 63 | viptela.result['bootstrap'] = bootstrap 64 | # else: 65 | # viptela.fail_json(msg="Could not generate bootstrap for UUID: {0}. 'vedgeCertificateState' not 'tokengenerated' or 'bootstrapconfiggenerated'".format(uuid)) 66 | elif viptela.params['model']: 67 | # if no uuid was specified, just grab the first free device 68 | device = viptela.get_unused_device(viptela.params['model']) 69 | if not device: 70 | viptela.fail_json(msg="Could not find available device") 71 | viptela.result['what_changed'].append('bootstrap') 72 | if not module.check_mode: 73 | bootstrap = viptela.generate_bootstrap(device['uuid']) 74 | viptela.result['bootstrap'] = bootstrap 75 | else: 76 | viptela.fail_json(msg="Could not find UUID with supplied arguments") 77 | 78 | if viptela.result['what_changed']: 79 | viptela.result['changed'] = True 80 | 81 | viptela.exit_json(**viptela.result) 82 | 83 | 84 | def main(): 85 | run_module() 86 | 87 | 88 | if __name__ == '__main__': 89 | main() 90 | -------------------------------------------------------------------------------- /library/vmanage_device_certificate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | 12 | def run_module(): 13 | # define available arguments/parameters a user can pass to the module 14 | argument_spec = viptela_argument_spec() 15 | argument_spec.update(state=dict(type='str', choices=['csr', 'cert', 'push'], default='cert'), 16 | name=dict(type='str'), 17 | transport_ip=dict(type='str', aliases=['device_ip', 'system_ip']), 18 | cert=dict(type='str', alias='deviceEnterpriseCertificate'), 19 | ) 20 | 21 | # seed the result dict in the object 22 | # we primarily care about changed and state 23 | # change is if this module effectively modified the target 24 | # state will include any data that you want your module to pass back 25 | # for consumption, for example, in a subsequent task 26 | result = dict( 27 | changed=False, 28 | ) 29 | 30 | # the AnsibleModule object will be our abstraction working with Ansible 31 | # this includes instantiation, a couple of common attr would be the 32 | # args/params passed to the execution, as well as if the module 33 | # supports check mode 34 | module = AnsibleModule(argument_spec=argument_spec, 35 | supports_check_mode=True, 36 | ) 37 | viptela = viptelaModule(module) 38 | viptela.result['what_changed'] = [] 39 | 40 | device = {} 41 | # See if we can find the device by deviceIP 42 | if viptela.params['transport_ip']: 43 | device = viptela.get_device_by_device_ip(viptela.params['transport_ip'], type='controllers') 44 | # If we could not find the device by deviceIP, see if we can find it be (host)name 45 | if not device: 46 | device = viptela.get_device_by_name(viptela.params['name'], type='controllers') 47 | 48 | if viptela.params['state'] == 'cert': 49 | if device: 50 | if not viptela.params['cert']: 51 | viptela.fail_json(msg="'cert' is required when added a certificate to a device") 52 | if device: 53 | if ('deviceEnterpriseCertificate' not in device) or (viptela.params['cert'] != device['deviceEnterpriseCertificate']): 54 | viptela.result['what_changed'].append('deviceEnterpriseCertificate') 55 | if not module.check_mode: 56 | viptela.install_device_cert(viptela.params['cert']) 57 | else: 58 | viptela.fail_json(msg="Device must exist to add it's certificate") 59 | else: 60 | viptela.fail_json(msg="Device not found") 61 | elif viptela.params['state'] == 'csr': 62 | if 'deviceCSR' in device: 63 | viptela.result['deviceCSR'] = device['deviceCSR'] 64 | else: 65 | viptela.result['what_changed'].append('csr') 66 | if not module.check_mode: 67 | if 'deviceIP' in device: 68 | viptela.result['deviceCSR'] = viptela.generate_csr(device['deviceIP']) 69 | else: 70 | viptela.fail_json(msg="Cannot find deviceIP for {0}".format(viptela.params['name'])) 71 | elif viptela.params['state'] == 'push': 72 | viptela.push_certificates() 73 | 74 | if viptela.result['what_changed']: 75 | viptela.result['changed'] = True 76 | 77 | viptela.exit_json(**viptela.result) 78 | 79 | 80 | def main(): 81 | run_module() 82 | 83 | 84 | if __name__ == '__main__': 85 | main() 86 | -------------------------------------------------------------------------------- /library/vmanage_device_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(factory_default=dict(type='bool', default=False), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | original_message='', 28 | message='' 29 | ) 30 | 31 | # the AnsibleModule object will be our abstraction working with Ansible 32 | # this includes instantiation, a couple of common attr would be the 33 | # args/params passed to the execution, as well as if the module 34 | # supports check mode 35 | module = AnsibleModule(argument_spec=argument_spec, 36 | supports_check_mode=True, 37 | ) 38 | viptela = viptelaModule(module) 39 | 40 | viptela.result['vedges'] = viptela.get_device_list('vedges') 41 | viptela.result['controllers'] = viptela.get_device_list('controllers') 42 | 43 | viptela.exit_json(**viptela.result) 44 | 45 | def main(): 46 | run_module() 47 | 48 | if __name__ == '__main__': 49 | main() -------------------------------------------------------------------------------- /library/vmanage_device_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 17 | name = dict(type='str', alias='templateName'), 18 | description = dict(type='str', alias='templateDescription'), 19 | templates = dict(type='str', alias='generalTemplates'), 20 | device_type = dict(type='list', alias='deviceType'), 21 | config_type=dict(type='list', alias='configType'), 22 | factory_default=dict(type='bool', alias='factoryDefault'), 23 | aggregate=dict(type='list'), 24 | ) 25 | 26 | # seed the result dict in the object 27 | # we primarily care about changed and state 28 | # change is if this module effectively modified the target 29 | # state will include any data that you want your module to pass back 30 | # for consumption, for example, in a subsequent task 31 | result = dict( 32 | changed=False, 33 | ) 34 | 35 | # the AnsibleModule object will be our abstraction working with Ansible 36 | # this includes instantiation, a couple of common attr would be the 37 | # args/params passed to the execution, as well as if the module 38 | # supports check mode 39 | module = AnsibleModule(argument_spec=argument_spec, 40 | supports_check_mode=True, 41 | ) 42 | viptela = viptelaModule(module) 43 | 44 | # Always as an aggregate... make a list if just given a single entry 45 | if viptela.params['aggregate']: 46 | device_template_list = viptela.params['aggregate'] 47 | else: 48 | if viptela.params['state'] == 'present': 49 | device_template_list = [ 50 | { 51 | 'templateName': viptela.params['name'], 52 | 'templateDescription': viptela.params['description'], 53 | 'deviceType': viptela.params['device_type'], 54 | 'configType': viptela.params['config_type'], 55 | 'factoryDefault': viptela.params['factory_default'], 56 | 'generalTemplates': viptela.params['templates'], 57 | } 58 | ] 59 | else: 60 | device_template_list = [ 61 | { 62 | 'templateName': viptela.params['name'], 63 | 'state': 'absent' 64 | } 65 | ] 66 | 67 | device_template_dict = viptela.get_device_template_dict(factory_default=True, remove_key=False) 68 | 69 | compare_values = ['templateDescription', 'deviceType', 'configType', 'generalTemplates'] 70 | ignore_values = ["lastUpdatedOn", "lastUpdatedBy", "templateId", "createdOn", "createdBy"] 71 | 72 | for device_template in device_template_list: 73 | if viptela.params['state'] == 'present': 74 | payload = { 75 | 'templateName': device_template['templateName'], 76 | 'templateDescription': device_template['templateDescription'], 77 | 'deviceType': device_template['deviceType'], 78 | 'factoryDefault': device_template['factoryDefault'], 79 | 'configType': device_template['configType'], 80 | } 81 | payload['policyId'] = '' 82 | payload['featureTemplateUidRange'] = [] 83 | # If a template by that name is already there 84 | if payload['templateName'] in device_template_dict: 85 | viptela.result['changed'] = False 86 | # changed_items = viptela.compare_payloads(payload, device_template_dict[payload['templateName']], compare_values=compare_values) 87 | # if changed_items: 88 | # viptela.result['changed'] = True 89 | # viptela.result['what_changed'] = changed_items 90 | # viptela.result['old_payload'] = device_template_dict[payload['templateName']] 91 | # if not module.check_mode: 92 | # # 93 | # # Convert template names to template IDs 94 | # # 95 | # if payload['configType'] == 'template': 96 | # payload['generalTemplates'] = viptela.generalTemplates_to_id(device_template['generalTemplates']) 97 | # viptela.request('/dataservice/template/device/feature/{0}'.format(device_template_dict[payload['templateName']]['templateId']), 98 | # method='PUT', payload=payload) 99 | else: 100 | if not module.check_mode: 101 | # 102 | # Convert template names to template IDs 103 | # 104 | if payload['configType'] == 'template': 105 | payload['generalTemplates'] = viptela.generalTemplates_to_id(device_template['generalTemplates']) 106 | viptela.request('/dataservice/template/device/feature', method='POST', payload=payload) 107 | viptela.result['changed'] = True 108 | else: 109 | if device_template['templateName'] in device_template_dict: 110 | if not module.check_mode: 111 | viptela.request('/dataservice/template/device/{0}'.format(device_template_dict[device_template['templateName']]['templateId']), 112 | method='DELETE') 113 | viptela.result['changed'] = True 114 | 115 | 116 | viptela.exit_json(**viptela.result) 117 | 118 | def main(): 119 | run_module() 120 | 121 | if __name__ == '__main__': 122 | main() -------------------------------------------------------------------------------- /library/vmanage_device_template_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(factory_default=dict(type='bool', default=False), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | original_message='', 28 | message='' 29 | ) 30 | 31 | # the AnsibleModule object will be our abstraction working with Ansible 32 | # this includes instantiation, a couple of common attr would be the 33 | # args/params passed to the execution, as well as if the module 34 | # supports check mode 35 | module = AnsibleModule(argument_spec=argument_spec, 36 | supports_check_mode=True, 37 | ) 38 | viptela = viptelaModule(module) 39 | 40 | viptela.result['device_templates'] = viptela.get_device_template_list(factory_default=viptela.params['factory_default']) 41 | 42 | viptela.exit_json(**viptela.result) 43 | 44 | def main(): 45 | run_module() 46 | 47 | if __name__ == '__main__': 48 | main() -------------------------------------------------------------------------------- /library/vmanage_feature_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | from collections import OrderedDict 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 17 | name=dict(type='str', alias='templateName'), 18 | description=dict(type='str', alias='templateDescription'), 19 | definition=dict(type='dict', alias='templateDefinition'), 20 | template_type=dict(type='str', alias='templateType'), 21 | device_type=dict(type='list', alias='deviceType'), 22 | template_min_version=dict(type='str', alias='templateMinVersion'), 23 | factory_default=dict(type='bool', alias='factoryDefault'), 24 | url=dict(type='bool', alias='templateUrl'), 25 | aggregate=dict(type='list'), 26 | ) 27 | 28 | # seed the result dict in the object 29 | # we primarily care about changed and state 30 | # change is if this module effectively modified the target 31 | # state will include any data that you want your module to pass back 32 | # for consumption, for example, in a subsequent task 33 | result = dict( 34 | changed=False, 35 | ) 36 | 37 | # the AnsibleModule object will be our abstraction working with Ansible 38 | # this includes instantiation, a couple of common attr would be the 39 | # args/params passed to the execution, as well as if the module 40 | # supports check mode 41 | module = AnsibleModule(argument_spec=argument_spec, 42 | supports_check_mode=True, 43 | ) 44 | viptela = viptelaModule(module) 45 | 46 | # Always as an aggregate... make a list if just given a single entry 47 | if viptela.params['aggregate']: 48 | feature_template_list = viptela.params['aggregate'] 49 | else: 50 | if viptela.params['state'] == 'present': 51 | try: 52 | 53 | feature_template_list = [ 54 | { 55 | 'templateName': viptela.params['name'], 56 | 'templateDescription': viptela.params['description'], 57 | 'deviceType': viptela.params['device_type'], 58 | 'templateDefinition': viptela.params['definition'], 59 | 'templateType': viptela.params['template_type'], 60 | 'templateMinVersion': viptela.params['template_min_version'], 61 | 'factoryDefault': viptela.params['factory_default'] 62 | } 63 | ] 64 | except: 65 | module.fail_json( 66 | msg="Required values: name, description, device_type, definition, template_type, template_min_version, factory_default") 67 | else: 68 | try: 69 | feature_template_list = [ 70 | { 71 | 'templateName': viptela.params['name'] 72 | } 73 | ] 74 | except: 75 | module.fail_json( 76 | msg='Required values: name' 77 | ) 78 | 79 | feature_template_dict = viptela.get_feature_template_dict(factory_default=True, remove_key=False) 80 | 81 | ignore_values = ["lastUpdatedOn", "lastUpdatedBy", "templateId", "createdOn", "createdBy"] 82 | compare_values = ['templateDescription', 'deviceType', 'templateType', 'templateDefinition', 'templateMinVersion'] 83 | 84 | for feature_template in feature_template_list: 85 | if viptela.params['state'] == 'present': 86 | payload = { 87 | 'templateName': feature_template['templateName'], 88 | 'templateDescription': feature_template['templateDescription'], 89 | 'deviceType': feature_template['deviceType'], 90 | 'templateType': feature_template['templateType'], 91 | 'templateMinVersion': feature_template['templateMinVersion'], 92 | 'factoryDefault': feature_template['factoryDefault'], 93 | 'templateDefinition': feature_template['templateDefinition'] 94 | } 95 | # FIXME (Issue #1): This is a temporary workaround for the fact that vManage requires it payload in a specific order 96 | template_definition = OrderedDict() 97 | if 'if-name' in feature_template['templateDefinition']: 98 | template_definition['if-name'] = feature_template['templateDefinition'].pop('if-name') 99 | if 'vpn-id' in feature_template['templateDefinition']: 100 | template_definition['vpn-id'] = feature_template['templateDefinition'].pop('vpn-id') 101 | for key, value in feature_template['templateDefinition'].items(): 102 | template_definition[key] = value 103 | payload['templateDefinition'] = template_definition 104 | if payload['templateName'] in feature_template_dict: 105 | viptela.result['changed'] = False 106 | # changed_items = viptela.compare_payloads(payload, feature_template_dict[payload['templateName']], compare_values=compare_values) 107 | # if changed_items: 108 | # viptela.result['changed'] = True 109 | # viptela.result['what_changed'] = changed_items 110 | # if not module.check_mode: 111 | # viptela.request('/dataservice/template/feature/{0}'.format(feature_template_dict[payload['templateName']]['templateId']), 112 | # method='PUT', payload=payload) 113 | else: 114 | if not module.check_mode: 115 | viptela.request('/dataservice/template/feature/', method='POST', payload=payload) 116 | viptela.result['changed'] = True 117 | else: 118 | if feature_template['templateName'] in feature_template_dict: 119 | if not module.check_mode: 120 | viptela.request('/dataservice/template/feature/{0}'.format( 121 | feature_template_dict[feature_template['templateName']]['templateId']), 122 | method='DELETE') 123 | viptela.result['changed'] = True 124 | 125 | viptela.exit_json(**viptela.result) 126 | 127 | 128 | def main(): 129 | run_module() 130 | 131 | 132 | if __name__ == '__main__': 133 | main() 134 | -------------------------------------------------------------------------------- /library/vmanage_feature_template_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(factory_default=dict(type='bool', default=False), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | original_message='', 28 | message='' 29 | ) 30 | 31 | # the AnsibleModule object will be our abstraction working with Ansible 32 | # this includes instantiation, a couple of common attr would be the 33 | # args/params passed to the execution, as well as if the module 34 | # supports check mode 35 | module = AnsibleModule(argument_spec=argument_spec, 36 | supports_check_mode=True, 37 | ) 38 | viptela = viptelaModule(module) 39 | 40 | viptela.result['feature_templates'] = viptela.get_feature_template_list(factory_default=viptela.params['factory_default']) 41 | 42 | viptela.exit_json(**viptela.result) 43 | 44 | def main(): 45 | run_module() 46 | 47 | if __name__ == '__main__': 48 | main() -------------------------------------------------------------------------------- /library/vmanage_fileupload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | import os.path 11 | # from requests.auth import HTTPBasicAuth 12 | # from paramiko import SSHClient 13 | # from scp import SCPClient 14 | from ansible.module_utils.basic import AnsibleModule, json 15 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 16 | 17 | 18 | def run_module(): 19 | # define available arguments/parameters a user can pass to the module 20 | argument_spec = viptela_argument_spec() 21 | argument_spec.update(file=dict(type='str', required=True), 22 | ) 23 | 24 | # seed the result dict in the object 25 | # we primarily care about changed and state 26 | # change is if this module effectively modified the target 27 | # state will include any data that you want your module to pass back 28 | # for consumption, for example, in a subsequent task 29 | result = dict( 30 | changed=False, 31 | original_message='', 32 | message='' 33 | ) 34 | 35 | # the AnsibleModule object will be our abstraction working with Ansible 36 | # this includes instantiation, a couple of common attr would be the 37 | # args/params passed to the execution, as well as if the module 38 | # supports check mode 39 | module = AnsibleModule(argument_spec=argument_spec, 40 | supports_check_mode=True, 41 | ) 42 | viptela = viptelaModule(module) 43 | 44 | if not module.check_mode: 45 | response = viptela.request('/dataservice/system/device/fileupload', method='POST', 46 | files={'file': open(module.params['file'], 'rb')}, 47 | data={'validity':'valid', 'upload':'true'}, 48 | headers=None) 49 | 50 | viptela.result['msg'] = response.json['vedgeListUploadStatus'] 51 | if 'successfully' in response.json['vedgeListUploadStatus']: 52 | viptela.result['changed'] = True 53 | 54 | viptela.exit_json() 55 | 56 | def main(): 57 | run_module() 58 | 59 | if __name__ == '__main__': 60 | main() -------------------------------------------------------------------------------- /library/vmanage_nping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(vedge = dict(type='str', required=True), 17 | dst_ip=dict(type='str', required=True, alias='host'), 18 | vpn=dict(type='int', default=0, required=False), 19 | src_interface=dict(type='str', required=False, alias='source'), 20 | probe_type=dict(type='str', required=False, default='icmp', alias='probeType'), 21 | count=dict(type='str', required=False), 22 | size=dict(type='str', required=False), 23 | df = dict(type='str', required=False), 24 | rapid = dict(type='bool', required=False), 25 | ) 26 | 27 | # seed the result dict in the object 28 | # we primarily care about changed and state 29 | # change is if this module effectively modified the target 30 | # state will include any data that you want your module to pass back 31 | # for consumption, for example, in a subsequent task 32 | result = dict( 33 | changed=False, 34 | ) 35 | 36 | # the AnsibleModule object will be our abstraction working with Ansible 37 | # this includes instantiation, a couple of common attr would be the 38 | # args/params passed to the execution, as well as if the module 39 | # supports check mode 40 | module = AnsibleModule(argument_spec=argument_spec, 41 | supports_check_mode=True, 42 | ) 43 | viptela = viptelaModule(module) 44 | device_dict = viptela.get_device_dict('vedge') 45 | 46 | if viptela.params['vedge'] in device_dict: 47 | system_ip = device_dict[viptela.params['vedge']]['system-ip'] 48 | else: 49 | viptela.fail_json(msg="Cannot find vedge {0}".format(viptela.params['vedge'])) 50 | 51 | payload = { 52 | 'host': viptela.params['dst_ip'], 53 | 'vpn': str(viptela.params['vpn']), 54 | 'probeType': viptela.params['probe_type'], 55 | } 56 | if viptela.params['src_interface']: 57 | payload['source'] = viptela.params['src_interface'] 58 | if viptela.params['count']: 59 | payload['count'] = viptela.params['count'] 60 | if viptela.params['size']: 61 | payload['size'] = viptela.params['size'] 62 | if viptela.params['df']: 63 | payload['df'] = viptela.params['df'] 64 | if viptela.params['rapid']: 65 | payload['rapid'] = "true" if viptela.params['rapid'] else "false" 66 | 67 | response = viptela.request('/dataservice/device/tools/nping/{0}'.format(system_ip), method='POST', payload=payload) 68 | if response.json: 69 | viptela.result['json'] = response.json 70 | 71 | viptela.exit_json(**viptela.result) 72 | 73 | def main(): 74 | run_module() 75 | 76 | if __name__ == '__main__': 77 | main() 78 | 79 | 80 | 81 | # https://192.133.178.190:8443/dataservice/device/tools/nping/10.255.210.1 82 | # {"host": "10.23.3.10", "vpn": "0", "source": "ge0/0", "probeType": "icmp"} 83 | #{"host":"10.22.3.10","vpn":"10","count":"5","size":"1500","probeType":"icmp","df":"true"} -------------------------------------------------------------------------------- /library/vmanage_policy_definition.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import os 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | # we only support the 'data' type currently, it will be fixed when we migrate to the SDK 17 | argument_spec = viptela_argument_spec() 18 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 19 | aggregate=dict(type='list'), 20 | name=dict(type='str'), 21 | description = dict(type = 'str'), 22 | type = dict(type ='str', required = True, choices= ['data']), 23 | sequences = dict(type ='list'), 24 | default_action = dict(type ='dict', alias='defaultAction'), 25 | ) 26 | 27 | # seed the result dict in the object 28 | # we primarily care about changed and state 29 | # change is if this module effectively modified the target 30 | # state will include any data that you want your module to pass back 31 | # for consumption, for example, in a subsequent task 32 | result = dict( 33 | changed=False, 34 | ) 35 | 36 | # the AnsibleModule object will be our abstraction working with Ansible 37 | # this includes instantiation, a couple of common attr would be the 38 | # args/params passed to the execution, as well as if the module 39 | # supports check mode 40 | module = AnsibleModule(argument_spec=argument_spec, 41 | supports_check_mode=True, 42 | ) 43 | viptela = viptelaModule(module) 44 | 45 | # Always as an aggregate... make a list if just given a single entry 46 | if viptela.params['aggregate']: 47 | definition_list = viptela.params['aggregate'] 48 | else: 49 | definition_list = [ 50 | { 51 | "name": viptela.params['name'], 52 | "description": viptela.params['description'], 53 | "type": viptela.params['type'], 54 | "sequences": viptela.params['sequences'], 55 | "defaultAction": viptela.params['default_action'] 56 | } 57 | ] 58 | 59 | policy_definition_dict = viptela.get_policy_definition_dict(viptela.params['type'], remove_key=False) 60 | 61 | compare_values = ["name", "description", "type", "sequences", "defaultAction"] 62 | 63 | # Import site lists 64 | for list in definition_list: 65 | payload = { 66 | "name": list['name'], 67 | "description": list['description'], 68 | "type": list['type'], 69 | "sequences": list['sequences'], 70 | "defaultAction": list['defaultAction'] 71 | } 72 | if viptela.params['state'] == 'present': 73 | if list['name'] in policy_definition_dict: 74 | changed_items = viptela.compare_payloads(list, policy_definition_dict[list['name']], compare_values=compare_values) 75 | if changed_items: 76 | viptela.result['changed'] = True 77 | viptela.result['what_changed'] = changed_items 78 | payload['sequences'] = viptela.convert_sequences_to_id(list['sequences']) 79 | if not module.check_mode: 80 | viptela.request('/dataservice/template/policy/definition/{0}/{1}'.format(list['type'], policy_definition_dict[list['name']]['definitionId']), 81 | method='PUT', payload=payload) 82 | else: 83 | payload['sequences'] = viptela.convert_sequences_to_id(list['sequences']) 84 | if not module.check_mode: 85 | viptela.request('/dataservice/template/policy/definition/{0}/'.format(list['type']), 86 | method='POST', payload=payload) 87 | viptela.result['changed'] = True 88 | else: 89 | if list['name'] in policy_definition_dict: 90 | if not module.check_mode: 91 | viptela.request('/dataservice/template/policy/definition/{0}/{1}'.format(list['type'], list['definitionId']), 92 | method='DELETE') 93 | viptela.result['changed'] = True 94 | 95 | viptela.exit_json(**viptela.result) 96 | 97 | def main(): 98 | run_module() 99 | 100 | if __name__ == '__main__': 101 | main() -------------------------------------------------------------------------------- /library/vmanage_policy_definition_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | 18 | # seed the result dict in the object 19 | # we primarily care about changed and state 20 | # change is if this module effectively modified the target 21 | # state will include any data that you want your module to pass back 22 | # for consumption, for example, in a subsequent task 23 | result = dict( 24 | changed=False, 25 | ) 26 | 27 | # the AnsibleModule object will be our abstraction working with Ansible 28 | # this includes instantiation, a couple of common attr would be the 29 | # args/params passed to the execution, as well as if the module 30 | # supports check mode 31 | module = AnsibleModule(argument_spec=argument_spec, 32 | supports_check_mode=True, 33 | ) 34 | viptela = viptelaModule(module) 35 | 36 | # vSmart policies 37 | # response = viptela.request('/dataservice/template/policy/vsmart') 38 | # response_json = response.json() 39 | # vsmart_policies = response_json['data'] 40 | policy_definitions = [] 41 | policy_list_dict = viptela.get_policy_list_dict('all', key_name='listId') 42 | for list_type in viptela.POLICY_DEFINITION_TYPES: 43 | definition_list = viptela.get_policy_definition_list(list_type) 44 | for definition in definition_list: 45 | definition_detail = viptela.get_policy_definition(list_type, definition['definitionId']) 46 | for sequence in definition_detail['sequences']: 47 | for entry in sequence['match']['entries']: 48 | entry['listName'] = policy_list_dict[entry['ref']]['name'] 49 | entry['listType'] = policy_list_dict[entry['ref']]['type'] 50 | policy_definitions.append(definition_detail) 51 | 52 | viptela.result['policy_definitions'] = policy_definitions 53 | 54 | viptela.exit_json(**viptela.result) 55 | 56 | def main(): 57 | run_module() 58 | 59 | if __name__ == '__main__': 60 | main() -------------------------------------------------------------------------------- /library/vmanage_policy_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import os 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 18 | aggregate=dict(type='list'), 19 | name=dict(type='str'), 20 | description = dict(type = 'str'), 21 | type = dict(type ='str', required = False, choices= ['all', 'color', 'vpn', 'site', 'app', 22 | 'dataprefix', 'prefix', 'aspath', 'class', 'community', 'extcommunity', 'mirror', 'tloc', 23 | 'sla', 'policer', 'ipprefixall', 'dataprefixall'], default='all'), 24 | entries = dict(type ='list'), 25 | push=dict(type='bool', default=False), 26 | force=dict(type='bool', default=False) 27 | ) 28 | 29 | # seed the result dict in the object 30 | # we primarily care about changed and state 31 | # change is if this module effectively modified the target 32 | # state will include any data that you want your module to pass back 33 | # for consumption, for example, in a subsequent task 34 | result = dict( 35 | changed=False, 36 | ) 37 | 38 | # the AnsibleModule object will be our abstraction working with Ansible 39 | # this includes instantiation, a couple of common attr would be the 40 | # args/params passed to the execution, as well as if the module 41 | # supports check mode 42 | module = AnsibleModule(argument_spec=argument_spec, 43 | supports_check_mode=True, 44 | ) 45 | viptela = viptelaModule(module) 46 | 47 | # Always as an aggregate... make a list if just given a single entry 48 | if viptela.params['aggregate']: 49 | policy_list = viptela.params['aggregate'] 50 | else: 51 | policy_list = [ 52 | { 53 | "name": viptela.params['name'], 54 | "description": viptela.params['description'], 55 | "type": viptela.params['type'], 56 | "entries": viptela.params['entries'], 57 | } 58 | ] 59 | 60 | policy_list_dict = viptela.get_policy_list_dict(viptela.params['type'], remove_key=False) 61 | 62 | compare_values = ["name", "description", "type", "entries"] 63 | 64 | # Import site lists 65 | for list in policy_list: 66 | if viptela.params['state'] == 'present': 67 | if list['name'] in policy_list_dict: 68 | # FIXME Just compare the entries for now. 69 | if (list['entries'] != policy_list_dict[list['name']]['entries']) or viptela.params['force']: 70 | list['listId'] = policy_list_dict[list['name']]['listId'] 71 | viptela.result['new_entries'] = list['entries'] 72 | viptela.result['existing_entries'] = policy_list_dict[list['name']]['entries'] 73 | # If description is not specified, try to get it from the existing information 74 | if not list['description']: 75 | list['description'] = policy_list_dict[list['name']]['description'] 76 | viptela.result['changed'] = True 77 | if not module.check_mode: 78 | viptela.result['put_payload'] = list 79 | response = viptela.request('/dataservice/template/policy/list/{0}/{1}'.format(list['type'].lower(), list['listId']), 80 | method='PUT', payload=list) 81 | viptela.result['response'] = response.json 82 | if response.json: 83 | # Updating the policy list returns a `processId` that locks the list and 'masterTemplatesAffected' 84 | # that lists the templates affected by the change. 85 | if 'processId' in response.json: 86 | process_id = response.json['processId'] 87 | viptela.result['put_payload'] = response.json['processId'] 88 | if viptela.params['push']: 89 | # If told to push out the change, we need to reattach each template affected by the change 90 | for template_id in response.json['masterTemplatesAffected']: 91 | action_id = viptela.reattach_device_template(template_id, process_id=process_id) 92 | 93 | # Delete the lock on the policy list 94 | # FIXME: The list does not seem to update when we unlock too soon, so I think that we need 95 | # to wait for the attachment, but need to understand this better. 96 | # response = viptela.request('/dataservice/template/lock/{0}'.format(process_id), method='DELETE') 97 | # viptela.result['lock_response'] = response.json 98 | else: 99 | if viptela.params['push']: 100 | viptela.fail_json(msg="Cannot push changes: Did not get a process id when updating policy list") 101 | else: 102 | if not module.check_mode: 103 | viptela.request('/dataservice/template/policy/list/{0}/'.format(list['type'].lower()), 104 | method='POST', payload=list) 105 | viptela.result['changed'] = True 106 | else: 107 | if list['name'] in policy_list_dict: 108 | if not module.check_mode: 109 | viptela.request('/dataservice/template/policy/list/{0}/{1}'.format(list['type'].lower(), list['listId']), 110 | method='DELETE') 111 | viptela.result['changed'] = True 112 | 113 | viptela.logout() 114 | viptela.exit_json(**viptela.result) 115 | 116 | def main(): 117 | run_module() 118 | 119 | if __name__ == '__main__': 120 | main() 121 | 122 | # https://192.133.178.76:8443/dataservice/template/lock/push_feature_template_configuration-2e133445-ae15-49ab-b74a-c1fe65e263b6 -------------------------------------------------------------------------------- /library/vmanage_policy_list_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | 18 | # seed the result dict in the object 19 | # we primarily care about changed and state 20 | # change is if this module effectively modified the target 21 | # state will include any data that you want your module to pass back 22 | # for consumption, for example, in a subsequent task 23 | result = dict( 24 | changed=False, 25 | ) 26 | 27 | # the AnsibleModule object will be our abstraction working with Ansible 28 | # this includes instantiation, a couple of common attr would be the 29 | # args/params passed to the execution, as well as if the module 30 | # supports check mode 31 | module = AnsibleModule(argument_spec=argument_spec, 32 | supports_check_mode=True, 33 | ) 34 | viptela = viptelaModule(module) 35 | 36 | # vSmart policies 37 | # response = viptela.request('/dataservice/template/policy/vsmart') 38 | # response_json = response.json() 39 | # vsmart_policies = response_json['data'] 40 | policy_lists = {} 41 | # for list_type in viptela.POLICY_LIST_TYPES: 42 | # list = viptela.get_policy_list_list(list_type) 43 | # policy_lists[list_type] = list 44 | policy_lists = viptela.get_policy_list_list('all') 45 | 46 | 47 | # # Prefix lists 48 | # prefix_lists = viptela.get_policy_list_list('prefix') 49 | # 50 | # # VPN lists 51 | # vpn_lists = viptela.get_policy_list_list('vpn') 52 | # 53 | # policy_lists = { 54 | # # 'vsmart_policies': vsmart_policies, 55 | # 'vmanage_site_lists': site_lists, 56 | # 'vmanage_prefix_lists': prefix_lists, 57 | # 'vmanage_vpn_lists': vpn_lists, 58 | # } 59 | 60 | viptela.result['policy_lists'] = policy_lists 61 | 62 | viptela.exit_json(**viptela.result) 63 | 64 | def main(): 65 | run_module() 66 | 67 | if __name__ == '__main__': 68 | main() -------------------------------------------------------------------------------- /library/vmanage_settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | from collections import OrderedDict 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(organization=dict(type='str'), 17 | vbond=dict(type='str'), 18 | vbond_port=dict(type='str', default='12346'), 19 | root_cert=dict(type='str'), 20 | ca_type=dict(type='str') 21 | ) 22 | 23 | # seed the result dict in the object 24 | # we primarily care about changed and state 25 | # change is if this module effectively modified the target 26 | # state will include any data that you want your module to pass back 27 | # for consumption, for example, in a subsequent task 28 | result = dict( 29 | changed=False, 30 | ) 31 | 32 | # the AnsibleModule object will be our abstraction working with Ansible 33 | # this includes instantiation, a couple of common attr would be the 34 | # args/params passed to the execution, as well as if the module 35 | # supports check mode 36 | module = AnsibleModule(argument_spec=argument_spec, 37 | supports_check_mode=True, 38 | ) 39 | viptela = viptelaModule(module) 40 | viptela.result['what_changed'] = [] 41 | 42 | if viptela.params['organization']: 43 | current = viptela.get_vmanage_org() 44 | if viptela.params['organization'] != current: 45 | viptela.result['what_changed'].append('organization') 46 | if not module.check_mode: 47 | viptela.set_vmanage_org(viptela.params['organization']) 48 | 49 | if viptela.params['vbond']: 50 | current = viptela.get_vmanage_vbond() 51 | if viptela.params['vbond'] != current['vbond'] or viptela.params['vbond_port'] != current['vbond_port']: 52 | viptela.result['what_changed'].append('vbond') 53 | if not module.check_mode: 54 | viptela.set_vmanage_vbond(viptela.params['vbond'], viptela.params['vbond_port']) 55 | 56 | if viptela.params['ca_type']: 57 | current = viptela.get_vmanage_ca_type() 58 | if viptela.params['ca_type'] != current: 59 | viptela.result['what_changed'].append('ca_type') 60 | if not module.check_mode: 61 | viptela.set_vmanage_ca_type(viptela.params['ca_type']) 62 | 63 | if viptela.params['root_cert']: 64 | current = viptela.get_vmanage_root_cert() 65 | if viptela.params['root_cert'] not in current: 66 | viptela.result['what_changed'].append('root_cert') 67 | if not module.check_mode: 68 | viptela.set_vmanage_root_cert(viptela.params['root_cert']) 69 | 70 | if viptela.result['what_changed']: 71 | viptela.result['changed'] = True 72 | 73 | viptela.exit_json(**viptela.result) 74 | 75 | 76 | def main(): 77 | run_module() 78 | 79 | 80 | if __name__ == '__main__': 81 | main() 82 | -------------------------------------------------------------------------------- /library/vmanage_software_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(factory_default=dict(type='bool', default=False), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | original_message='', 28 | message='' 29 | ) 30 | 31 | # the AnsibleModule object will be our abstraction working with Ansible 32 | # this includes instantiation, a couple of common attr would be the 33 | # args/params passed to the execution, as well as if the module 34 | # supports check mode 35 | module = AnsibleModule(argument_spec=argument_spec, 36 | supports_check_mode=True, 37 | ) 38 | viptela = viptelaModule(module) 39 | 40 | viptela.result['software_images'] = viptela.get_software_images_list() 41 | viptela.result['vedges'] = viptela.get_installed_software(type='vedge') 42 | viptela.result['controller'] = viptela.get_installed_software(type='controller') 43 | viptela.result['vmanage'] = viptela.get_installed_software(type='vmanage') 44 | 45 | viptela.exit_json(**viptela.result) 46 | 47 | def main(): 48 | run_module() 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /library/vmanage_software_upgrade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | from collections import OrderedDict 12 | 13 | def run_module(): 14 | # define available arguments/parameters a user can pass to the module 15 | argument_spec = viptela_argument_spec() 16 | argument_spec.update(devices=dict(type='list'), 17 | deviceType=dict(type='str', choices=['controller', 'vedge'], default='vedge'), 18 | version=dict(type='str'), 19 | activate=dict(type='bool'), 20 | set_default=dict(type='bool') 21 | ) 22 | 23 | # seed the result dict in the object 24 | # we primarily care about changed and state 25 | # change is if this module effectively modified the target 26 | # state will include any data that you want your module to pass back 27 | # for consumption, for example, in a subsequent task 28 | result = dict( 29 | changed=False, 30 | ) 31 | 32 | # the AnsibleModule object will be our abstraction working with Ansible 33 | # this includes instantiation, a couple of common attr would be the 34 | # args/params passed to the execution, as well as if the module 35 | # supports check mode 36 | module = AnsibleModule(argument_spec=argument_spec, 37 | supports_check_mode=True, 38 | ) 39 | viptela = viptelaModule(module) 40 | 41 | # It seems to me that the software operations on the vManage are already idempotent 42 | # This is why at least in this phase we are not implementing other idempotency checks 43 | # In future it might make sense to implement them to avoid generating useless tasks 44 | # on the vManage. This would streamline operations. 45 | # TODO Add idempotency cheks to this module 46 | 47 | devices = module.params['devices'] 48 | deviceType = module.params['deviceType'] 49 | version = module.params['version'] 50 | data = [ 51 | { 52 | "family":"vedge-x86", 53 | "version": version 54 | } 55 | ] 56 | reboot = module.params['activate'] 57 | set_default = module.params['set_default'] 58 | 59 | # Verify if the target software (version) is available in the vManage and set flag 60 | vManage_software_list = viptela.get_software_images_list() 61 | software_present_on_vManage = False 62 | for software in vManage_software_list: 63 | if version == software['versionName']: 64 | software_present_on_vManage = True 65 | 66 | # If we have found the software we can move on and perform the operation 67 | if software_present_on_vManage: 68 | viptela.software_install(devices,deviceType,data,reboot) 69 | 70 | if set_default: 71 | for device in devices: 72 | device.update({'version': version}) 73 | viptela.set_default_partition(devices,deviceType) 74 | 75 | # If not, we fail 76 | else: 77 | module.fail_json( 78 | msg="We don't have this software, I am sorry") 79 | 80 | viptela.exit_json(**viptela.result) 81 | 82 | 83 | def main(): 84 | run_module() 85 | 86 | 87 | if __name__ == '__main__': 88 | main() -------------------------------------------------------------------------------- /library/vmanage_software_upload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | #### CAN WE DO THIS ???? 9 | import os 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | from collections import OrderedDict 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(state=dict(type='str', choices=['absent', 'present'], default='present'), 18 | file=dict(type='str'), 19 | aggregate=dict(type='list') 20 | ) 21 | 22 | # seed the result dict in the object 23 | # we primarily care about changed and state 24 | # change is if this module effectively modified the target 25 | # state will include any data that you want your module to pass back 26 | # for consumption, for example, in a subsequent task 27 | result = dict( 28 | changed=False, 29 | ) 30 | 31 | # the AnsibleModule object will be our abstraction working with Ansible 32 | # this includes instantiation, a couple of common attr would be the 33 | # args/params passed to the execution, as well as if the module 34 | # supports check mode 35 | module = AnsibleModule(argument_spec=argument_spec, 36 | supports_check_mode=True, 37 | ) 38 | viptela = viptelaModule(module) 39 | 40 | 41 | if viptela.params['aggregate']: 42 | upload_software_list = viptela.params['aggregate'] 43 | else: 44 | upload_software_list = [ 45 | { 46 | 'file': module.params['file'] 47 | } 48 | ] 49 | 50 | 51 | # THIS MODULE IS DESIGNED TO UPLOAD UPGRADE IMAGES TO THE VMANAGE 52 | # Software in SD-WAN varies depending on what you want to upgrade. 53 | # This is a complication for what concern idempotency of this module 54 | # Files to upgrade vmanage will look like: vmanage-XX.YY.ZZ-.tar.gz 55 | # Files to upgrade vedge cloud/vedge 5k/vbond/vsmart will look like: viptela-XX.YY.ZZ-.tar.gz 56 | # Physical appliances will NOT have incremental upgrade images 57 | # CISCO Physical appliances will be upgraded via a new .bin file 58 | # VIPTELA Physical appliances will be upgraded via a new .tar.gz file 59 | 60 | viptela.result['changed'] = False 61 | 62 | vManage_software_list = viptela.get_software_images_list() 63 | 64 | if viptela.params['state'] == 'present': 65 | 66 | for software_to_upload in upload_software_list: 67 | 68 | try: 69 | present = False 70 | path_software_to_be_uploaded = software_to_upload['file'] 71 | 72 | if not os.path.exists(path_software_to_be_uploaded): 73 | module.fail_json( 74 | msg="File does not exists") 75 | 76 | filename_software_to_be_uploaded = os.path.basename(path_software_to_be_uploaded) 77 | 78 | for software in vManage_software_list: 79 | availabe_files_list = software["availableFiles"].split(', ') 80 | if filename_software_to_be_uploaded in availabe_files_list: 81 | present = True 82 | 83 | 84 | if not module.check_mode and not present: 85 | response = viptela.request('/dataservice/device/action/software/package', method='POST', 86 | files={'file': open(path_software_to_be_uploaded, 'rb')}, 87 | data={'validity':'valid', 'upload':'true'}, 88 | headers=None) 89 | 90 | viptela.result['changed'] = True 91 | except Exception as e: 92 | module.fail_json( 93 | msg="General Error {0}".format(e)) 94 | 95 | else: 96 | # absent to be added 97 | pass 98 | 99 | viptela.exit_json(**viptela.result) 100 | 101 | 102 | def main(): 103 | run_module() 104 | 105 | 106 | if __name__ == '__main__': 107 | main() -------------------------------------------------------------------------------- /library/vmanage_template_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import requests 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(file = dict(type='str', required=True), 18 | factory_default=dict(type='str', default=False), 19 | ) 20 | 21 | # seed the result dict in the object 22 | # we primarily care about changed and state 23 | # change is if this module effectively modified the target 24 | # state will include any data that you want your module to pass back 25 | # for consumption, for example, in a subsequent task 26 | result = dict( 27 | changed=False, 28 | ) 29 | 30 | # the AnsibleModule object will be our abstraction working with Ansible 31 | # this includes instantiation, a couple of common attr would be the 32 | # args/params passed to the execution, as well as if the module 33 | # supports check mode 34 | module = AnsibleModule(argument_spec=argument_spec, 35 | supports_check_mode=True, 36 | ) 37 | viptela = viptelaModule(module) 38 | 39 | feature_template_export = viptela.get_feature_template_list(factory_default=viptela.params['factory_default']) 40 | device_template_export = viptela.get_device_template_list(factory_default=viptela.params['factory_default']) 41 | 42 | template_export = { 43 | 'feature_templates': feature_template_export, 44 | 'device_templates': device_template_export 45 | } 46 | 47 | if not module.check_mode: 48 | with open(viptela.params['file'], 'w') as f: 49 | json.dump(template_export, f, indent=4, sort_keys=True) 50 | 51 | viptela.exit_json(**viptela.result) 52 | 53 | def main(): 54 | run_module() 55 | 56 | if __name__ == '__main__': 57 | main() -------------------------------------------------------------------------------- /library/vmanage_template_facts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | from ansible.module_utils.basic import AnsibleModule, json 10 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 11 | 12 | def run_module(): 13 | # define available arguments/parameters a user can pass to the module 14 | argument_spec = viptela_argument_spec() 15 | argument_spec.update(factory_default=dict(type='bool', default=False), 16 | ) 17 | 18 | # seed the result dict in the object 19 | # we primarily care about changed and state 20 | # change is if this module effectively modified the target 21 | # state will include any data that you want your module to pass back 22 | # for consumption, for example, in a subsequent task 23 | result = dict( 24 | changed=False, 25 | original_message='', 26 | message='' 27 | ) 28 | 29 | # the AnsibleModule object will be our abstraction working with Ansible 30 | # this includes instantiation, a couple of common attr would be the 31 | # args/params passed to the execution, as well as if the module 32 | # supports check mode 33 | module = AnsibleModule(argument_spec=argument_spec, 34 | supports_check_mode=True, 35 | ) 36 | viptela = viptelaModule(module) 37 | 38 | # if the user is working with this module in only check mode we do not 39 | # want to make any changes to the environment, just return the current 40 | # state with no modifications 41 | if module.check_mode: 42 | return result 43 | 44 | feature_templates = viptela.get_feature_template_list(factory_default=viptela.params['factory_default']) 45 | device_templates = viptela.get_device_template_list(factory_default=viptela.params['factory_default']) 46 | 47 | templates = { 48 | "feature_templates": feature_templates, 49 | "device_templates": device_templates, 50 | } 51 | 52 | viptela.logout() 53 | 54 | viptela.result['templates'] = templates 55 | viptela.exit_json(**viptela.result) 56 | 57 | def main(): 58 | run_module() 59 | 60 | if __name__ == '__main__': 61 | main() -------------------------------------------------------------------------------- /library/vmanage_template_import.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ANSIBLE_METADATA = { 4 | 'metadata_version': '1.1', 5 | 'status': ['preview'], 6 | 'supported_by': 'community' 7 | } 8 | 9 | import os 10 | from ansible.module_utils.basic import AnsibleModule, json 11 | from ansible.module_utils.viptela import viptelaModule, viptela_argument_spec 12 | 13 | 14 | def run_module(): 15 | # define available arguments/parameters a user can pass to the module 16 | argument_spec = viptela_argument_spec() 17 | argument_spec.update(file = dict(type='str', required=True), 18 | ) 19 | 20 | # seed the result dict in the object 21 | # we primarily care about changed and state 22 | # change is if this module effectively modified the target 23 | # state will include any data that you want your module to pass back 24 | # for consumption, for example, in a subsequent task 25 | result = dict( 26 | changed=False, 27 | ) 28 | 29 | # the AnsibleModule object will be our abstraction working with Ansible 30 | # this includes instantiation, a couple of common attr would be the 31 | # args/params passed to the execution, as well as if the module 32 | # supports check mode 33 | module = AnsibleModule(argument_spec=argument_spec, 34 | supports_check_mode=True, 35 | ) 36 | viptela = viptelaModule(module) 37 | 38 | # Read in the datafile 39 | if not os.path.exists(viptela.params['file']): 40 | module.fail_json(msg='Cannot find file {0}'.format(viptela.params['file'])) 41 | with open(viptela.params['file']) as f: 42 | template_data = json.load(f) 43 | 44 | # Separate the feature template data from the device template data 45 | feature_template_data = template_data['feature_templates'] 46 | device_template_data = template_data['device_templates'] 47 | 48 | # Process the feature templates 49 | feature_templates = viptela.get_feature_template_dict(factory_default=True) 50 | for data in feature_template_data: 51 | if data['templateName'] not in feature_templates: 52 | payload = { 53 | 'templateName': data['templateName'], 54 | 'templateDescription': data['templateDescription'], 55 | 'deviceType': data['deviceType'], 56 | 'templateDefinition': data['templateDefinition'], 57 | 'templateType': data['templateType'], 58 | 'templateMinVersion': data['templateMinVersion'], 59 | 'factoryDefault': data['factoryDefault'], 60 | 'configType': data['configType'], 61 | 'feature': data['feature'], 62 | } 63 | viptela.result['payload'] = payload 64 | # Don't make the actual POST if we are in check mode 65 | if not module.check_mode: 66 | response = viptela.request('/dataservice/template/feature/', method='POST', data=json.dumps(payload)) 67 | viptela.result['changed'] = True 68 | 69 | # Process the device templates 70 | device_templates = viptela.get_device_template_dict() 71 | for device_template in device_template_data: 72 | if device_template['templateName'] not in device_templates: 73 | payload = { 74 | 'templateName': device_template['templateName'], 75 | 'templateDescription': device_template['templateDescription'], 76 | 'deviceType': device_template['deviceType'], 77 | 'factoryDefault': device_template['factoryDefault'], 78 | 'configType': device_template['configType'] 79 | } 80 | 81 | # 82 | # File templates are much easier in that they are just a bunch of CLI 83 | # 84 | if device_template['configType'] == 'file': 85 | payload['templateConfiguration'] = device_template['templateConfiguration'] 86 | if not module.check_mode: 87 | viptela.request('/dataservice/template/device/cli', method='POST', payload=payload) 88 | # 89 | # Feature based templates are just a list of templates Id that make up a devie template. We are 90 | # given the name of the feature templates, but we need to translate that to the template ID 91 | # 92 | else: 93 | if 'generalTemplates' in device_template: 94 | payload['generalTemplates'] = viptela.generalTemplates_to_id(device_template['generalTemplates']) 95 | else: 96 | viptela.fail_json(msg="No generalTemplates found in device template", data=device_template) 97 | payload['policyId'] = '' 98 | if 'connectionPreference' in device_template: 99 | payload['connectionPreference'] = device_template['connectionPreference'] 100 | if 'connectionPreferenceRequired' in device_template: 101 | payload['connectionPreferenceRequired'] = device_template['connectionPreferenceRequired'] 102 | payload['featureTemplateUidRange'] = [] 103 | # Don't make the actual POST if we are in check mode 104 | if not module.check_mode: 105 | viptela.request('/dataservice/template/device/feature', method='POST', payload=payload) 106 | viptela.result['changed'] = True 107 | 108 | viptela.exit_json(**viptela.result) 109 | 110 | def main(): 111 | run_module() 112 | 113 | if __name__ == '__main__': 114 | main() -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /module_utils/viptela.py: -------------------------------------------------------------------------------- 1 | # from __future__ import absolute_import, division, generate_function 2 | # __metaclass__ = type 3 | import json 4 | import requests 5 | import re 6 | import time 7 | from ansible.module_utils.basic import AnsibleModule, json, env_fallback 8 | from collections import OrderedDict 9 | 10 | try: 11 | from json.decoder import JSONDecodeError 12 | except ImportError: 13 | JSONDecodeError = ValueError 14 | 15 | def viptela_argument_spec(): 16 | return dict(host=dict(type='str', required=True, fallback=(env_fallback, ['VMANAGE_HOST'])), 17 | user=dict(type='str', required=True, fallback=(env_fallback, ['VMANAGE_USER'])), 18 | password=dict(type='str', required=True, fallback=(env_fallback, ['VMANAGE_PASSWORD'])), 19 | validate_certs=dict(type='bool', required=False, default=False), 20 | timeout=dict(type='int', default=30) 21 | ) 22 | 23 | STANDARD_HTTP_TIMEOUT = 10 24 | STANDARD_JSON_HEADER = {'Connection': 'keep-alive', 'Content-Type': 'application/json'} 25 | POLICY_LIST_DICT = { 26 | 'siteLists': 'site', 27 | 'vpnLists': 'vpn', 28 | } 29 | VALID_STATUS_CODES = [200, 201, 202, 203, 204, 205, 206, 207, 208, 226] 30 | 31 | class viptelaModule(object): 32 | 33 | def __init__(self, module, function=None): 34 | self.module = module 35 | self.params = module.params 36 | self.result = dict(changed=False) 37 | self.headers = dict() 38 | self.function = function 39 | self.cookies = None 40 | self.json = None 41 | 42 | self.method = None 43 | self.path = None 44 | self.response = None 45 | self.status = None 46 | self.url = None 47 | self.params['force_basic_auth'] = True 48 | self.user = self.params['user'] 49 | self.password = self.params['password'] 50 | self.host = self.params['host'] 51 | self.timeout = self.params['timeout'] 52 | self.modifiable_methods = ['POST', 'PUT', 'DELETE'] 53 | 54 | self.session = requests.Session() 55 | self.session.verify = self.params['validate_certs'] 56 | 57 | self.POLICY_DEFINITION_TYPES = ['cflowd', 'dnssecurity', 'control', 'hubandspoke', 'acl', 'vpnmembershipgroup', 58 | 'mesh', 'rewriterule', 'data', 'rewriterule', 'aclv6'] 59 | self.POLICY_LIST_TYPES = ['community', 'localdomain', 'ipv6prefix', 'dataipv6prefix', 'tloc', 'aspath', 'zone', 60 | 'color', 'sla', 'app', 'mirror', 'dataprefix', 'extcommunity', 'site', 'ipprefixall', 61 | 'prefix', 'umbrelladata', 'class', 'ipssignature', 'dataprefixall', 62 | 'urlblacklist', 'policer', 'urlwhitelist', 'vpn'] 63 | 64 | self.login() 65 | 66 | # Deleting (Calling destructor) 67 | # def __del__(self): 68 | # self.logout() 69 | 70 | def _fallback(self, value, fallback): 71 | if value is None: 72 | return fallback 73 | return value 74 | 75 | def list_to_dict(self, list, key_name, remove_key=True): 76 | dict = OrderedDict() 77 | for item in list: 78 | if key_name in item: 79 | if remove_key: 80 | key = item.pop(key_name) 81 | else: 82 | key = item[key_name] 83 | 84 | dict[key] = item 85 | # else: 86 | # self.fail_json(msg="key {0} not found in dictionary".format(key_name)) 87 | 88 | return dict 89 | 90 | @staticmethod 91 | def compare_payloads(new_payload, old_payload, compare_values=[]): 92 | payload_key_diff = [] 93 | for key, value in new_payload.items(): 94 | if key in compare_values: 95 | if key not in old_payload: 96 | payload_key_diff.append(key) 97 | elif new_payload[key] != old_payload[key]: 98 | payload_key_diff.append(key) 99 | return payload_key_diff 100 | 101 | 102 | def login(self): 103 | # self.session.headers.update({'Connection': 'keep-alive', 'Content-Type': 'application/json'}) 104 | 105 | try: 106 | response = self.session.post( 107 | url='https://{0}/j_security_check'.format(self.host), 108 | headers={'Content-Type': 'application/x-www-form-urlencoded'}, 109 | data={'j_username': self.user, 'j_password': self.password}, 110 | timeout=self.timeout 111 | ) 112 | except requests.exceptions.RequestException as e: # This is the correct syntax 113 | self.module.fail_json(msg=e) 114 | 115 | if response.text.startswith(''): 116 | self.fail_json(msg='Could not login to device, check user credentials.', **self.result) 117 | 118 | response = self.session.get( 119 | url='https://{0}/dataservice/client/token'.format(self.host), 120 | timeout=self.timeout 121 | ) 122 | if response.status_code == 200: 123 | self.session.headers['X-XSRF-TOKEN'] = response.content 124 | elif response.status_code == 404: 125 | # Assume this is pre-19.2 126 | pass 127 | else: 128 | self.fail_json(msg='Failed getting X-XSRF-TOKEN: {0}'.format(response.status_code)) 129 | 130 | return response 131 | 132 | def logout(self): 133 | self.request('/dataservice/settings/clientSessionTimeout') 134 | self.request('/logout') 135 | 136 | def request(self, url_path, method='GET', data=None, files=None, headers=None, payload=None, status_codes=VALID_STATUS_CODES): 137 | """Generic HTTP method for viptela requests.""" 138 | 139 | self.method = method 140 | self.url = 'https://{0}{1}'.format(self.host, url_path) 141 | self.result['url'] = self.url 142 | self.result['headers'] = self.session.headers.__dict__ 143 | if files == None: 144 | self.session.headers['Content-Type'] = 'application/json' 145 | 146 | # {'Connection': 'keep-alive', 'Content-Type': 'application/json'} 147 | # self.session.headers.update(headers) 148 | 149 | if payload: 150 | self.result['payload'] = payload 151 | data = json.dumps(payload) 152 | self.result['data'] = data 153 | 154 | response = self.session.request(method, self.url, files=files, data=data) 155 | 156 | self.status_code = response.status_code 157 | self.status = requests.status_codes._codes[response.status_code][0] 158 | decoded_response = {} 159 | if self.status_code not in status_codes: 160 | try: 161 | decoded_response = response.json() 162 | except JSONDecodeError: 163 | pass 164 | 165 | if 'error' in decoded_response: 166 | error='Unknown' 167 | details='Unknown' 168 | if 'details' in decoded_response['error']: 169 | details = decoded_response['error']['details'] 170 | if 'message' in decoded_response['error']: 171 | error = decoded_response['error']['message'] 172 | self.fail_json(msg='{0}: {1}'.format(error, details)) 173 | else: 174 | self.fail_json(msg=self.status) 175 | 176 | try: 177 | response.json = response.json() 178 | except JSONDecodeError: 179 | response.json = {} 180 | 181 | return response 182 | 183 | def get_template_attachments(self, template_id, key='host-name'): 184 | response = self.request('/dataservice/template/device/config/attached/{0}'.format(template_id)) 185 | 186 | attached_devices = [] 187 | if response.json: 188 | device_list = response.json['data'] 189 | for device in device_list: 190 | attached_devices.append(device[key]) 191 | 192 | return attached_devices 193 | 194 | def generalTemplates_to_id(self, generalTemplates): 195 | converted_generalTemplates = [] 196 | feature_templates = self.get_feature_template_dict(factory_default=True) 197 | for template in generalTemplates: 198 | if 'templateName' not in template: 199 | self.result['generalTemplates'] = generalTemplates 200 | self.fail_json(msg="Bad template") 201 | if template['templateName'] in feature_templates: 202 | template_item = { 203 | 'templateId': feature_templates[template['templateName']]['templateId'], 204 | 'templateType': template['templateType']} 205 | if 'subTemplates' in template: 206 | subTemplates = [] 207 | for sub_template in template['subTemplates']: 208 | if sub_template['templateName'] in feature_templates: 209 | subTemplates.append( 210 | {'templateId': feature_templates[sub_template['templateName']]['templateId'], 211 | 'templateType': sub_template['templateType']}) 212 | else: 213 | self.fail_json(msg="There is no existing feature template named {0}".format( 214 | sub_template['templateName'])) 215 | template_item['subTemplates'] = subTemplates 216 | 217 | converted_generalTemplates.append(template_item) 218 | else: 219 | self.fail_json(msg="There is no existing feature template named {0}".format(template['templateName'])) 220 | 221 | return converted_generalTemplates 222 | 223 | def convert_sequences_to_id(self, sequence_list): 224 | for sequence in sequence_list: 225 | for entry in sequence['match']['entries']: 226 | policy_list_dict = self.get_policy_list_dict(entry['listType']) 227 | if entry['listName'] in policy_list_dict: 228 | entry['ref'] = policy_list_dict[entry['listName']]['listId'] 229 | entry.pop('listName') 230 | entry.pop('listType') 231 | else: 232 | self.fail_json(msg="Could not find list {0} of type {1}".format(entry['listName'], entry['listType'])) 233 | return sequence_list 234 | 235 | def get_device_status(self, value, key='system-ip'): 236 | result = self.request('/dataservice/device?{0}={1}'.format(key, value)) 237 | 238 | try: 239 | return response.json['data'][0] 240 | except: 241 | return {} 242 | 243 | def get_device_template_list(self, factory_default=False): 244 | response = self.request('/dataservice/template/device') 245 | 246 | return_list = [] 247 | if response.json: 248 | 249 | device_body = response.json 250 | feature_template_dict = self.get_feature_template_dict(factory_default=True, key_name='templateId') 251 | 252 | for device in device_body['data']: 253 | object_response = self.request('/dataservice/template/device/object/{0}'.format(device['templateId'])) 254 | if object_response.json: 255 | object = object_response.json 256 | if not factory_default and object['factoryDefault']: 257 | continue 258 | 259 | if 'generalTemplates' in object: 260 | generalTemplates = [] 261 | for old_template in object.pop('generalTemplates'): 262 | new_template = { 263 | 'templateName': feature_template_dict[old_template['templateId']]['templateName'], 264 | 'templateType': old_template['templateType']} 265 | if 'subTemplates' in old_template: 266 | subTemplates = [] 267 | for sub_template in old_template['subTemplates']: 268 | subTemplates.append({'templateName':feature_template_dict[sub_template['templateId']]['templateName'], 'templateType':sub_template['templateType']}) 269 | new_template['subTemplates'] = subTemplates 270 | 271 | generalTemplates.append(new_template) 272 | object['generalTemplates'] = generalTemplates 273 | 274 | object['templateId'] = device['templateId'] 275 | object['attached_devices'] = self.get_template_attachments(device['templateId']) 276 | object['input'] = self.get_template_input(device['templateId']) 277 | 278 | return_list.append(object) 279 | 280 | return return_list 281 | 282 | def get_device_template_dict(self, factory_default=False, key_name='templateName', remove_key=True): 283 | device_template_list = self.get_device_template_list(factory_default=factory_default) 284 | 285 | return self.list_to_dict(device_template_list, key_name, remove_key) 286 | 287 | def get_feature_template_list(self, factory_default=False): 288 | response = self.request('/dataservice/template/feature') 289 | 290 | return_list = [] 291 | if response.json: 292 | template_list = response.json['data'] 293 | for template in template_list: 294 | if not factory_default and template['factoryDefault']: 295 | continue 296 | template['templateDefinition'] = json.loads(template['templateDefinition']) 297 | template.pop('editedTemplateDefinition', None) 298 | return_list.append(template) 299 | 300 | return return_list 301 | 302 | def get_feature_template_dict(self, factory_default=False, key_name='templateName', remove_key=True): 303 | feature_template_list = self.get_feature_template_list(factory_default=factory_default) 304 | 305 | return self.list_to_dict(feature_template_list, key_name, remove_key) 306 | 307 | def get_policy_list(self, type, list_id): 308 | response = self.request('/dataservice/template/policy/list/{0}/{1}'.format(type.lower(), list_id)) 309 | return response.json 310 | 311 | def get_policy_list_list(self, type='all'): 312 | if type == 'all': 313 | response = self.request('/dataservice/template/policy/list', status_codes=[200, 404]) 314 | else: 315 | response = self.request('/dataservice/template/policy/list/{0}'.format(type.lower()), status_codes=[200, 404]) 316 | 317 | if response.status_code == 404: 318 | return [] 319 | else: 320 | return response.json['data'] 321 | 322 | def get_policy_list_dict(self, type, key_name='name', remove_key=False): 323 | 324 | policy_list = self.get_policy_list_list(type) 325 | 326 | return self.list_to_dict(policy_list, key_name, remove_key=remove_key) 327 | 328 | def get_policy_definition(self, type, definition_id): 329 | response = self.request('/dataservice/template/policy/definition/{0}/{1}'.format(type, definition_id)) 330 | return response.json 331 | 332 | def get_policy_definition_list(self, type): 333 | response = self.request('/dataservice/template/policy/definition/{0}'.format(type)) 334 | 335 | return response.json['data'] 336 | 337 | def get_vmanage_org(self): 338 | response = self.request('/dataservice/settings/configuration/organization') 339 | try: 340 | return response.json['data'][0]['org'] 341 | except: 342 | return None 343 | 344 | def set_vmanage_org(self, org): 345 | payload = {'org': org} 346 | response = self.request('/dataservice/settings/configuration/organization',method='POST', payload=payload) 347 | 348 | return response.json['data'] 349 | 350 | def get_vmanage_vbond(self): 351 | response = self.request('/dataservice/settings/configuration/device') 352 | vbond = None 353 | vbond_port = None 354 | try: 355 | return {'vbond': response.json['data'][0]['domainIp'], 'vbond_port': response.json['data'][0]['port']} 356 | except: 357 | return {'vbond': None, 'vbond_port': None} 358 | 359 | def set_vmanage_vbond(self, vbond, vbond_port='12346'): 360 | payload = {'domainIp': vbond, 'port': vbond_port} 361 | response = self.request('/dataservice/settings/configuration/device', method='POST', payload=payload) 362 | 363 | return 364 | 365 | def get_vmanage_ca_type(self): 366 | response = self.request('/dataservice/settings/configuration/certificate') 367 | try: 368 | return response.json['data'][0]['certificateSigning'] 369 | except: 370 | return None 371 | 372 | def set_vmanage_ca_type(self, type): 373 | payload = {'certificateSigning': type, 'challengeAvailable': 'false'} 374 | response = self.request('/dataservice/settings/configuration/certificate', method='POST', payload=payload) 375 | return 376 | 377 | def get_vmanage_root_cert(self): 378 | response = self.request('/dataservice/certificate/rootcertificate') 379 | try: 380 | return response.json['rootcertificate'] 381 | except: 382 | return None 383 | 384 | def set_vmanage_root_cert(self, cert): 385 | payload = {'enterpriseRootCA': cert} 386 | response = self.request('/dataservice/settings/configuration/certificate/enterpriserootca', method='PUT', payload=payload) 387 | return 388 | 389 | def install_device_cert(self, cert): 390 | response = self.request('/dataservice/certificate/install/signedCert', method='POST', data=cert) 391 | if response.json and 'id' in response.json: 392 | self.waitfor_action_completion(response.json['id']) 393 | else: 394 | self.fail_json(msg='Did not get action ID after attaching device to template.') 395 | return response.json['id'] 396 | 397 | def get_policy_definition_dict(self, type, key_name='name', remove_key=False): 398 | 399 | policy_definition_list = self.get_policy_definition_list(type) 400 | 401 | return self.list_to_dict(policy_definition_list, key_name, remove_key=remove_key) 402 | 403 | def get_central_policy_list(self): 404 | response = self.request('/dataservice/template/policy/vsmart') 405 | if response.json: 406 | central_policy_list = response.json['data'] 407 | for policy in central_policy_list: 408 | policy['policyDefinition'] = json.loads(policy['policyDefinition']) 409 | for item in policy['policyDefinition']['assembly']: 410 | policy_definition = self.get_policy_definition(item['type'], item['definitionId']) 411 | item['definitionName'] = policy_definition['name'] 412 | for entry in item['entries']: 413 | for key, list in entry.items(): 414 | if key in POLICY_LIST_DICT: 415 | for index, list_id in enumerate(list): 416 | policy_list = self.get_policy_list(POLICY_LIST_DICT[key], list_id) 417 | list[index] = policy_list['name'] 418 | # if 'policyDefinition' in policy: 419 | # for old_template in policy.pop('policyDefinition'): 420 | # 421 | 422 | return central_policy_list 423 | else: 424 | return [] 425 | 426 | def get_central_policy_dict(self, key_name='policyName', remove_key=False): 427 | 428 | central_policy_list = self.get_central_policy_list() 429 | 430 | return self.list_to_dict(central_policy_list, key_name, remove_key=remove_key) 431 | 432 | def get_unused_device(self, model): 433 | response = self.request('/dataservice/system/device/vedges?model={0}&state=tokengenerated'.format(model)) 434 | 435 | if response.json: 436 | try: 437 | return response.json['data'][0] 438 | except: 439 | return response.json['data'] 440 | else: 441 | return {} 442 | 443 | def get_device_by_state(self, state, type='vedges'): 444 | response = self.request('/dataservice/system/device/{0}?state={1}'.format(type, state)) 445 | 446 | if response.json: 447 | try: 448 | return response.json['data'][0] 449 | except: 450 | return response.json['data'] 451 | else: 452 | return {} 453 | 454 | def get_device_by_uuid(self, uuid, type='vedges'): 455 | response = self.request('/dataservice/system/device/{0}?uuid={1}'.format(type, uuid)) 456 | 457 | if response.json: 458 | try: 459 | return response.json['data'][0] 460 | except: 461 | return response.json['data'] 462 | else: 463 | return {} 464 | 465 | def get_device_by_device_ip(self, device_ip, type='vedges'): 466 | response = self.request('/dataservice/system/device/{0}?deviceIP={1}'.format(type, device_ip)) 467 | 468 | if response.json: 469 | try: 470 | return response.json['data'][0] 471 | except: 472 | return response.json['data'] 473 | else: 474 | return {} 475 | 476 | def get_device_by_name(self, name, type='vedges'): 477 | device_dict = self.get_device_dict(type) 478 | 479 | try: 480 | return device_dict[name] 481 | except: 482 | return {} 483 | 484 | def get_device_list(self, type, key_name='host-name', remove_key=True): 485 | response = self.request('/dataservice/system/device/{0}'.format(type)) 486 | 487 | if response.json: 488 | return response.json['data'] 489 | else: 490 | return [] 491 | 492 | def get_device_dict(self, type, key_name='host-name', remove_key=False): 493 | 494 | device_list = self.get_device_list(type) 495 | 496 | return self.list_to_dict(device_list, key_name=key_name, remove_key=remove_key) 497 | 498 | def get_device_status_list(self): 499 | response = self.request('/dataservice/device') 500 | 501 | if response.json: 502 | return response.json['data'] 503 | else: 504 | return [] 505 | 506 | def get_device_status_dict(self, key_name='host-name', remove_key=False): 507 | 508 | device_list = self.get_device_list() 509 | 510 | return self.list_to_dict(device_list, key_name=key_name, remove_key=remove_key) 511 | 512 | def get_device_status(self, value, key='system-ip'): 513 | response = self.request('/dataservice/device?{0}={1}'.format(key, value)) 514 | 515 | try: 516 | return response.json['data'][0] 517 | except: 518 | return {} 519 | 520 | 521 | def get_device_vedges(self, key_name='host-name', remove_key=True): 522 | response = self.request('/dataservice/system/device/vedges') 523 | 524 | if response.json: 525 | return self.list_to_dict(response.json['data'], key_name=key_name, remove_key=remove_key) 526 | else: 527 | return {} 528 | 529 | 530 | def get_device_controllers(self, key_name='host-name', remove_key=True): 531 | response = self.request('/dataservice/system/device/controllers') 532 | 533 | if response.json: 534 | return self.list_to_dict(response.json['data'], key_name=key_name, remove_key=remove_key) 535 | else: 536 | return {} 537 | 538 | def create_controller(self, device_ip, personality, username, password): 539 | payload = { 540 | "deviceIP": device_ip, 541 | "username": username, 542 | "password": password, 543 | "personality": personality, 544 | "generateCSR": "false" 545 | } 546 | 547 | response = self.request('/dataservice/system/device', method='POST', payload=payload) 548 | 549 | if response.json: 550 | return response.json 551 | else: 552 | return None 553 | 554 | def delete_controller(self, uuid): 555 | response = self.request('/dataservice/certificate/{0}'.format(uuid), method='DELETE') 556 | 557 | return response 558 | 559 | def decommision_device(self, uuid): 560 | response = self.request('/dataservice/system/device/decommission/{0}'.format(uuid), method='PUT') 561 | 562 | return response 563 | 564 | def generate_csr(self, device_ip): 565 | payload = {"deviceIP": device_ip} 566 | response = self.request('/dataservice/certificate/generate/csr', method='POST', payload=payload) 567 | 568 | if response.json: 569 | try: 570 | return response.json['data'][0]['deviceCSR'] 571 | except: 572 | return None 573 | else: 574 | return None 575 | 576 | def generate_bootstrap(self, uuid): 577 | response = self.request('/dataservice/system/device/bootstrap/device/{0}?configtype=cloudinit'.format(uuid)) 578 | 579 | try: 580 | bootstrap_config = response.json['bootstrapConfig'] 581 | except: 582 | return None 583 | 584 | regex = re.compile(r'otp : (?P[a-z0-9]+)[^a-z0-9]') 585 | match = regex.search(bootstrap_config) 586 | if match: 587 | otp = match.groups('otp')[0] 588 | else: 589 | otp = None 590 | 591 | return_dict = { 592 | 'bootstrapConfig': bootstrap_config, 593 | 'otp': otp, 594 | 'uuid': uuid 595 | } 596 | return return_dict 597 | 598 | def get_template_input(self, template_id): 599 | payload = { 600 | "deviceIds": [], 601 | "isEdited": False, 602 | "isMasterEdited": False, 603 | "templateId": template_id 604 | } 605 | return_dict = { 606 | "columns": [], 607 | } 608 | response = self.request('/dataservice/template/device/config/input', method='POST', payload=payload) 609 | 610 | if response.json: 611 | if 'header' in response.json and 'columns' in response.json['header']: 612 | column_list = response.json['header']['columns'] 613 | 614 | regex = re.compile(r'\((?P[^(]+)\)') 615 | 616 | for column in column_list: 617 | if column['editable']: 618 | match = regex.search(column['title']) 619 | if match: 620 | variable = match.groups('variable')[0] 621 | else: 622 | variable = None 623 | 624 | entry = {'title': column['title'], 625 | 'property': column['property'], 626 | 'variable': variable} 627 | return_dict['columns'].append(entry) 628 | 629 | return return_dict 630 | 631 | def get_template_variables(self, template_id): 632 | payload = { 633 | "deviceIds": [], 634 | "isEdited": False, 635 | "isMasterEdited": False, 636 | "templateId": template_id 637 | } 638 | return_dict = {} 639 | response = self.request('/dataservice/template/device/config/input', method='POST', payload=payload) 640 | 641 | if response.json: 642 | if 'header' in response.json and 'columns' in response.json['header']: 643 | column_list = response.json['header']['columns'] 644 | 645 | regex = re.compile(r'\((?P[^(]+)\)') 646 | 647 | for column in column_list: 648 | if column['editable']: 649 | #match = regex.search(column['title']) 650 | match = regex.findall(column['title']) 651 | if match: 652 | #variable = match.groups('variable')[0] 653 | variable = match[-1] 654 | return_dict[variable] = column['property'] 655 | 656 | return return_dict 657 | 658 | 659 | def get_template_optional_variables(self, template_id): 660 | payload = { 661 | "deviceIds": [], 662 | "isEdited": False, 663 | "isMasterEdited": False, 664 | "templateId": template_id 665 | } 666 | return_dict = {} 667 | response = self.request('/dataservice/template/device/config/input', method='POST', payload=payload) 668 | 669 | if response.json: 670 | if 'header' in response.json and 'columns' in response.json['header']: 671 | column_list = response.json['header']['columns'] 672 | 673 | regex = re.compile(r'\((?P[^(]+)\)') 674 | 675 | # The following can be removed once the API will mark as optional 676 | # the nexthop value of a static route that has been marked as optional 677 | regexYangStaticR = re.compile(r'.*/vpn-instance/ip/route/.*/prefix') 678 | regex_yang_nexthop = re.compile(r'.*/vpn-instance/ip/route/(?P.*)/next-hop/.*/address') 679 | optionalStaticRoutesList = [] 680 | # Until here 681 | 682 | # The following can be removed once the API will mark as optional 683 | # all the vrrp attributes once the vrrp grp-id has been marked optional 684 | regexYangVRRPgrpID = re.compile(r'.*/vrrp/.*/grp-id') 685 | regexYangVRRPpriority = re.compile(r'.*/vrrp/(?P.*)/priority') 686 | regexYangVRRPtimer = re.compile(r'.*/vrrp/(?P.*)/timer') 687 | regexYangVRRPtrackPrefix = re.compile(r'.*/vrrp/(?P.*)/track-prefix-list') 688 | regexYangVRRPtrackOMP = re.compile(r'.*/vrrp/(?P.*)/track-omp') 689 | regexYangVRRPipAddress = re.compile(r'.*/vrrp/(?P.*)/ipv4/address') 690 | optionalVRRPvariales = [] 691 | # Until here 692 | 693 | # The following can be removed once the API will mark as optional 694 | # all the logging attributes once the logging has been marked optional 695 | regexYangLoggingServerName = re.compile(r'///logging/server/.*/name') 696 | regexYangLoggingSourceInt = re.compile(r'///logging/server/(?P.*)/source-interface') 697 | regexYangLoggingVPN = re.compile(r'///logging/server/(?P.*)/vpn') 698 | regexYangLoggingPriority = re.compile(r'///logging/server/(?P.*)/priority') 699 | optionalLoggingVariales = [] 700 | # Until here 701 | 702 | for column in column_list: 703 | 704 | # The following can be removed once the API will mark as optional 705 | # the nexthop value of a static route that has been marked as optional 706 | 707 | # Based on the regular expression above we match 708 | # static routes and next-hop variables based on the YANG variable 709 | # a static route looks like this /1/vpn-instance/ip/route//prefix 710 | # a next-hop looks like this /1/vpn-instance/ip/route//next-hop//address 711 | 712 | # If we find a static route and this is optional we 713 | # store its common name into an array 714 | # we don't add this parameter to the return list now 715 | # since it will be added later 716 | isStaticR = regexYangStaticR.match(column['property']) 717 | if isStaticR and column['optional']: 718 | match = regex.findall(column['title']) 719 | if match: 720 | variable = match[-1] 721 | optionalStaticRoutesList.append(variable) 722 | 723 | 724 | # If we find a next-hop we extrapolate the common name 725 | # of the static route. If we have already found that 726 | # common name and we know it is optional we will add 727 | # this next-hop paramter to the return list since it 728 | # will be optional as well 729 | 730 | # ALL OF THIS IS BASED ON THE ASSUMPTION THAT STATIC ROUTES 731 | # ARE LISTED BEFORE NEXT-HOP VALUES 732 | nextHopStaticR = regex_yang_nexthop.findall(column['property']) 733 | if nextHopStaticR: 734 | if nextHopStaticR[0] in optionalStaticRoutesList: 735 | match = regex.findall(column['title']) 736 | if match: 737 | variable = match[-1] 738 | return_dict[variable] = column['property'] 739 | 740 | # Until here 741 | 742 | # The following can be removed once the API will mark as optional 743 | # the attributes for vrrp as optional if the whole vrrp has been 744 | # marked as optional 745 | 746 | # Based on the regular expression above we match 747 | # vrrp atributes based on the YANG variable 748 | 749 | # If we find a VRRP grp ID and this is optional we 750 | # store its common name into an array 751 | # we don't add this parameter to the return list now 752 | # since it will be added later 753 | isVRRP = regexYangVRRPgrpID.match(column['property']) 754 | if isVRRP and column['optional']: 755 | match = regex.findall(column['title']) 756 | if match: 757 | variable = match[-1] 758 | optionalVRRPvariales.append(variable) 759 | 760 | 761 | # If we find a any vrrp attribute we extrapolate the common name 762 | # If we have already found that 763 | # common name and we know it is optional we will add 764 | # this paramter to the return list since it 765 | # will be optional as well 766 | 767 | # ALL OF THIS IS BASED ON THE ASSUMPTION THAT VRRP GRP-ID is 768 | # LISTED BEFORE ALL THE OTHER ATTRIBUTES 769 | VRRPpriority = regexYangVRRPpriority.findall(column['property']) 770 | VRRPtimer = regexYangVRRPtimer.findall(column['property']) 771 | VRRPtrackPrefix = regexYangVRRPtrackPrefix.findall(column['property']) 772 | VRRPipAddress = regexYangVRRPipAddress.findall(column['property']) 773 | VRRPtrackOMP = regexYangVRRPtrackOMP.findall(column['property']) 774 | if VRRPpriority: 775 | if VRRPpriority[0] in optionalVRRPvariales: 776 | match = regex.findall(column['title']) 777 | if match: 778 | variable = match[-1] 779 | return_dict[variable] = column['property'] 780 | elif VRRPtimer: 781 | if VRRPtimer[0] in optionalVRRPvariales: 782 | match = regex.findall(column['title']) 783 | if match: 784 | variable = match[-1] 785 | return_dict[variable] = column['property'] 786 | elif VRRPtrackPrefix: 787 | if VRRPtrackPrefix[0] in optionalVRRPvariales: 788 | match = regex.findall(column['title']) 789 | if match: 790 | variable = match[-1] 791 | return_dict[variable] = column['property'] 792 | elif VRRPipAddress: 793 | if VRRPipAddress[0] in optionalVRRPvariales: 794 | match = regex.findall(column['title']) 795 | if match: 796 | variable = match[-1] 797 | return_dict[variable] = column['property'] 798 | elif VRRPtrackOMP: 799 | if VRRPtrackOMP[0] in optionalVRRPvariales: 800 | match = regex.findall(column['title']) 801 | if match: 802 | variable = match[-1] 803 | return_dict[variable] = column['property'] 804 | # Until here 805 | 806 | # Same logic for logging optional variables 807 | isLogging = regexYangLoggingServerName.match(column['property']) 808 | if isLogging and column['optional']: 809 | match = regex.findall(column['title']) 810 | if match: 811 | variable = match[-1] 812 | optionalLoggingVariales.append(variable) 813 | 814 | LoggingSourceInt = regexYangLoggingSourceInt.findall(column['property']) 815 | LoggingVPN = regexYangLoggingVPN.findall(column['property']) 816 | LoggingPriority = regexYangLoggingPriority.findall(column['property']) 817 | 818 | if LoggingSourceInt: 819 | if LoggingSourceInt[0] in optionalLoggingVariales: 820 | match = regex.findall(column['title']) 821 | if match: 822 | variable = match[-1] 823 | return_dict[variable] = column['property'] 824 | elif LoggingVPN: 825 | if LoggingVPN[0] in optionalLoggingVariales: 826 | match = regex.findall(column['title']) 827 | if match: 828 | variable = match[-1] 829 | return_dict[variable] = column['property'] 830 | elif LoggingPriority: 831 | if LoggingPriority[0] in optionalLoggingVariales: 832 | match = regex.findall(column['title']) 833 | if match: 834 | variable = match[-1] 835 | return_dict[variable] = column['property'] 836 | 837 | # Until here 838 | 839 | if column['editable'] and column['optional']: 840 | match = regex.findall(column['title']) 841 | if match: 842 | variable = match[-1] 843 | return_dict[variable] = column['property'] 844 | 845 | 846 | return return_dict 847 | 848 | def get_software_images_list(self): 849 | #TODO undertand the difference with the URL: /dataservice/device/action/software/images used in devnetsandbox 850 | response = self.request('/dataservice/device/action/software', method='GET') 851 | 852 | if response.json: 853 | return response.json['data'] 854 | else: 855 | return [] 856 | 857 | def get_installed_software(self,type): 858 | response = self.request('/dataservice/device/action/install/devices/{0}?groupId=all'.format(type), method='GET') 859 | 860 | if response.json: 861 | return response.json['data'] 862 | else: 863 | return [] 864 | 865 | def software_install(self,devices,deviceType,data,reboot): 866 | 867 | payload= { 868 | "action":"install", 869 | "input":{ 870 | "vEdgeVPN":0, 871 | "vSmartVPN":0, 872 | "data": data, 873 | "versionType":"vmanage", 874 | "reboot":reboot, 875 | "sync": True 876 | }, 877 | "devices": devices, 878 | "deviceType": deviceType 879 | } 880 | 881 | response = self.request('/dataservice/device/action/install', method='POST', payload=payload) 882 | 883 | if response.json and 'id' in response.json: 884 | self.waitfor_action_completion(response.json['id']) 885 | else: 886 | self.fail_json( 887 | msg='Did not get action ID after installing software.') 888 | 889 | return response.json['id'] 890 | 891 | def set_default_partition(self,devices,deviceType): 892 | 893 | payload= { 894 | "action":"defaultpartition", 895 | "devices": devices, 896 | "deviceType": deviceType 897 | } 898 | 899 | response = self.request('/dataservice/device/action/defaultpartition', method='POST', payload=payload) 900 | 901 | if response.json and 'id' in response.json: 902 | self.waitfor_action_completion(response.json['id']) 903 | else: 904 | self.fail_json( 905 | msg='Did not get action ID after setting default image.') 906 | 907 | return response.json['id'] 908 | 909 | def push_certificates(self): 910 | response = self.request('/dataservice/certificate/vedge/list?action=push', method='POST') 911 | if response.json and 'id' in response.json: 912 | self.waitfor_action_completion(response.json['id']) 913 | else: 914 | self.fail_json(msg='Did not get action ID after pushing certificates.') 915 | return response.json['id'] 916 | 917 | def reattach_device_template(self, template_id, process_id=None): 918 | device_list = self.get_template_attachments(template_id, key='uuid') 919 | # First, we need to get the input to feed to the re-attach 920 | payload = { 921 | "templateId": template_id, 922 | "deviceIds": device_list, 923 | "isEdited": "true", 924 | "isMasterEdited": "false" 925 | } 926 | response = self.request('/dataservice/template/device/config/input/', method='POST', payload=payload) 927 | # Then we feed that to the attach 928 | if response.json and 'data' in response.json: 929 | payload = { 930 | "deviceTemplateList": 931 | [ 932 | { 933 | "templateId": template_id, 934 | "device": response.json['data'], 935 | "isEdited": "true" 936 | } 937 | ] 938 | } 939 | response = self.request('/dataservice/template/device/config/attachfeature', method='POST', payload=payload) 940 | if response.json and 'id' in response.json: 941 | self.waitfor_action_completion(response.json['id']) 942 | else: 943 | self.fail_json( 944 | msg='Did not get action ID after attaching device to template.') 945 | 946 | # if process_id: 947 | # self.request('/dataservice/template/lock/{0}'.format(process_id), method='DELETE') 948 | 949 | else: 950 | self.fail_json(msg="Could not retrieve input for template {0}".format(template_id)) 951 | return response.json['id'] 952 | 953 | def waitfor_action_completion(self, action_id): 954 | status = 'in_progress' 955 | response = {} 956 | while status == "in_progress": 957 | response = self.request('/dataservice/device/action/status/{0}'.format(action_id)) 958 | if response.json: 959 | status = response.json['summary']['status'] 960 | if 'data' in response.json and response.json['data']: 961 | action_status = response.json['data'][0]['statusId'] 962 | action_activity = response.json['data'][0]['activity'] 963 | if 'actionConfig' in response.json['data'][0]: 964 | action_config = response.json['data'][0]['actionConfig'] 965 | else: 966 | action_config = None 967 | else: 968 | self.fail_json(msg="Unable to get action status: No response") 969 | time.sleep(10) 970 | 971 | # self.result['action_response'] = response.json 972 | self.result['action_id'] = action_id 973 | self.result['action_status'] = action_status 974 | self.result['action_activity'] = action_activity 975 | self.result['action_config'] = action_config 976 | if self.result['action_status'] == 'failure': 977 | self.fail_json(msg="Action failed") 978 | return response 979 | 980 | def exit_json(self, **kwargs): 981 | # self.logout() 982 | """Custom written method to exit from module.""" 983 | 984 | self.result.update(**kwargs) 985 | self.module.exit_json(**self.result) 986 | 987 | def fail_json(self, msg, **kwargs): 988 | # self.logout() 989 | """Custom written method to return info on failure.""" 990 | 991 | self.result.update(**kwargs) 992 | self.module.fail_json(msg=msg, **self.result) 993 | -------------------------------------------------------------------------------- /plugins/httpapi/vmanage.py: -------------------------------------------------------------------------------- 1 | from __future__ import (absolute_import, division, print_function) 2 | 3 | __metaclass__ = type 4 | 5 | import json 6 | 7 | from ansible.module_utils.basic import to_text 8 | from ansible.errors import AnsibleConnectionFailure 9 | from ansible.module_utils.six.moves.urllib.error import HTTPError 10 | from ansible.plugins.httpapi import HttpApiBase 11 | from ansible.module_utils.connection import ConnectionError 12 | 13 | BASE_HEADERS = { 14 | 'Content-Type': 'application/json', 15 | } 16 | 17 | 18 | class HttpApi(HttpApiBase): 19 | def login(self, username, password): 20 | if username and password: 21 | payload = {'user': username, 'password': password} 22 | url = '/web_api/login' 23 | response, response_data = self.send_request(url, payload) 24 | else: 25 | raise AnsibleConnectionFailure('Username and password are required for login') 26 | 27 | try: 28 | self.connection._auth = {'X-chkp-sid': response_data['sid']} 29 | self.connection._session_uid = response_data['uid'] 30 | except KeyError: 31 | raise ConnectionError( 32 | 'Server returned response without token info during connection authentication: %s' % response) 33 | 34 | def logout(self): 35 | url = '/logout' 36 | 37 | response, dummy = self.send_request(url, None) 38 | 39 | def get_session_uid(self): 40 | return self.connection._session_uid 41 | 42 | def send_request(self, path, body_params): 43 | data = json.dumps(body_params) if body_params else '{}' 44 | 45 | try: 46 | self._display_request() 47 | response, response_data = self.connection.send(path, data, method='POST', headers=BASE_HEADERS) 48 | value = self._get_response_value(response_data) 49 | 50 | return response.getcode(), self._response_to_json(value) 51 | except AnsibleConnectionFailure as e: 52 | return 404, 'Object not found' 53 | except HTTPError as e: 54 | error = json.loads(e.read()) 55 | return e.code, error 56 | 57 | def _display_request(self): 58 | self.connection.queue_message('vvvv', 'Web Services: %s %s' % ('POST', self.connection._url)) 59 | 60 | def _get_response_value(self, response_data): 61 | return to_text(response_data.getvalue()) 62 | 63 | def _response_to_json(self, response_text): 64 | try: 65 | return json.loads(response_text) if response_text else {} 66 | # JSONDecodeError only available on Python 3.5+ 67 | except ValueError: 68 | raise ConnectionError('Invalid JSON response: %s' % response_text) 69 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==2.8.6 2 | asn1crypto==0.24.0 3 | bcrypt==3.1.7 4 | certifi==2019.6.16 5 | cffi==1.12.3 6 | chardet==3.0.4 7 | cryptography==2.7 8 | enum34==1.1.6 9 | idna==2.6 10 | ipaddress==1.0.17 11 | Jinja2==2.10.1 12 | keyring==10.6.0 13 | keyrings.alt==3.0 14 | lxml==4.4.0 15 | MarkupSafe==1.1.1 16 | ncclient==0.6.6 17 | netaddr==0.7.19 18 | paramiko==2.6.0 19 | pycparser==2.19 20 | pycrypto==2.6.1 21 | PyNaCl==1.3.0 22 | pyOpenSSL==19.0.0 23 | pyxdg==0.26 24 | PyYAML==5.1.2 25 | requests==2.22.0 26 | SecretStorage==2.3.1 27 | selectors2==2.0.1 28 | six==1.11.0 29 | urllib3==1.25.3 30 | -------------------------------------------------------------------------------- /tasks/add-controller.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: See if {{ device_personality }}, {{ device_ip }} exists 5 | uri: 6 | url: "https://{{ vmanage_ip }}/dataservice/system/device/controllers" 7 | method: GET 8 | headers: 9 | Cookie: "{{ viptela_api_cookie }}" 10 | validate_certs: "{{ validate_certs }}" 11 | return_content: yes 12 | register: uri_results 13 | delegate_to: localhost 14 | until: uri_results.msg.find("OK") != -1 15 | retries: 60 16 | delay: 10 17 | 18 | - set_fact: 19 | controller_deviceIP_list: "{{ uri_results.json.data | map(attribute='deviceIP') | list }}" 20 | controller_hostname_list: "{{ uri_results.json.data | selectattr('host-name', 'defined') | map(attribute='host-name') | list }}" 21 | 22 | - name: Add {{ device_personality }}, {{ device_ip }} 23 | uri: 24 | url: "https://{{ vmanage_ip }}/dataservice/system/device" 25 | method: POST 26 | headers: 27 | Cookie: "{{ viptela_api_cookie }}" 28 | body_format: json 29 | body: 30 | deviceIP: "{{ device_ip }}" 31 | username: "{{ viptela_api_username }}" 32 | password: "{{ viptela_api_password }}" 33 | personality: "{{ device_personality }}" 34 | generateCSR: "{{ device_generateCSR | default('false') }}" 35 | validate_certs: "{{ validate_certs }}" 36 | return_content: yes 37 | register: controller_results 38 | changed_when: true 39 | delegate_to: localhost 40 | when: (device_ip not in controller_deviceIP_list) and (device_hostname not in controller_hostname_list) 41 | retries: 60 42 | delay: 10 43 | until: controller_results is not failed -------------------------------------------------------------------------------- /tasks/bootstrap-vedge.yml: -------------------------------------------------------------------------------- 1 | - name: Get bootstrap info 2 | vmanage_device_bootstrap: 3 | host: "{{ vmanage_ip }}" 4 | user: admin 5 | password: admin 6 | name: "{{ inventory_hostname }}" 7 | model: 'vedge-cloud' 8 | delegate_to: localhost 9 | register: result 10 | retries: 10 11 | delay: 10 12 | until: result is not failed 13 | 14 | - block: 15 | - name: Copy Root CA 16 | copy: 17 | src: "{{ root_ca_file }}" 18 | dest: /home/admin 19 | register: copy_results 20 | 21 | - name: Install RootCA 22 | cli_command: 23 | command: "request root-cert-chain install /home/admin/myCA.pem" 24 | register: command_result 25 | connection: network_cli 26 | failed_when: "'Failed' in command_result.stdout" 27 | changed_when: true 28 | when: copy_results.changed 29 | 30 | - debug: msg="UUID - {{ result.bootstrap.uuid }}, TOKEN - {{ result.bootstrap.otp }}" 31 | 32 | - cli_command: 33 | command: "request vedge-cloud activate chassis-number {{ result.bootstrap.uuid }} token {{ result.bootstrap.otp }}" 34 | connection: network_cli 35 | register: command_results 36 | 37 | when: result.changed 38 | -------------------------------------------------------------------------------- /tasks/device-template.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: device-template,device-template:render,render 4 | vars: 5 | template_list: "{{ DeviceTemplate }}" 6 | template: sdwan-templates/device-template.j2 7 | folder: device-template 8 | when: DeviceTemplate is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: device-template,device-template:create,device-template:delete,device-template:debug 13 | vars: 14 | template_list: "{{ DeviceTemplate }}" 15 | template: sdwan-templates/device-template.j2 16 | folder: device-template 17 | when: DeviceTemplate is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: device-template,device-template:create 22 | vars: 23 | folder: device-template 24 | when: DeviceTemplate is defined 25 | 26 | - name: Interact with the vManage to create templates 27 | vmanage_device_template: 28 | user: "{{vmanage_user}}" 29 | host: "{{vmanage_host}}" 30 | password: "{{vmanage_password}}" 31 | state: present 32 | aggregate: "{{present_template_list}}" 33 | tags: device-template,device-template:create 34 | when: present_template_list 35 | 36 | - name: Interact with the vManage to create templates 37 | vmanage_device_template: 38 | user: "{{vmanage_user}}" 39 | host: "{{vmanage_host}}" 40 | password: "{{vmanage_password}}" 41 | state: absent 42 | aggregate: "{{absent_template_list}}" 43 | when: absent_template_list 44 | -------------------------------------------------------------------------------- /tasks/facts.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - debug: var=viptela_api_cookie 5 | 6 | - block: 7 | - name: Get Tenant Status 8 | uri: 9 | url: "https://{{ vmanage_ip }}/dataservice/system/device" 10 | method: GET 11 | headers: 12 | Cookie: "{{ viptela_api_cookie }}" 13 | validate_certs: "{{ validate_certs }}" 14 | return_content: yes 15 | register: tenantstatus_results 16 | delegate_to: localhost 17 | retries: 60 18 | delay: 10 19 | until: tenantstatus_results is not failed 20 | 21 | - debug: var=tenantstatus_results 22 | # 23 | # - set_fact: 24 | # tower_api_host_map: "{{ tower_api_host_map|default({}) | combine( { tower_api_host_name: post_result.json.id } ) }}" 25 | # 26 | # when: tower_api_host_map[tower_api_host_name] is not defined 27 | # 28 | # - name: Update host {{ tower_api_host_name }} 29 | # uri: 30 | # url: "https://{{ tower_api_endpoint }}/api/v2/hosts/{{ tower_api_host_map[tower_api_host_name] }}/" 31 | # method: PUT 32 | # headers: 33 | # Authorization: "Token {{ tower_api_token }}" 34 | # body: 35 | # enabled: "{{ 'true' if (tower_api_cloud_state == 'present') else 'false' }}" 36 | # name: "{{ tower_api_host_name }}" 37 | # variables: "{{ tower_api_host_vars | to_json }}" 38 | # body_format: json 39 | # validate_certs: "{{ validate_certs }}" 40 | # return_content: yes 41 | # status_code: 200,204 42 | # register: post_result 43 | # when: tower_api_host_map[tower_api_host_name] is defined 44 | # 45 | # - name: Add host {{ tower_api_host_name }} to groups {{ tower_api_host_groups | join(',')}} 46 | # uri: 47 | # url: "https://{{ tower_api_endpoint }}/api/v2/groups/{{ tower_api_group_map[group_item] }}/hosts/" 48 | # method: POST 49 | # headers: 50 | # Authorization: "Token {{ tower_api_token }}" 51 | # body: 52 | # name: "{{ tower_api_host_name }}" 53 | # body_format: json 54 | # validate_certs: "{{ validate_certs }}" 55 | # return_content: yes 56 | # status_code: 201,204 57 | # register: post_result 58 | # with_items: "{{ tower_api_host_groups }}" 59 | # loop_control: 60 | # loop_var: group_item 61 | -------------------------------------------------------------------------------- /tasks/feature-aaa.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-aaa,feature-aaa:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-aaa'] }}" 6 | template: sdwan-templates/feature-aaa.j2 7 | folder: feature-aaa 8 | when: FeatureTemplates['feature-aaa'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-aaa,feature-aaa:create,feature-aaa:delete,feature-aaa:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-aaa'] }}" 15 | template: sdwan-templates/feature-aaa.j2 16 | folder: feature-aaa 17 | when: FeatureTemplates['feature-aaa'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-aaa,feature-aaa:create 22 | vars: 23 | folder: feature-aaa 24 | when: FeatureTemplates['feature-aaa'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-aaa,feature-aaa:create 29 | when: FeatureTemplates['feature-aaa'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-aaa:debug,never 34 | when: FeatureTemplates['feature-aaa'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-aaa,feature-aaa:delete 39 | when: FeatureTemplates['feature-aaa'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-banner.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-banner,feature-banner:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-banner'] }}" 6 | template: sdwan-templates/feature-banner.j2 7 | folder: feature-banner 8 | when: FeatureTemplates['feature-banner'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-banner,feature-banner:create,feature-banner:delete,feature-banner:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-banner'] }}" 15 | template: sdwan-templates/feature-banner.j2 16 | folder: feature-banner 17 | when: FeatureTemplates['feature-banner'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-banner,feature-banner:create 22 | vars: 23 | folder: feature-banner 24 | when: FeatureTemplates['feature-banner'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-banner,feature-banner:create 29 | when: FeatureTemplates['feature-banner'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-banner:debug,never 34 | when: FeatureTemplates['feature-banner'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-banner,feature-banner:delete 39 | when: FeatureTemplates['feature-banner'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-bfd.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-bfd,feature-bfd:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-bfd'] }}" 6 | template: sdwan-templates/feature-bfd.j2 7 | folder: feature-bfd 8 | when: FeatureTemplates['feature-bfd'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-bfd,feature-bfd:create,feature-bfd:delete,feature-bfd:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-bfd'] }}" 15 | template: sdwan-templates/feature-bfd.j2 16 | folder: feature-bfd 17 | when: FeatureTemplates['feature-bfd'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-bfd,feature-bfd:create 22 | vars: 23 | folder: feature-bfd 24 | when: FeatureTemplates['feature-bfd'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-bfd,feature-bfd:create 29 | when: FeatureTemplates['feature-bfd'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-bfd:debug,never 34 | when: FeatureTemplates['feature-bfd'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-bfd,feature-bfd:delete 39 | when: FeatureTemplates['feature-bfd'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-interface.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-interface,feature-interface:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-interface'] }}" 6 | template: sdwan-templates/feature-interface.j2 7 | folder: feature-interface 8 | when: FeatureTemplates['feature-interface'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-interface,feature-interface:create,feature-interface:delete,feature-interface:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-interface'] }}" 15 | template: sdwan-templates/feature-interface.j2 16 | folder: feature-interface 17 | when: FeatureTemplates['feature-interface'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-interface,feature-interface:create 22 | vars: 23 | folder: feature-interface 24 | when: FeatureTemplates['feature-interface'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-interface,feature-interface:create 29 | when: FeatureTemplates['feature-interface'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-interface:debug,never 34 | when: FeatureTemplates['feature-interface'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-interface,feature-interface:delete 39 | when: FeatureTemplates['feature-interface'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-logging.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-logging,feature-logging:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-logging'] }}" 6 | template: sdwan-templates/feature-logging.j2 7 | folder: feature-logging 8 | when: FeatureTemplates['feature-logging'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-logging,feature-logging:create,feature-logging:delete,feature-logging:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-logging'] }}" 15 | template: sdwan-templates/feature-logging.j2 16 | folder: feature-logging 17 | when: FeatureTemplates['feature-logging'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-logging,feature-logging:create 22 | vars: 23 | folder: feature-logging 24 | when: FeatureTemplates['feature-logging'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-logging,feature-logging:create 29 | when: FeatureTemplates['feature-logging'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-logging:debug,never 34 | when: FeatureTemplates['feature-logging'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-logging,feature-logging:delete 39 | when: FeatureTemplates['feature-logging'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-system.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-system,feature-system:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-system'] }}" 6 | template: sdwan-templates/feature-system-vedge.j2 7 | folder: feature-system 8 | when: FeatureTemplates['feature-system'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-system,feature-system:create,feature-system:delete,feature-system:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-system'] }}" 15 | template: sdwan-templates/feature-system-vedge.j2 16 | folder: feature-system 17 | when: FeatureTemplates['feature-system'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-system,feature-system:create 22 | vars: 23 | folder: feature-system 24 | when: FeatureTemplates['feature-system'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-system,feature-system:create 29 | when: FeatureTemplates['feature-system'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-system:debug,never 34 | when: FeatureTemplates['feature-system'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-system,feature-system:delete 39 | when: FeatureTemplates['feature-system'] is defined 40 | -------------------------------------------------------------------------------- /tasks/feature-vpn.yml: -------------------------------------------------------------------------------- 1 | - import_tasks: generic-template-workflow/render.yml 2 | run_once: True 3 | tags: feature-vpn,feature-vpn:render,render 4 | vars: 5 | template_list: "{{ FeatureTemplates['feature-vpn'] }}" 6 | template: sdwan-templates/feature-vpn.j2 7 | folder: feature-vpn 8 | when: FeatureTemplates['feature-vpn'] is defined 9 | 10 | - import_tasks: generic-template-workflow/init-datastructures.yml 11 | run_once: True 12 | tags: feature-vpn,feature-vpn:create,feature-vpn:delete,feature-vpn:debug 13 | vars: 14 | template_list: "{{ FeatureTemplates['feature-vpn'] }}" 15 | template: sdwan-templates/feature-vpn.j2 16 | folder: feature-vpn 17 | when: FeatureTemplates['feature-vpn'] is defined 18 | 19 | - import_tasks: generic-template-workflow/pretty-json.yml 20 | run_once: True 21 | tags: feature-vpn,feature-vpn:create 22 | vars: 23 | folder: feature-vpn 24 | when: FeatureTemplates['feature-vpn'] is defined 25 | 26 | - import_tasks: generic-template-workflow/create-template-aggregate.yml 27 | run_once: True 28 | tags: feature-vpn,feature-vpn:create 29 | when: FeatureTemplates['feature-vpn'] is defined 30 | 31 | - import_tasks: generic-template-workflow/create-template-single.yml 32 | run_once: True 33 | tags: feature-vpn:debug,never 34 | when: FeatureTemplates['feature-vpn'] is defined 35 | 36 | - import_tasks: generic-template-workflow/delete-template-aggregate.yml 37 | run_once: True 38 | tags: feature-vpn,feature-vpn:delete 39 | when: FeatureTemplates['feature-vpn'] is defined 40 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/README.md: -------------------------------------------------------------------------------- 1 | #This task has the goal of representing the generic workflow for ANY feature template. 2 | #This task is not intended to be run directly in a playbook as standalone task. It is supposed to be included from a parent task. 3 | #Examples of parent tasks are the ones specific for a type of feature template, e.g. banner. 4 | #Once a parent tasks includes this generic task some variables have to be passed, e.g. loop_over,folder and template. 5 | # 6 | #template_list: list of templates to be manipulated 7 | #folder: where to store the json file of the rendered template 8 | #template: jinja template to be used to render the final template 9 | # 10 | #At high level this tasks first removes all the previously rendered templates from the filesystem, then it renders the 11 | #new ones and saves them in $folder. The tasks renders only the template with state present. We then load from 12 | #filesystem the templates that we have just rendered, this is due to the fact that the template module is not able to 13 | #generate facts with the rendered content. On the other side this enables multi-staged actions, meaning that we can perform 14 | #the rendering and the upload at different phases. Once we have loaded the files we then pass them as input to the ansible module 15 | #using the aggregate parameter for bulk operations. 16 | # 17 | #There are also tasks that create a list of templates with the state absent. We finally pass this list to the ansible module. 18 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/create-template-aggregate.yml: -------------------------------------------------------------------------------- 1 | - name: Interact with the vManage to create templates 2 | vmanage_feature_template: 3 | user: "{{vmanage_user}}" 4 | host: "{{vmanage_host}}" 5 | password: "{{vmanage_password}}" 6 | state: present 7 | aggregate: "{{present_template_list}}" 8 | when: present_template_list 9 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/create-template-single.yml: -------------------------------------------------------------------------------- 1 | - vmanage_feature_template: 2 | user: "{{vmanage_user}}" 3 | host: "{{vmanage_host}}" 4 | password: "{{vmanage_password}}" 5 | state: present 6 | name: "{{ item.templateName }}" 7 | description: "{{ item.templateDescription }}" 8 | definition: "{{ item.templateDefinition }}" 9 | template_type: "{{ item.templateType }}" 10 | device_type: "{{ item.deviceType }}" 11 | template_min_version: "{{ item.templateMinVersion }}" 12 | factory_default: "{{ item.factoryDefault }}" 13 | loop: "{{present_template_list}}" 14 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/delete-template-aggregate.yml: -------------------------------------------------------------------------------- 1 | - name: Interact with the vManage to delete templates 2 | vmanage_feature_template: 3 | user: "{{vmanage_user}}" 4 | host: "{{vmanage_host}}" 5 | password: "{{vmanage_password}}" 6 | state: absent 7 | aggregate: "{{absent_template_list}}" 8 | when: absent_template_list 9 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/init-datastructures.yml: -------------------------------------------------------------------------------- 1 | - name: Loading templates from filesystem 2 | set_fact: 3 | generic_template: "{{lookup('file', '{{item}}') }}" 4 | with_fileglob: 5 | - "{{role_path}}/files/rendered_templates/{{ folder }}/*.json" 6 | register: generic_template_registered_list 7 | 8 | - name: Creating a list of templates 9 | set_fact: 10 | present_template_list: "{{ generic_template_registered_list.results | map(attribute = 'ansible_facts.generic_template') | list }}" 11 | 12 | - name: Creating an empty list of templates to be removed 13 | set_fact: 14 | absent_template_list: [] 15 | no_log: True 16 | 17 | - name: Creating a list of templates to be removed 18 | set_fact: 19 | absent_template_list: "{{ absent_template_list|default([]) + [ {'templateName': item.templateName } ] }}" 20 | when: item.state == "absent" 21 | with_items: "{{ template_list }}" 22 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/pretty-json.yml: -------------------------------------------------------------------------------- 1 | - name: Pretty json 2 | copy: 3 | content: "{{ lookup('file',item) | from_json | to_nice_json }}" 4 | dest: "{{item}}" 5 | with_fileglob: 6 | - "{{role_path}}/files/rendered_templates/{{ folder }}/*.json" 7 | -------------------------------------------------------------------------------- /tasks/generic-template-workflow/render.yml: -------------------------------------------------------------------------------- 1 | - name: Making sure appropiate directories are there 2 | file: 3 | path: "{{item}}" 4 | state: directory 5 | run_once: True 6 | with_items: 7 | - "{{role_path}}/files/rendered_templates/" 8 | - "{{role_path}}/files/rendered_templates/{{ folder }}" 9 | 10 | - name: Clean rendered templates 11 | file: 12 | path: "{{item}}" 13 | state: absent 14 | force: true 15 | run_once: True 16 | with_fileglob: 17 | - "{{role_path}}/files/rendered_templates/{{ folder }}/*" 18 | 19 | - name: Render template 20 | template: src={{ item.template | default(template) }} dest={{role_path}}/files/rendered_templates/{{ folder }}/{{ item.templateName }}.json 21 | when: item.state != "ignore" and item.state != "absent" 22 | with_items: "{{ template_list }}" 23 | -------------------------------------------------------------------------------- /tasks/get-cookie.yml: -------------------------------------------------------------------------------- 1 | - name: Check for a cookie file 2 | stat: 3 | path: "{{ viptela_api_cookie_file }}" 4 | register: stat_result 5 | delegate_to: localhost 6 | 7 | - set_fact: 8 | viptela_api_cookie: "{{ lookup('file', '{{ viptela_api_cookie_file }}') }}" 9 | when: stat_result.stat.exists 10 | 11 | - name: Check Cookie 12 | uri: 13 | url: "https://{{ vmanage_ip }}/dataservice/settings/clientSessionTimeout" 14 | method: GET 15 | headers: 16 | Cookie: "{{ viptela_api_cookie }}" 17 | return_content: yes 18 | validate_certs: "{{ validate_certs }}" 19 | no_log: false # Don't show output as your password will be on the URI string 20 | register: login_results 21 | failed_when: false 22 | delegate_to: localhost 23 | when: viptela_api_cookie is defined 24 | retries: 60 25 | delay: 10 26 | until: login_results is not failed 27 | 28 | - name: Get Cookie 29 | uri: 30 | url: "https://{{ vmanage_ip }}/j_security_check" 31 | method: POST 32 | body: 33 | j_username: "{{ viptela_api_username }}" 34 | j_password: "{{ viptela_api_password }}" 35 | body_format: form-urlencoded 36 | return_content: yes 37 | validate_certs: "{{ validate_certs }}" 38 | no_log: false # Don't show output as your password will be on the URI string 39 | register: login_results 40 | delegate_to: localhost 41 | until: login_results.set_cookie is defined 42 | when: (viptela_api_cookie is not defined) or (login_results.msg is not search('OK')) 43 | retries: 60 44 | delay: 10 45 | until: login_results is not failed 46 | 47 | - set_fact: 48 | viptela_api_cookie: "{{ login_results.set_cookie }}" 49 | when: login_results.set_cookie is defined 50 | 51 | - copy: 52 | content: "{{ viptela_api_cookie }}" 53 | dest: "{{ viptela_api_cookie_file }}" 54 | delegate_to: localhost 55 | when: login_results.set_cookie is defined 56 | -------------------------------------------------------------------------------- /tasks/get-csr.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: Check to see if CSR exists 5 | stat: 6 | path: "{{ csr_filename }}" 7 | register: stat_result 8 | delegate_to: localhost 9 | 10 | - name: Get our deviceIP by hostname 11 | uri: 12 | url: "https://{{ vmanage_ip }}/dataservice/system/device/controllers" 13 | method: GET 14 | headers: 15 | Cookie: "{{ viptela_api_cookie }}" 16 | validate_certs: "{{ validate_certs }}" 17 | return_content: yes 18 | register: uri_results 19 | delegate_to: localhost 20 | retries: 60 21 | delay: 10 22 | until: uri_results is not failed 23 | 24 | - set_fact: 25 | found_device_ip: "{{ uri_results.json.data | selectattr('host-name', 'defined') | selectattr('host-name', 'equalto', device_hostname) | map(attribute='deviceIP') | join('') }}" 26 | 27 | - set_fact: 28 | csr_device_ip: "{{ found_device_ip if found_device_ip else device_ip }}" 29 | 30 | - debug: msg="Getting CSR for deviceIP {{ csr_device_ip }}" 31 | 32 | - block: 33 | - name: Get CSR 34 | uri: 35 | url: "https://{{ vmanage_ip }}/dataservice/certificate/generate/csr" 36 | method: POST 37 | headers: 38 | Cookie: "{{ viptela_api_cookie }}" 39 | body_format: json 40 | body: 41 | deviceIP: "{{ csr_device_ip }}" 42 | validate_certs: "{{ validate_certs }}" 43 | return_content: yes 44 | register: uri_results 45 | changed_when: true 46 | delegate_to: localhost 47 | retries: 60 48 | delay: 10 49 | until: uri_results is not failed 50 | 51 | - set_fact: 52 | device_csr: "{{ uri_results.json.data[0].deviceCSR }}" 53 | when: uri_results.json.data[0] is defined 54 | 55 | - copy: 56 | content: "{{ device_csr }}" 57 | dest: "{{ csr_filename }}" 58 | delegate_to: localhost 59 | when: not stat_result.stat.exists -------------------------------------------------------------------------------- /tasks/install-cert.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: Install Certificate 5 | uri: 6 | url: "https://{{ vmanage_ip }}/dataservice/certificate/install/signedCert" 7 | method: POST 8 | headers: 9 | Cookie: "{{ viptela_api_cookie }}" 10 | body_format: json 11 | body: 12 | "{{ device_cert }}" 13 | validate_certs: "{{ validate_certs }}" 14 | return_content: yes 15 | register: uri_results 16 | delegate_to: localhost 17 | retries: 60 18 | delay: 10 19 | until: uri_results is not failed -------------------------------------------------------------------------------- /tasks/install-serials.yml: -------------------------------------------------------------------------------- 1 | #- include_tasks: get-cookie.yml 2 | # when: viptela_api_cookie is not defined 3 | 4 | - name: Check for the serial number file 5 | stat: 6 | path: "{{ serial_number_file }}" 7 | register: stat_result 8 | delegate_to: localhost 9 | 10 | # Ansible URI module does not support multi-part at the time this was tested 11 | #- name: Upload Serial File 12 | # uri: 13 | # url: "https://{{ hostvars['vmanage1'].ansible_host }}/dataservice/system/device/fileupload" 14 | # method: POST 15 | # headers: 16 | # Cookie: "{{ viptela_api_cookie }}" 17 | # Content-Type: application/octet-stream 18 | # src: "{{ viptela_serial_file }}" 19 | # validate_certs: "{{ validate_certs }}" 20 | # register: uri_results 21 | # delegate_to: localhost 22 | # 23 | #- debug: var=uri_results 24 | 25 | - name: Install Serial File 26 | shell: curl --insecure -F "file=@{{ serial_number_file }}" -F "validity=valid" -F "upload=true" --cookie "{{ viptela_api_cookie }}" https://{{ vmanage_ip }}/dataservice/system/device/fileupload 27 | args: 28 | warn: false 29 | when: stat_result.stat.exists 30 | register: command_results 31 | delegate_to: localhost 32 | 33 | #- debug: var=command_results -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # - include_tasks: facts.yml 3 | -------------------------------------------------------------------------------- /tasks/ping-cedge.yml: -------------------------------------------------------------------------------- 1 | - ios_ping: 2 | provider: 3 | host: "{{ hostvars[ping_vedge].ansible_host }}" 4 | username: admin 5 | password: admin 6 | source: "{{ ping_src_ip }}" 7 | dest: "{{ ping_dst_ip }}" 8 | vrf: "{{ ping_vpn }}" 9 | state: "{{ 'present' if ping_pass else 'absent' }}" 10 | connection: network_cli 11 | delegate_to: localhost 12 | register: ping 13 | 14 | - set_fact: 15 | ping_rx: "{{ ping.packets_rx }}" 16 | ping_tx: "{{ ping.packets_tx }}" 17 | ping_loss: "{{ ping.packet_loss }}" 18 | ping_rtt_min: "{{ ping.rtt.min }}" 19 | ping_rtt_max: "{{ ping.rtt.max }}" 20 | ping_rtt_avg: "{{ ping.rtt.avg }}" -------------------------------------------------------------------------------- /tasks/ping-test.yml: -------------------------------------------------------------------------------- 1 | - set_fact: 2 | edge_type: "{{ 'vedge' if hostvars[ping_vedge].sdwan_model is regex('^vedge-[c0-9]') else 'cedge' }}" 3 | 4 | - include_tasks: 5 | file: "ping-{{ edge_type }}.yml" 6 | 7 | - debug: 8 | msg: "{{ ping_vedge }}(VPN {{ping_vpn}}) => {{ ping_dst_ip }}: {{ ping_rx }}/{{ ping_tx }}, {{ ping_loss }} loss, Pass(actual/expected) {{ actual_result }}/{{ expected_result }}" 9 | failed_when: actual_result != expected_result 10 | ignore_errors: False 11 | vars: 12 | actual_result: "{{ True if ping_loss != '100%' else False }}" 13 | expected_result: "{{ ping_pass }}" 14 | -------------------------------------------------------------------------------- /tasks/ping-vedge.yml: -------------------------------------------------------------------------------- 1 | - vmanage_nping: 2 | user: "{{ vmanage_user }}" 3 | host: "{{ vmanage_ip }}" 4 | password: "{{ vmanage_pass }}" 5 | dst_ip: "{{ ping_dst_ip }}" 6 | vedge: "{{ ping_vedge }}" 7 | vpn: "{{ ping_vpn }}" 8 | count: "{{ ping_count | default(5) }}" 9 | rapid: no 10 | register: nping 11 | delegate_to: localhost 12 | 13 | - set_fact: 14 | ping_tx: "{{ nping.json.packetsTransmitted }}" 15 | # ping_rx: "{{ nping.json.packetsReceived }}" 16 | ping_rx: "{{ nping.json.rawOutput | select('search', 'Echo reply') | list | length | default('0') }}" 17 | ping_loss: "{{ nping.json.lossPercentage }}" 18 | ping_rtt_min: "{{ nping.json.minRoundTrip }}" 19 | ping_rtt_max: "{{ nping.json.maxRoundTrip }}" 20 | ping_rtt_avg: "{{ nping.json.avgRoundTrip }}" 21 | 22 | 23 | - set_fact: 24 | ping_loss: "{{ (100 - (100 * (ping_rx|int / ping_tx|int))) | int | string }}%" 25 | -------------------------------------------------------------------------------- /tasks/push-certs.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: Push Certificates 5 | uri: 6 | url: "https://{{ vmanage_ip }}/dataservice/certificate/vedge/list?action=push" 7 | method: POST 8 | headers: 9 | Cookie: "{{ viptela_api_cookie }}" 10 | validate_certs: "{{ validate_certs }}" 11 | return_content: yes 12 | register: uri_results 13 | delegate_to: localhost 14 | retries: 60 15 | delay: 10 16 | until: uri_results is not failed -------------------------------------------------------------------------------- /tasks/set-org.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: Get current Organization 5 | uri: 6 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/organization" 7 | method: GET 8 | headers: 9 | Cookie: "{{ viptela_api_cookie }}" 10 | validate_certs: "{{ validate_certs }}" 11 | return_content: yes 12 | register: uri_results 13 | delegate_to: localhost 14 | retries: 60 15 | delay: 10 16 | until: uri_results is not failed 17 | 18 | - set_fact: 19 | current_org_name: "{{ uri_results.json.data[0].org | default('') }}" 20 | 21 | - name: Set Organization 22 | uri: 23 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/organization" 24 | method: PUT 25 | headers: 26 | Cookie: "{{ viptela_api_cookie }}" 27 | body_format: json 28 | body: 29 | org: "{{ org_name }}" 30 | validate_certs: "{{ validate_certs }}" 31 | return_content: yes 32 | register: uri_results 33 | delegate_to: localhost 34 | changed_when: true 35 | when: current_org_name != org_name 36 | retries: 60 37 | delay: 10 38 | until: uri_results is not failed -------------------------------------------------------------------------------- /tasks/set-rootca.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | # - name: See if the vBond is already set 5 | # uri: 6 | # url: "https://{{ hostvars['vmanage1'].ansible_host }}/dataservice/settings/configuration/device" 7 | # method: GET 8 | # headers: 9 | # Cookie: "{{ viptela_api_cookie }}" 10 | # validate_certs: "{{ validate_certs }}" 11 | # return_content: yes 12 | # register: uri_results 13 | # delegate_to: localhost 14 | # 15 | # - set_fact: 16 | # current_vbond_ip: "{{ uri_results.json.data[0].domainIp }}" 17 | # current_vbond_port: "{{ uri_results.json.data[0].port }}" 18 | # when: uri_results.json.data[0] is defined 19 | 20 | - name: Get CA Type 21 | uri: 22 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/certificate" 23 | method: GET 24 | headers: 25 | Cookie: "{{ viptela_api_cookie }}" 26 | validate_certs: "{{ validate_certs }}" 27 | return_content: yes 28 | register: uri_results 29 | delegate_to: localhost 30 | retries: 60 31 | delay: 10 32 | until: uri_results is not failed 33 | 34 | - name: Set CA Type 35 | uri: 36 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/certificate" 37 | method: POST 38 | headers: 39 | Cookie: "{{ viptela_api_cookie }}" 40 | body_format: json 41 | body: 42 | certificateSigning: enterprise 43 | challengeAvailable: false 44 | validate_certs: "{{ validate_certs }}" 45 | return_content: yes 46 | register: uri_results 47 | delegate_to: localhost 48 | retries: 60 49 | delay: 10 50 | until: uri_results is not failed 51 | 52 | - name: Get CA Cert 53 | uri: 54 | url: "https://{{ vmanage_ip }}/dataservice/certificate/rootcertificate" 55 | method: GET 56 | headers: 57 | Cookie: "{{ viptela_api_cookie }}" 58 | validate_certs: "{{ validate_certs }}" 59 | return_content: yes 60 | register: uri_results 61 | delegate_to: localhost 62 | retries: 60 63 | delay: 10 64 | until: uri_results is not failed 65 | 66 | - name: Set CA Cert 67 | uri: 68 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/certificate/enterpriserootca" 69 | method: PUT 70 | headers: 71 | Cookie: "{{ viptela_api_cookie }}" 72 | body_format: json 73 | body: 74 | enterpriseRootCA: "{{ root_cert }}" 75 | validate_certs: "{{ validate_certs }}" 76 | return_content: yes 77 | register: uri_results 78 | delegate_to: localhost 79 | retries: 60 80 | delay: 10 81 | until: uri_results is not failed 82 | 83 | -------------------------------------------------------------------------------- /tasks/set-vbond.yml: -------------------------------------------------------------------------------- 1 | - include_tasks: get-cookie.yml 2 | when: viptela_api_cookie is not defined 3 | 4 | - name: See if the vBond is already set 5 | uri: 6 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/device" 7 | method: GET 8 | headers: 9 | Cookie: "{{ viptela_api_cookie }}" 10 | validate_certs: "{{ validate_certs }}" 11 | return_content: yes 12 | register: uri_results 13 | delegate_to: localhost 14 | retries: 60 15 | 16 | - set_fact: 17 | current_vbond_ip: "{{ uri_results.json.data[0].domainIp | default('') }}" 18 | current_vbond_port: "{{ uri_results.json.data[0].port | default('') }}" 19 | 20 | - name: Set vBond 21 | uri: 22 | url: "https://{{ vmanage_ip }}/dataservice/settings/configuration/device" 23 | method: PUT 24 | headers: 25 | Cookie: "{{ viptela_api_cookie }}" 26 | body_format: json 27 | body: 28 | domainIp: "{{ vbond_ip }}" 29 | port: "{{ vbond_port | default('12346') }}" 30 | validate_certs: "{{ validate_certs }}" 31 | return_content: yes 32 | register: uri_results 33 | changed_when: true 34 | when: current_vbond_ip != vbond_ip 35 | delegate_to: localhost 36 | retries: 60 -------------------------------------------------------------------------------- /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - tower_api -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for tower_api --------------------------------------------------------------------------------