From 74a52bf7fa7f83f23f526540c24bc31a36c360dc Mon Sep 17 00:00:00 2001 From: ssrish Date: Wed, 20 Jan 2021 10:31:14 -0800 Subject: [PATCH 01/49] retry w/o conflict --- plugins/module_utils/aci.py | 105 +++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index aa96eedde..609516b8a 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -42,11 +42,13 @@ import base64 import json import os +import inspect from copy import deepcopy from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_bytes, to_native from ansible.module_utils.basic import env_fallback +from ansible.module_utils.connection import Connection # Optional, only used for APIC signature-based authentication try: @@ -85,12 +87,16 @@ def aci_argument_spec(): return dict( +<<<<<<< HEAD host=dict( type="str", required=True, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"]), ), +======= + host=dict(type="str", required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"])), +>>>>>>> 6341500 (retry w/o conflict) port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])), username=dict( type="str", @@ -337,17 +343,19 @@ def __init__(self, module): self.module.warn("Enable debug output because ANSIBLE_DEBUG was set.") self.params["output_level"] = "debug" - if self.params.get("private_key"): - # Perform signature-based authentication, no need to log on separately - if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL: - self.module.fail_json(msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") - elif self.params.get("password") is not None: - self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") - elif self.params.get("password"): - # Perform password-based authentication, log on using password - self.login() - else: - self.module.fail_json(msg="Either parameter 'password' or 'private_key' is required for authentication") + if self.module._socket_path is None: + if self.params.get("private_key"): + # Perform signature-based authentication, no need to log on separately + if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL: + self.module.fail_json( + msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") + elif self.params.get("password") is not None: + self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") + elif self.params.get("password"): + # Perform password-based authentication, log on using password + self.login() + else: + self.module.fail_json(msg="Either parameter 'password' or 'private_key' is required for authentication") def boolean(self, value, true="yes", false="no"): """Return an acceptable value back""" @@ -499,7 +507,10 @@ def cert_auth(self, path=None, payload="", method=None): def response_json(self, rawoutput): """Handle APIC JSON response output""" try: - jsondata = json.loads(rawoutput) + if isinstance(rawoutput, dict): + jsondata = rawoutput + else: + jsondata = json.loads(rawoutput) except Exception as e: # Expose RAW output for troubleshooting self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e) @@ -1235,9 +1246,9 @@ def delete_config(self): elif not self.module.check_mode: # Sign and encode request as to APIC's wishes - if self.params["private_key"]: - self.cert_auth(method="DELETE") + self.call("DELETE") +<<<<<<< HEAD resp, info = fetch_url( self.module, self.url, @@ -1263,6 +1274,8 @@ def delete_config(self): except KeyError: # Connection error self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) +======= +>>>>>>> 6341500 (retry w/o conflict) else: self.result["changed"] = True self.method = "DELETE" @@ -1386,6 +1399,7 @@ def get_existing(self): that this method can be used to supply the existing configuration when using the get_diff method. The response, status, and existing configuration will be added to the self.result dictionary. """ +<<<<<<< HEAD uri = self.url + self.filter_string # Sign and encode request as to APIC's wishes @@ -1415,6 +1429,9 @@ def get_existing(self): except KeyError: # Connection error self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) +======= + self.call("GET") +>>>>>>> 6341500 (retry w/o conflict) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1534,6 +1551,7 @@ def post_config(self, parent_class=None): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes +<<<<<<< HEAD url = self.url if parent_class is not None: if self.params.get("port") is not None: @@ -1570,6 +1588,9 @@ def post_config(self, parent_class=None): except KeyError: # Connection error self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) +======= + self.call("POST") +>>>>>>> da2af7d (retry w/o conflict) else: self.result["changed"] = True self.method = "POST" @@ -1680,6 +1701,7 @@ def dump_json(self): if self.result.get("changed") is True: json.dump([mo], output_file) +<<<<<<< HEAD def delete_config_request(self, path): self._config_request(path, "absent") self.result["changed"] = True @@ -1707,3 +1729,58 @@ def ospf_spec(): multipod_internal=dict(type="str", choices=["no", "yes"]), name_alias=dict(type="str"), ) +======= + def call(self, method): + if method == 'GET': + call_path = self.path + self.filter_string + call_url = self.url + self.filter_string + data = None + elif method == 'POST': + call_path = self.path + call_url = self.url + data = json.dumps(self.config) + elif method == 'DELETE': + call_path = self.path + call_url = self.url + data = None + resp = None + if self.module._socket_path: + conn = Connection(self.module._socket_path) + info = conn.send_request(method, '/' + call_path, data) + else: + if self.params.get('private_key'): + self.cert_auth(method=method, path=call_path, payload=data) + resp, info = fetch_url(self.module, call_url, + data=data, + headers=self.headers, + method=method, + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) + self.response = info.get('msg') + self.status = info.get('status') + self.method = method + + # Handle APIC response + if info.get('status') == 200: + if method == 'POST' or method == 'DELETE': + self.result['changed'] = True + try: + if method == 'GET': + self.existing = json.loads(resp.read())['imdata'] + else: + self.response_json(resp.read()) + except AttributeError: + if method == 'GET': + self.existing = info['body']['imdata'] + else: + self.response_json(info) + else: + try: + # APIC error + self.response_json(info['body']) + self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + except KeyError: + # Connection error + self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) + +>>>>>>> da2af7d (retry w/o conflict) From ec938f95eb3dd7d92b35e6555972d1acfd91738f Mon Sep 17 00:00:00 2001 From: ssrish Date: Wed, 20 Jan 2021 13:05:06 -0800 Subject: [PATCH 02/49] Added plugin option to aci_rest --- plugins/httpapi/aci.py | 177 ++++++++++++++++++ plugins/modules/aci_rest.py | 28 ++- .../targets/aci_rest/tasks/error_handling.yml | 5 + 3 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 plugins/httpapi/aci.py diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py new file mode 100644 index 000000000..f40372c9f --- /dev/null +++ b/plugins/httpapi/aci.py @@ -0,0 +1,177 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = """ +--- +author: +- Lionel Hercot (lhercot) +- Shreyas Srish (shrsr) +httpapi: aci +short_description: Ansible ACI HTTPAPI Plugin. +description: + - This APIC plugin provides the HTTPAPI transport methods needed to initiate + a connection to the ACI controller, send API requests and process the + response from the controller. +version_added: "2.1.0" +""" + +import base64 +import json +import os +import collections +import requests +import sys +import re +import traceback +from copy import deepcopy + +from ansible.module_utils._text import to_text +from ansible.module_utils.connection import ConnectionError +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list +from ansible.plugins.httpapi import HttpApiBase + +#from ansible_collections.ansible.netcommon.plugins.connection import httpapi +from ansible.playbook.play_context import PlayContext +from ansible.playbook import play_context +from ansible.parsing.dataloader import DataLoader +from ansible.vars.manager import VariableManager +from ansible.inventory.manager import InventoryManager +from ansible.plugins.loader import httpapi_loader +from ansible.module_utils._text import to_bytes, to_native + + +class HttpApi(HttpApiBase): + + def __init__(self, *args, **kwargs): + super(HttpApi, self).__init__(*args, **kwargs) + self.headers = { + 'Content-Type': "application/json" + } + + def handle_httperror(self, exc): + return self._return_error(exc.code, exc) + + def login(self, username, password): + ''' Log in to APIC ''' + # Perform login request + method = 'POST' + path = '/api/aaaLogin.json' + if username is None or password is None: + raise ConnectionError("Invalid username/password") + payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} + # timeout = self.connection.get_option("persistent_connect_timeout") * 1000 + # data = "{'expirationTime': %s}" % timeout + data = json.dumps(payload) + #raise ConnectionError(self.connection.get_option("network_os")) + #host = self.connection.get_option("host") + try: + response, response_data = self.connection.send(path, data, method=method, headers=self.headers, timeout=10) + response_value = self._get_response_value(response_data) + self.connection._auth = {'Cookie': 'APIC-Cookie={0}' + .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} + + except Exception as e: + msg = 'Error on attempt to connect and authenticate with user {0} to APIC: {1} '.format(username, e) + + def logout(self): + method = 'POST' + path = '/api/aaaLogout.json' + + try: + response, response_data = self.connection.send(path, {}, method=method, headers=self.headers, force_basic_auth=True) + except Exception as e: + msg = 'Error on attempt to logout from APIC controller: {0}'.format(e) + raise ConnectionError(self._return_info(None, method, path, msg)) + + self._verify_response(response, method, path, response_data, rest_type='json') + # Clean up tokens + self.connection._auth = None + + def send_request(self, method, path, json=None): + ''' This method handles all APIC REST API requests other then login ''' + if json is None: + json = {} + #self.connection.set_option("host","10.23.248.116") + try: + # Perform some very basic path input validation. + path = str(path) + if path[0] != '/': + msg = 'Value of does not appear to be formated properly' + raise ConnectionError(self._return_info(None, method, path, msg)) + response, rdata = self.connection.send(path, json, method=method, + headers=self.headers, + force_basic_auth=True) + if path.find('.json') != -1: + return self._verify_response(response, method, path, rdata, rest_type='json') + else: + return self._verify_response(response, method, path, rdata, rest_type='xml') + + except Exception as e: + pass + # TO DO + + def _verify_response(self, response, method, path, rdata, rest_type): + ''' Process the return code and response object from APIC ''' + resp_value = self._get_response_value(rdata) + if rest_type == 'json': + respond_data = self._response_to_json(resp_value) + else: + respond_data = resp_value + response_code = response.getcode() + path = response.geturl() + msg = response.msg + # Handle APIC response + if response_code == 200: + return self._return_info(response_code, method, path, msg, respond_data) + else: + # Never Reaches Here because of httperror exception in httpapi module + pass + + def _get_response_value(self, response_data): + ''' Extract string data from response_data returned from APIC ''' + return to_text(response_data.getvalue()) + + def _response_to_json(self, response_text): + ''' Convert response_text to json format ''' + try: + return json.loads(response_text) if response_text else {} + # JSONDecodeError only available on Python 3.5+ + except ValueError: + return 'Invalid JSON response: {0}'.format(response_text) + + def _return_info(self, response_code, method, path, msg, respond_data=None): + ''' Format success/error data and return with consistent format ''' + + info = {} + info['status'] = response_code + info['method'] = method + info['url'] = path + info['msg'] = msg + info['body'] = respond_data + + return info + + def _return_error(self, code, response): + error = {} + error['status'] = code + # error['method'] = method + # error['url'] = path + # error['msg'] = msg + error['body'] = response + + return error diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 67428e4f9..16e0356b9 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -281,6 +281,7 @@ from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_text +from ansible.module_utils.connection import Connection def update_qsl(url, params): @@ -390,11 +391,19 @@ def main(): elif rest_type == "xml" and HAS_LXML_ETREE: if content and isinstance(content, dict) and HAS_XMLJSON_COBRA: # Validate inline YAML/JSON +<<<<<<< HEAD payload = etree.tostring(cobra.etree(payload)[0], encoding="unicode") elif payload and isinstance(payload, str): try: # Validate XML string payload = etree.tostring(etree.fromstring(payload), encoding="unicode") +======= + payload = etree.tostring(cobra.etree(payload)[0], encoding='unicode') + elif payload and isinstance(payload, str): + try: + # Validate XML string + payload = etree.tostring(etree.fromstring(payload), encoding='unicode') +>>>>>>> 1620e2b (Added plugin option to aci_rest) except Exception as e: module.fail_json(msg="Failed to parse provided XML payload: %s" % to_text(e), payload=payload) @@ -414,9 +423,17 @@ def main(): aci.method = aci.params.get("method").upper() # Perform request - resp, info = fetch_url( - module, aci.url, data=payload, headers=aci.headers, method=aci.method, timeout=aci.params.get("timeout"), use_proxy=aci.params.get("use_proxy") - ) + resp = None + if module._socket_path: + conn = Connection(aci.module._socket_path) + info = conn.send_request(aci.method, '/' + path, payload) + else: + resp, info = fetch_url(module, aci.url, + data=payload, + headers=aci.headers, + method=aci.method, + timeout=aci.params.get('timeout'), + use_proxy=aci.params.get('use_proxy')) aci.response = info.get("msg") aci.status = info.get("status") @@ -431,7 +448,10 @@ def main(): # Connection error aci.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) - aci.response_type(resp.read(), rest_type) + try: + aci.response_type(resp.read(), rest_type) + except AttributeError: + aci.response_type(info.get('body'), rest_type) aci.result["status"] = aci.status aci.result["imdata"] = aci.imdata diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index 1c503c5f2..f748f713f 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -20,7 +20,12 @@ fvTenant: attributes: name: ansible_test +<<<<<<< HEAD ignore_errors: true +======= + ignore_errors: yes + delegate_to: localhost +>>>>>>> 1620e2b (Added plugin option to aci_rest) register: error_on_name_resolution - name: Verify error_on_name_resolution From 96022f30f0b11e315cc7fddd022501c19da10b86 Mon Sep 17 00:00:00 2001 From: ssrish Date: Wed, 20 Jan 2021 14:44:09 -0800 Subject: [PATCH 03/49] Passes error handling of aci_rest test case --- plugins/httpapi/aci.py | 76 +++++-------------- .../targets/aci_rest/tasks/error_handling.yml | 2 +- 2 files changed, 21 insertions(+), 57 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index f40372c9f..fa89057ec 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -45,15 +45,6 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list from ansible.plugins.httpapi import HttpApiBase -#from ansible_collections.ansible.netcommon.plugins.connection import httpapi -from ansible.playbook.play_context import PlayContext -from ansible.playbook import play_context -from ansible.parsing.dataloader import DataLoader -from ansible.vars.manager import VariableManager -from ansible.inventory.manager import InventoryManager -from ansible.plugins.loader import httpapi_loader -from ansible.module_utils._text import to_bytes, to_native - class HttpApi(HttpApiBase): @@ -62,23 +53,14 @@ def __init__(self, *args, **kwargs): self.headers = { 'Content-Type': "application/json" } - - def handle_httperror(self, exc): - return self._return_error(exc.code, exc) - + def login(self, username, password): ''' Log in to APIC ''' # Perform login request method = 'POST' path = '/api/aaaLogin.json' - if username is None or password is None: - raise ConnectionError("Invalid username/password") payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} - # timeout = self.connection.get_option("persistent_connect_timeout") * 1000 - # data = "{'expirationTime': %s}" % timeout data = json.dumps(payload) - #raise ConnectionError(self.connection.get_option("network_os")) - #host = self.connection.get_option("host") try: response, response_data = self.connection.send(path, data, method=method, headers=self.headers, timeout=10) response_value = self._get_response_value(response_data) @@ -86,7 +68,8 @@ def login(self, username, password): .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} except Exception as e: - msg = 'Error on attempt to connect and authenticate with user {0} to APIC: {1} '.format(username, e) + msg = 'Error on attempt to connect and authenticate with user: {0} to APIC: {1}. {2} '.format(username, self.connection.get_option("host"), e) + raise ConnectionError(msg) def logout(self): method = 'POST' @@ -95,7 +78,7 @@ def logout(self): try: response, response_data = self.connection.send(path, {}, method=method, headers=self.headers, force_basic_auth=True) except Exception as e: - msg = 'Error on attempt to logout from APIC controller: {0}'.format(e) + msg = 'Error on attempt to logout from APIC. {0}'.format(e) raise ConnectionError(self._return_info(None, method, path, msg)) self._verify_response(response, method, path, response_data, rest_type='json') @@ -106,24 +89,20 @@ def send_request(self, method, path, json=None): ''' This method handles all APIC REST API requests other then login ''' if json is None: json = {} - #self.connection.set_option("host","10.23.248.116") - try: + # Perform some very basic path input validation. - path = str(path) - if path[0] != '/': - msg = 'Value of does not appear to be formated properly' - raise ConnectionError(self._return_info(None, method, path, msg)) - response, rdata = self.connection.send(path, json, method=method, - headers=self.headers, - force_basic_auth=True) - if path.find('.json') != -1: - return self._verify_response(response, method, path, rdata, rest_type='json') - else: - return self._verify_response(response, method, path, rdata, rest_type='xml') - - except Exception as e: - pass - # TO DO + path = str(path) + if path[0] != '/': + msg = 'Value of does not appear to be formated properly' + raise ConnectionError(self._return_info(None, method, path, msg)) + response, rdata = self.connection.send(path, json, method=method, + headers=self.headers, + force_basic_auth=True) + + if path.find('.json') != -1: + return self._verify_response(response, method, path, rdata, rest_type='json') + else: + return self._verify_response(response, method, path, rdata, rest_type='xml') def _verify_response(self, response, method, path, rdata, rest_type): ''' Process the return code and response object from APIC ''' @@ -134,13 +113,8 @@ def _verify_response(self, response, method, path, rdata, rest_type): respond_data = resp_value response_code = response.getcode() path = response.geturl() - msg = response.msg - # Handle APIC response - if response_code == 200: - return self._return_info(response_code, method, path, msg, respond_data) - else: - # Never Reaches Here because of httperror exception in httpapi module - pass + msg = str(response) + return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): ''' Extract string data from response_data returned from APIC ''' @@ -164,14 +138,4 @@ def _return_info(self, response_code, method, path, msg, respond_data=None): info['msg'] = msg info['body'] = respond_data - return info - - def _return_error(self, code, response): - error = {} - error['status'] = code - # error['method'] = method - # error['url'] = path - # error['msg'] = msg - error['body'] = response - - return error + return info \ No newline at end of file diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index f748f713f..cb8e3f0a3 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -122,7 +122,7 @@ that: - error_on_input_validation is failed - error_on_input_validation.method == 'POST' - - "error_on_input_validation.msg == 'APIC Error 801: property descr of tn-ansible_test failed validation for value \\'This is an [invalid] description\\''" + - "error_on_input_validation.msg == 'APIC Error 801: property descr of uni/tn-ansible_test failed validation for value \\'This is an [invalid] description\\''" - 'error_on_input_validation.response == "HTTP Error 400: Bad Request"' - error_on_input_validation.status == 400 - "'current' not in error_on_input_validation" From a3f9b5e2bfe9552b683b22277d7caddefe5dad43 Mon Sep 17 00:00:00 2001 From: ssrish Date: Thu, 21 Jan 2021 23:13:03 -0800 Subject: [PATCH 04/49] Add private key in plugin --- plugins/httpapi/aci.py | 15 ++++++++-- plugins/module_utils/aci.py | 29 ++++++++++--------- plugins/modules/aci_rest.py | 2 +- .../targets/aci_rest/tasks/error_handling.yml | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index fa89057ec..afe85a8e0 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -53,9 +53,14 @@ def __init__(self, *args, **kwargs): self.headers = { 'Content-Type': "application/json" } + self.auth = None + + def get_auth(self, auth): + self.auth = auth def login(self, username, password): ''' Log in to APIC ''' + # Perform login request method = 'POST' path = '/api/aaaLogin.json' @@ -89,15 +94,19 @@ def send_request(self, method, path, json=None): ''' This method handles all APIC REST API requests other then login ''' if json is None: json = {} + + if self.auth is not None: + self.connection._auth = {'Cookie': '{0}' + .format(self.auth)} - # Perform some very basic path input validation. + # Perform some very basic path input validation. path = str(path) if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) + response, rdata = self.connection.send(path, json, method=method, - headers=self.headers, - force_basic_auth=True) + headers=self.headers) if path.find('.json') != -1: return self._verify_response(response, method, path, rdata, rest_type='json') diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 609516b8a..02931d994 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -321,6 +321,7 @@ def __init__(self, module): self.original = None self.proposed = dict() self.stdout = None + self.get_auth = None # debug output self.filter_string = "" @@ -343,15 +344,15 @@ def __init__(self, module): self.module.warn("Enable debug output because ANSIBLE_DEBUG was set.") self.params["output_level"] = "debug" - if self.module._socket_path is None: - if self.params.get("private_key"): - # Perform signature-based authentication, no need to log on separately - if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL: - self.module.fail_json( - msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") - elif self.params.get("password") is not None: - self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") - elif self.params.get("password"): + if self.params.get("private_key"): + # Perform signature-based authentication, no need to log on separately + if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL: + self.module.fail_json( + msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") + elif self.params.get("password") is not None: + self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") + elif self.module._socket_path is None: + if self.params.get("password"): # Perform password-based authentication, log on using password self.login() else: @@ -1744,12 +1745,14 @@ def call(self, method): call_url = self.url data = None resp = None + if self.params.get('private_key'): + self.cert_auth(method=method, path=call_path, payload=data) + if self.module._socket_path: conn = Connection(self.module._socket_path) - info = conn.send_request(method, '/' + call_path, data) + conn.get_auth(self.get_auth) + info = conn.send_request(method, '/{0}'.format(call_path), data) else: - if self.params.get('private_key'): - self.cert_auth(method=method, path=call_path, payload=data) resp, info = fetch_url(self.module, call_url, data=data, headers=self.headers, @@ -1773,7 +1776,7 @@ def call(self, method): if method == 'GET': self.existing = info['body']['imdata'] else: - self.response_json(info) + self.response_json(info.get('body')) else: try: # APIC error diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 16e0356b9..6090b66cf 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -426,7 +426,7 @@ def main(): resp = None if module._socket_path: conn = Connection(aci.module._socket_path) - info = conn.send_request(aci.method, '/' + path, payload) + info = conn.send_request(aci.method, '/{0}'.format(path), payload) else: resp, info = fetch_url(module, aci.url, data=payload, diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index cb8e3f0a3..f748f713f 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -122,7 +122,7 @@ that: - error_on_input_validation is failed - error_on_input_validation.method == 'POST' - - "error_on_input_validation.msg == 'APIC Error 801: property descr of uni/tn-ansible_test failed validation for value \\'This is an [invalid] description\\''" + - "error_on_input_validation.msg == 'APIC Error 801: property descr of tn-ansible_test failed validation for value \\'This is an [invalid] description\\''" - 'error_on_input_validation.response == "HTTP Error 400: Bad Request"' - error_on_input_validation.status == 400 - "'current' not in error_on_input_validation" From 5c49c955f5df34830d5a56a9f6a989822aa827be Mon Sep 17 00:00:00 2001 From: ssrish Date: Sat, 23 Jan 2021 09:50:05 -0800 Subject: [PATCH 05/49] Add variables for host, username and password in plugin --- plugins/httpapi/aci.py | 32 ++++++++++++++++++++++---------- plugins/module_utils/aci.py | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index afe85a8e0..ea6d73ff0 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -54,9 +54,15 @@ def __init__(self, *args, **kwargs): 'Content-Type': "application/json" } self.auth = None + self.aci_host = None + self.aci_user = None + self.aci_pass = None - def get_auth(self, auth): + def get_auth(self, auth, host, username, password): self.auth = auth + self.aci_host = host + self.aci_user = username + self.aci_pass = password def login(self, username, password): ''' Log in to APIC ''' @@ -86,7 +92,7 @@ def logout(self): msg = 'Error on attempt to logout from APIC. {0}'.format(e) raise ConnectionError(self._return_info(None, method, path, msg)) - self._verify_response(response, method, path, response_data, rest_type='json') + self._verify_response(response, method, path, response_data) # Clean up tokens self.connection._auth = None @@ -98,7 +104,16 @@ def send_request(self, method, path, json=None): if self.auth is not None: self.connection._auth = {'Cookie': '{0}' .format(self.auth)} - + + if self.aci_host is not None: + self.connection.set_option("host", self.aci_host) + + if self.aci_user is not None: + self.connection.set_option("remote_user", self.aci_user) + + if self.aci_pass is not None: + self.connection.set_option("password", self.aci_pass) + # Perform some very basic path input validation. path = str(path) if path[0] != '/': @@ -108,21 +123,18 @@ def send_request(self, method, path, json=None): response, rdata = self.connection.send(path, json, method=method, headers=self.headers) - if path.find('.json') != -1: - return self._verify_response(response, method, path, rdata, rest_type='json') - else: - return self._verify_response(response, method, path, rdata, rest_type='xml') + return self._verify_response(response, method, path, rdata) - def _verify_response(self, response, method, path, rdata, rest_type): + def _verify_response(self, response, method, path, rdata): ''' Process the return code and response object from APIC ''' resp_value = self._get_response_value(rdata) - if rest_type == 'json': + if path.find('.json') != -1: respond_data = self._response_to_json(resp_value) else: respond_data = resp_value response_code = response.getcode() path = response.geturl() - msg = str(response) + msg = response.msg return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 02931d994..6fe841e8b 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1750,7 +1750,7 @@ def call(self, method): if self.module._socket_path: conn = Connection(self.module._socket_path) - conn.get_auth(self.get_auth) + conn.get_auth(self.get_auth, self.params.get('host'), self.params.get('username'), self.params.get('password')) info = conn.send_request(method, '/{0}'.format(call_path), data) else: resp, info = fetch_url(self.module, call_url, From 9737deea2f676c20d30bf4a5c46373950e5eb82c Mon Sep 17 00:00:00 2001 From: ssrish Date: Sun, 24 Jan 2021 10:57:51 -0800 Subject: [PATCH 06/49] Check Sanity --- plugins/httpapi/aci.py | 15 +++++++-------- plugins/module_utils/aci.py | 17 ++++++++++++----- plugins/modules/aci_rest.py | 10 +++++----- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index ea6d73ff0..5b96baaf5 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -18,7 +18,7 @@ DOCUMENTATION = """ --- -author: +author: - Lionel Hercot (lhercot) - Shreyas Srish (shrsr) httpapi: aci @@ -63,10 +63,10 @@ def get_auth(self, auth, host, username, password): self.aci_host = host self.aci_user = username self.aci_pass = password - + def login(self, username, password): ''' Log in to APIC ''' - + # Perform login request method = 'POST' path = '/api/aaaLogin.json' @@ -119,10 +119,9 @@ def send_request(self, method, path, json=None): if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) - - response, rdata = self.connection.send(path, json, method=method, - headers=self.headers) - + + response, rdata = self.connection.send(path, json, method=method, headers=self.headers) + return self._verify_response(response, method, path, rdata) def _verify_response(self, response, method, path, rdata): @@ -159,4 +158,4 @@ def _return_info(self, response_code, method, path, msg, respond_data=None): info['msg'] = msg info['body'] = respond_data - return info \ No newline at end of file + return info diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 6fe841e8b..7463a6dff 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -13,7 +13,11 @@ # Copyright: (c) 2019, Rob Huelga (@RobW3LGA) # Copyright: (c) 2020, Lionel Hercot (@lhercot) # Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) +<<<<<<< HEAD # Copyright: (c) 2023, Gaspard Micol (@gmicol) +======= +# Copyright: (c) 2020, Shreyas Srish (@shrsr) +>>>>>>> 02c53f6 (Check Sanity) # All rights reserved. # Redistribution and use in source and binary forms, with or without modification, @@ -508,10 +512,10 @@ def cert_auth(self, path=None, payload="", method=None): def response_json(self, rawoutput): """Handle APIC JSON response output""" try: - if isinstance(rawoutput, dict): - jsondata = rawoutput - else: - jsondata = json.loads(rawoutput) + if isinstance(rawoutput, dict): + jsondata = rawoutput + else: + jsondata = json.loads(rawoutput) except Exception as e: # Expose RAW output for troubleshooting self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e) @@ -1746,7 +1750,7 @@ def call(self, method): data = None resp = None if self.params.get('private_key'): - self.cert_auth(method=method, path=call_path, payload=data) + self.cert_auth(method=method, path=call_path, payload=data) if self.module._socket_path: conn = Connection(self.module._socket_path) @@ -1785,5 +1789,8 @@ def call(self, method): except KeyError: # Connection error self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) +<<<<<<< HEAD >>>>>>> da2af7d (retry w/o conflict) +======= +>>>>>>> 02c53f6 (Check Sanity) diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 6090b66cf..e6d2c62e3 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -429,11 +429,11 @@ def main(): info = conn.send_request(aci.method, '/{0}'.format(path), payload) else: resp, info = fetch_url(module, aci.url, - data=payload, - headers=aci.headers, - method=aci.method, - timeout=aci.params.get('timeout'), - use_proxy=aci.params.get('use_proxy')) + data=payload, + headers=aci.headers, + method=aci.method, + timeout=aci.params.get('timeout'), + use_proxy=aci.params.get('use_proxy')) aci.response = info.get("msg") aci.status = info.get("status") From 39b5282e400390c327d63d54a7dd9b750313c5a5 Mon Sep 17 00:00:00 2001 From: ssrish Date: Sun, 24 Jan 2021 11:23:48 -0800 Subject: [PATCH 07/49] Check Import Errors --- plugins/httpapi/aci.py | 10 +--------- plugins/module_utils/aci.py | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 5b96baaf5..862cd83eb 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -30,19 +30,11 @@ version_added: "2.1.0" """ -import base64 + import json -import os -import collections -import requests -import sys -import re -import traceback -from copy import deepcopy from ansible.module_utils._text import to_text from ansible.module_utils.connection import ConnectionError -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list from ansible.plugins.httpapi import HttpApiBase diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 7463a6dff..ba850d6d3 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -46,7 +46,6 @@ import base64 import json import os -import inspect from copy import deepcopy from ansible.module_utils.urls import fetch_url From b4b9588d3c40fa9a08891dcceb934148d5b02499 Mon Sep 17 00:00:00 2001 From: ssrish Date: Mon, 25 Jan 2021 19:03:56 -0800 Subject: [PATCH 08/49] Modified ACI plugin's response message --- plugins/httpapi/aci.py | 50 +++++++++++++++++++++++++------------ plugins/module_utils/aci.py | 4 +-- plugins/modules/aci_rest.py | 2 ++ 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 862cd83eb..cc0f1f6ab 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -24,10 +24,9 @@ httpapi: aci short_description: Ansible ACI HTTPAPI Plugin. description: - - This APIC plugin provides the HTTPAPI transport methods needed to initiate + - This ACI plugin provides the HTTPAPI transport methods needed to initiate a connection to the ACI controller, send API requests and process the response from the controller. -version_added: "2.1.0" """ @@ -42,19 +41,24 @@ class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) - self.headers = { - 'Content-Type': "application/json" - } - self.auth = None self.aci_host = None + self.aci_port = None self.aci_user = None self.aci_pass = None + self.auth = None + self.aci_proxy = None + self.aci_ssl = None + self.aci_validate_certs = None - def get_auth(self, auth, host, username, password): - self.auth = auth + def set_auth(self, auth, host, username, password, port, use_ssl, use_proxy, validate_certs): self.aci_host = host + self.aci_port = port self.aci_user = username self.aci_pass = password + self.auth = auth + self.aci_proxy = use_proxy + self.aci_ssl = use_ssl + self.aci_validate_certs = validate_certs def login(self, username, password): ''' Log in to APIC ''' @@ -65,7 +69,7 @@ def login(self, username, password): payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} data = json.dumps(payload) try: - response, response_data = self.connection.send(path, data, method=method, headers=self.headers, timeout=10) + response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} @@ -79,7 +83,7 @@ def logout(self): path = '/api/aaaLogout.json' try: - response, response_data = self.connection.send(path, {}, method=method, headers=self.headers, force_basic_auth=True) + response, response_data = self.connection.send(path, {}, method=method) except Exception as e: msg = 'Error on attempt to logout from APIC. {0}'.format(e) raise ConnectionError(self._return_info(None, method, path, msg)) @@ -93,26 +97,37 @@ def send_request(self, method, path, json=None): if json is None: json = {} - if self.auth is not None: - self.connection._auth = {'Cookie': '{0}' - .format(self.auth)} - if self.aci_host is not None: self.connection.set_option("host", self.aci_host) + if self.aci_port is not None: + self.connection.set_option("port", self.aci_port) + if self.aci_user is not None: self.connection.set_option("remote_user", self.aci_user) if self.aci_pass is not None: self.connection.set_option("password", self.aci_pass) + if self.auth is not None: + self.connection._auth = {'Cookie': '{0}'.format(self.auth)} + + if self.aci_proxy is not None: + self.connection.set_option("use_proxy", self.aci_proxy) + + if self.aci_ssl is not None: + self.connection.set_option("use_ssl", self.aci_ssl) + + if self.aci_validate_certs is not None: + self.connection.set_option("validate_certs", self.aci_validate_certs) + # Perform some very basic path input validation. path = str(path) if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) - response, rdata = self.connection.send(path, json, method=method, headers=self.headers) + response, rdata = self.connection.send(path, json, method=method) return self._verify_response(response, method, path, rdata) @@ -125,7 +140,10 @@ def _verify_response(self, response, method, path, rdata): respond_data = resp_value response_code = response.getcode() path = response.geturl() - msg = response.msg + if response_code == 400: + msg = str(response) + else: + msg = '{0} ({1} bytes)'.format(response.msg, len(resp_value)) return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index ba850d6d3..505b2a187 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -324,7 +324,6 @@ def __init__(self, module): self.original = None self.proposed = dict() self.stdout = None - self.get_auth = None # debug output self.filter_string = "" @@ -1753,7 +1752,8 @@ def call(self, method): if self.module._socket_path: conn = Connection(self.module._socket_path) - conn.get_auth(self.get_auth, self.params.get('host'), self.params.get('username'), self.params.get('password')) + conn.set_auth(self.headers.get('Cookie'), self.params.get('host'), self.params.get('username'), self.params.get('password'), + self.params.get('port'), self.params.get('use_ssl'), self.params.get('use_proxy'), self.params.get('validate_certs')) info = conn.send_request(method, '/{0}'.format(call_path), data) else: resp, info = fetch_url(self.module, call_url, diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index e6d2c62e3..61801940a 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -426,6 +426,8 @@ def main(): resp = None if module._socket_path: conn = Connection(aci.module._socket_path) + conn.set_auth(aci.headers.get('Cookie'), aci.params.get('host'), aci.params.get('username'), aci.params.get('password'), + aci.params.get('port'), aci.params.get('use_ssl'), aci.params.get('use_proxy'), aci.params.get('validate_certs')) info = conn.send_request(aci.method, '/{0}'.format(path), payload) else: resp, info = fetch_url(module, aci.url, From 90a6aa24b80d7d946619266fa24b0bfa4961267a Mon Sep 17 00:00:00 2001 From: ssrish Date: Mon, 25 Jan 2021 19:23:00 -0800 Subject: [PATCH 09/49] aci_rest sanity check --- plugins/modules/aci_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 61801940a..2a2b769c1 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -427,7 +427,7 @@ def main(): if module._socket_path: conn = Connection(aci.module._socket_path) conn.set_auth(aci.headers.get('Cookie'), aci.params.get('host'), aci.params.get('username'), aci.params.get('password'), - aci.params.get('port'), aci.params.get('use_ssl'), aci.params.get('use_proxy'), aci.params.get('validate_certs')) + aci.params.get('port'), aci.params.get('use_ssl'), aci.params.get('use_proxy'), aci.params.get('validate_certs')) info = conn.send_request(aci.method, '/{0}'.format(path), payload) else: resp, info = fetch_url(module, aci.url, From 034ea5e30e2b3f89d107a7b8a18054a504add0d4 Mon Sep 17 00:00:00 2001 From: ssrish Date: Wed, 27 Jan 2021 16:50:37 -0800 Subject: [PATCH 10/49] fix for aaa_cert, cloud_modules and config_rollback --- plugins/httpapi/aci.py | 10 +++++- plugins/modules/aci_config_rollback.py | 43 ++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index cc0f1f6ab..acde89641 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -46,6 +46,7 @@ def __init__(self, *args, **kwargs): self.aci_user = None self.aci_pass = None self.auth = None + self.check_auth_from_private_key = None self.aci_proxy = None self.aci_ssl = None self.aci_validate_certs = None @@ -93,10 +94,13 @@ def logout(self): self.connection._auth = None def send_request(self, method, path, json=None): - ''' This method handles all APIC REST API requests other then login ''' + ''' This method handles all APIC REST API requests other than login ''' if json is None: json = {} + if self.connection._connected is True and self.aci_host != self.connection.get_option("host"): + self.connection._connected = False + if self.aci_host is not None: self.connection.set_option("host", self.aci_host) @@ -111,6 +115,7 @@ def send_request(self, method, path, json=None): if self.auth is not None: self.connection._auth = {'Cookie': '{0}'.format(self.auth)} + self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} if self.aci_proxy is not None: self.connection.set_option("use_proxy", self.aci_proxy) @@ -121,6 +126,9 @@ def send_request(self, method, path, json=None): if self.aci_validate_certs is not None: self.connection.set_option("validate_certs", self.aci_validate_certs) + if self.auth is None and self.check_auth_from_private_key is not None: + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + # Perform some very basic path input validation. path = str(path) if path[0] != '/': diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index ceccc3c98..74467c0f6 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -198,6 +198,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_bytes from ansible.module_utils.urls import fetch_url +from ansible.module_utils.connection import Connection # Optional, only used for rollback preview try: @@ -285,6 +286,7 @@ def main(): elif state == "preview": aci.url = "%(protocol)s://%(host)s/mqapi2/snapshots.diff.xml" % module.params + aci.path = "mqapi2/snapshots.diff.xml" aci.filter_string = ( "?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-%(export_policy)s]/snapshot-%(snapshot)s&" "s2dn=uni/backupst/snapshots-[uni/fabric/configexp-%(compare_export_policy)s]/snapshot-%(compare_snapshot)s" @@ -301,19 +303,48 @@ def get_preview(aci): This function is used to generate a preview between two snapshots and add the parsed results to the aci module return data. """ uri = aci.url + aci.filter_string - resp, info = fetch_url( - aci.module, uri, headers=aci.headers, method="GET", timeout=aci.module.params.get("timeout"), use_proxy=aci.module.params.get("use_proxy") - ) + path = aci.path + aci.filter_string + resp = None + if aci.module._socket_path: + conn = Connection(aci.module._socket_path) + conn.set_auth( + aci.headers.get("Cookie"), + aci.module.params.get("host"), + aci.module.params.get("username"), + aci.module.params.get("password"), + aci.module.params.get("port"), + aci.module.params.get("use_ssl"), + aci.module.params.get("use_proxy"), + aci.module.params.get("validate_certs"), + ) + info = conn.send_request("GET", "/{0}".format(path)) + else: + resp, info = fetch_url( + aci.module, + uri, + headers=aci.headers, + method="GET", + timeout=aci.module.params.get("timeout"), + use_proxy=aci.module.params.get("use_proxy"), + ) aci.method = "GET" aci.response = info.get("msg") aci.status = info.get("status") # Handle APIC response if info.get("status") == 200: - xml_to_json(aci, resp.read()) + try: + xml_to_json(aci, resp.read()) + except AttributeError: + xml_to_json(aci, info.get("body")) else: - aci.result["raw"] = resp.read() - aci.fail_json(msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error) + try: + aci.result["raw"] = resp.read() + except AttributeError: + aci.result["raw"] = info.get("body") + aci.fail_json( + msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error + ) def xml_to_json(aci, response_data): From 2497574ed383197930b8e92860ab9b2e5744aa5b Mon Sep 17 00:00:00 2001 From: ssrish Date: Fri, 29 Jan 2021 14:26:51 -0800 Subject: [PATCH 11/49] List of hosts from inventory --- plugins/httpapi/aci.py | 115 ++++++++++++++++--------- plugins/module_utils/aci.py | 5 +- plugins/modules/aci_config_rollback.py | 3 +- 3 files changed, 80 insertions(+), 43 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index acde89641..477e6ce80 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -31,6 +31,9 @@ import json +import re +import pickle +import ipaddress from ansible.module_utils._text import to_text from ansible.module_utils.connection import ConnectionError @@ -50,20 +53,29 @@ def __init__(self, *args, **kwargs): self.aci_proxy = None self.aci_ssl = None self.aci_validate_certs = None - - def set_auth(self, auth, host, username, password, port, use_ssl, use_proxy, validate_certs): - self.aci_host = host - self.aci_port = port - self.aci_user = username - self.aci_pass = password + self.backup_hosts = None + self.host_counter = 0 + + def set_params(self, auth, params): + self.aci_host = params.get('host') + self.aci_port = params.get('port') + self.aci_user = params.get('username') + self.aci_pass = params.get('password') self.auth = auth - self.aci_proxy = use_proxy - self.aci_ssl = use_ssl - self.aci_validate_certs = validate_certs + self.aci_proxy = params.get('use_proxy') + self.aci_ssl = params.get('use_ssl') + self.aci_validate_certs = params.get('validate_certs') + + def set_backup_hosts(self): + try: + list_of_hosts = re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",") + ipaddress.ip_address(list_of_hosts[0]) + return list_of_hosts + except Exception: + return [] def login(self, username, password): ''' Log in to APIC ''' - # Perform login request method = 'POST' path = '/api/aaaLogin.json' @@ -75,9 +87,8 @@ def login(self, username, password): self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} - except Exception as e: - msg = 'Error on attempt to connect and authenticate with user: {0} to APIC: {1}. {2} '.format(username, self.connection.get_option("host"), e) - raise ConnectionError(msg) + except Exception: + self.handle_error() def logout(self): method = 'POST' @@ -88,46 +99,58 @@ def logout(self): except Exception as e: msg = 'Error on attempt to logout from APIC. {0}'.format(e) raise ConnectionError(self._return_info(None, method, path, msg)) - - self._verify_response(response, method, path, response_data) - # Clean up tokens self.connection._auth = None + self._verify_response(response, method, path, response_data) - def send_request(self, method, path, json=None): + def send_request(self, method, path, json): ''' This method handles all APIC REST API requests other than login ''' if json is None: json = {} + # Case1: List of hosts is provided + self.backup_hosts = self.set_backup_hosts() + if not self.backup_hosts: + # Case 1: Used for multiple hosts present in the playbook + if self.connection._connected is True and self.aci_host != self.connection.get_option("host"): + self.connection._connected = False - if self.connection._connected is True and self.aci_host != self.connection.get_option("host"): - self.connection._connected = False - - if self.aci_host is not None: - self.connection.set_option("host", self.aci_host) + if self.aci_host is not None: + self.connection.set_option("host", self.aci_host) - if self.aci_port is not None: - self.connection.set_option("port", self.aci_port) + if self.aci_port is not None: + self.connection.set_option("port", self.aci_port) - if self.aci_user is not None: - self.connection.set_option("remote_user", self.aci_user) + if self.aci_user is not None: + self.connection.set_option("remote_user", self.aci_user) - if self.aci_pass is not None: - self.connection.set_option("password", self.aci_pass) + if self.aci_pass is not None: + self.connection.set_option("password", self.aci_pass) - if self.auth is not None: - self.connection._auth = {'Cookie': '{0}'.format(self.auth)} - self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} + if self.auth is not None: + self.connection._auth = {'Cookie': '{0}'.format(self.auth)} + self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} - if self.aci_proxy is not None: - self.connection.set_option("use_proxy", self.aci_proxy) + if self.aci_proxy is not None: + self.connection.set_option("use_proxy", self.aci_proxy) - if self.aci_ssl is not None: - self.connection.set_option("use_ssl", self.aci_ssl) + if self.aci_ssl is not None: + self.connection.set_option("use_ssl", self.aci_ssl) - if self.aci_validate_certs is not None: - self.connection.set_option("validate_certs", self.aci_validate_certs) + if self.aci_validate_certs is not None: + self.connection.set_option("validate_certs", self.aci_validate_certs) - if self.auth is None and self.check_auth_from_private_key is not None: - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + # Case2: Switch using private key to credential authentication when private key is not specified + if self.auth is None and self.check_auth_from_private_key is not None: + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + else: + try: + with open('my_hosts.pk', 'rb') as fi: + self.host_counter = pickle.load(fi) + except FileNotFoundError: + pass + try: + self.connection.set_option("host", self.backup_hosts[self.host_counter]) + except IndexError: + pass # Perform some very basic path input validation. path = str(path) @@ -136,9 +159,21 @@ def send_request(self, method, path, json=None): raise ConnectionError(self._return_info(None, method, path, msg)) response, rdata = self.connection.send(path, json, method=method) - return self._verify_response(response, method, path, rdata) + def handle_error(self): + self.host_counter += 1 + if self.host_counter == len(self.backup_hosts): + raise ConnectionError("No hosts left in cluster to continue operation") + with open('my_hosts.pk', 'wb') as fi: + pickle.dump(self.host_counter, fi) + try: + self.connection.set_option("host", self.backup_hosts[self.host_counter]) + except IndexError: + pass + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + return True + def _verify_response(self, response, method, path, rdata): ''' Process the return code and response object from APIC ''' resp_value = self._get_response_value(rdata) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 505b2a187..19fac7be6 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -324,6 +324,7 @@ def __init__(self, module): self.original = None self.proposed = dict() self.stdout = None + self.counter = 0 # debug output self.filter_string = "" @@ -1752,9 +1753,9 @@ def call(self, method): if self.module._socket_path: conn = Connection(self.module._socket_path) - conn.set_auth(self.headers.get('Cookie'), self.params.get('host'), self.params.get('username'), self.params.get('password'), - self.params.get('port'), self.params.get('use_ssl'), self.params.get('use_proxy'), self.params.get('validate_certs')) + conn.set_params(self.headers.get('Cookie'), self.params) info = conn.send_request(method, '/{0}'.format(call_path), data) + self.url = info.get('url') else: resp, info = fetch_url(self.module, call_url, data=data, diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 74467c0f6..4b0cdb005 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -317,7 +317,8 @@ def get_preview(aci): aci.module.params.get("use_proxy"), aci.module.params.get("validate_certs"), ) - info = conn.send_request("GET", "/{0}".format(path)) + conn.set_params(aci.headers.get('Cookie'), aci.params) + info = conn.send_request('GET', '/{0}'.format(path), None) else: resp, info = fetch_url( aci.module, From 1b25656082f0927053b98b1581e4fbeeaa330a03 Mon Sep 17 00:00:00 2001 From: ssrish Date: Tue, 2 Feb 2021 20:38:10 -0800 Subject: [PATCH 12/49] Target next available apic in the list when an apic goes down --- plugins/httpapi/aci.py | 9 ++++++--- plugins/module_utils/aci.py | 6 +++++- plugins/modules/aci_config_rollback.py | 5 +++++ plugins/modules/aci_rest.py | 8 ++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 477e6ce80..36244ddbf 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -157,9 +157,12 @@ def send_request(self, method, path, json): if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) - - response, rdata = self.connection.send(path, json, method=method) - return self._verify_response(response, method, path, rdata) + response = None + try: + response, rdata = self.connection.send(path, json, method=method) + return self._verify_response(response, method, path, rdata) + except Exception: + self.handle_error() def handle_error(self): self.host_counter += 1 diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 19fac7be6..cbf14ca35 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1755,7 +1755,11 @@ def call(self, method): conn = Connection(self.module._socket_path) conn.set_params(self.headers.get('Cookie'), self.params) info = conn.send_request(method, '/{0}'.format(call_path), data) - self.url = info.get('url') + try: + self.url = info.get('url') + except Exception: + info = conn.send_request(method, '/{0}'.format(call_path), data) + self.url = info.get('url') else: resp, info = fetch_url(self.module, call_url, data=data, diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 4b0cdb005..0e886116e 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -319,6 +319,11 @@ def get_preview(aci): ) conn.set_params(aci.headers.get('Cookie'), aci.params) info = conn.send_request('GET', '/{0}'.format(path), None) + try: + aci.url = info.get('url') + except Exception: + info = conn.send_request('GET', '/{0}'.format(path), None) + aci.url = info.get('url') else: resp, info = fetch_url( aci.module, diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 2a2b769c1..7983d29dd 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -426,9 +426,13 @@ def main(): resp = None if module._socket_path: conn = Connection(aci.module._socket_path) - conn.set_auth(aci.headers.get('Cookie'), aci.params.get('host'), aci.params.get('username'), aci.params.get('password'), - aci.params.get('port'), aci.params.get('use_ssl'), aci.params.get('use_proxy'), aci.params.get('validate_certs')) + conn.set_params(aci.headers.get('Cookie'), aci.params) info = conn.send_request(aci.method, '/{0}'.format(path), payload) + try: + aci.url = info.get('url') + except Exception: + info = conn.send_request(aci.method, '/{0}'.format(path), payload) + aci.url = info.get('url') else: resp, info = fetch_url(module, aci.url, data=payload, From bbd13655177a7e08c504c442ab7f4d531a343f07 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Fri, 17 Jun 2022 08:42:57 -0700 Subject: [PATCH 13/49] [ignore] Change location of persistent storage file --- plugins/httpapi/aci.py | 106 +++++++++++++----------------------- plugins/module_utils/aci.py | 58 ++++++++++++++++---- 2 files changed, 85 insertions(+), 79 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 36244ddbf..ac36daaa0 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -14,22 +14,9 @@ from __future__ import (absolute_import, division, print_function) +from ipaddress import ip_address __metaclass__ = type -DOCUMENTATION = """ ---- -author: -- Lionel Hercot (lhercot) -- Shreyas Srish (shrsr) -httpapi: aci -short_description: Ansible ACI HTTPAPI Plugin. -description: - - This ACI plugin provides the HTTPAPI transport methods needed to initiate - a connection to the ACI controller, send API requests and process the - response from the controller. -""" - - import json import re import pickle @@ -44,27 +31,14 @@ class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) - self.aci_host = None - self.aci_port = None - self.aci_user = None - self.aci_pass = None self.auth = None self.check_auth_from_private_key = None - self.aci_proxy = None - self.aci_ssl = None - self.aci_validate_certs = None self.backup_hosts = None self.host_counter = 0 def set_params(self, auth, params): - self.aci_host = params.get('host') - self.aci_port = params.get('port') - self.aci_user = params.get('username') - self.aci_pass = params.get('password') + self.params = params self.auth = auth - self.aci_proxy = params.get('use_proxy') - self.aci_ssl = params.get('use_ssl') - self.aci_validate_certs = params.get('validate_certs') def set_backup_hosts(self): try: @@ -85,15 +59,13 @@ def login(self, username, password): response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' - .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} - + .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} except Exception: self.handle_error() def logout(self): method = 'POST' path = '/api/aaaLogout.json' - try: response, response_data = self.connection.send(path, {}, method=method) except Exception as e: @@ -107,51 +79,49 @@ def send_request(self, method, path, json): if json is None: json = {} # Case1: List of hosts is provided + if self.host_counter == 0: + with open('/tmp/hosts.pkl', 'wb') as fi: + pickle.dump(0, fi) self.backup_hosts = self.set_backup_hosts() + self.connection.set_option('persistent_command_timeout', 1) if not self.backup_hosts: - # Case 1: Used for multiple hosts present in the playbook - if self.connection._connected is True and self.aci_host != self.connection.get_option("host"): - self.connection._connected = False - - if self.aci_host is not None: - self.connection.set_option("host", self.aci_host) + # if self.connection._connected is True and self.params.get('host') != self.connection.get_option("host"): + # self.connection._connected = False + if self.params.get('host') is not None: + self.connection.set_option("host", self.params.get('host')) - if self.aci_port is not None: - self.connection.set_option("port", self.aci_port) + if self.params.get('port') is not None: + self.connection.set_option("port", self.params.get('port')) - if self.aci_user is not None: - self.connection.set_option("remote_user", self.aci_user) + if self.params.get('username') is not None: + self.connection.set_option("remote_user", self.params.get('username')) - if self.aci_pass is not None: - self.connection.set_option("password", self.aci_pass) + if self.params.get('password') is not None: + self.connection.set_option("password", self.params.get('password')) if self.auth is not None: self.connection._auth = {'Cookie': '{0}'.format(self.auth)} self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} - if self.aci_proxy is not None: - self.connection.set_option("use_proxy", self.aci_proxy) + if self.params.get('use_proxy') is not None: + self.connection.set_option("use_proxy", self.params.get('use_proxy')) - if self.aci_ssl is not None: - self.connection.set_option("use_ssl", self.aci_ssl) + if self.params.get('use_ssl') is not None: + self.connection.set_option("use_ssl", self.params.get('use_ssl')) - if self.aci_validate_certs is not None: - self.connection.set_option("validate_certs", self.aci_validate_certs) + if self.params.get('validate_certs') is not None: + self.connection.set_option("validate_certs", self.params.get('validate_certs')) - # Case2: Switch using private key to credential authentication when private key is not specified - if self.auth is None and self.check_auth_from_private_key is not None: - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + #Case2: Switch using private key to credential authentication when private key is not specified + # if self.auth is None and self.check_auth_from_private_key is not None: + # self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) else: - try: - with open('my_hosts.pk', 'rb') as fi: - self.host_counter = pickle.load(fi) - except FileNotFoundError: - pass + with open('/tmp/hosts.pkl', 'rb') as fi: + self.host_counter = pickle.load(fi) try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) except IndexError: pass - # Perform some very basic path input validation. path = str(path) if path[0] != '/': @@ -165,20 +135,19 @@ def send_request(self, method, path, json): self.handle_error() def handle_error(self): + #raise ConnectionError("login") self.host_counter += 1 - if self.host_counter == len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation") - with open('my_hosts.pk', 'wb') as fi: + if self.host_counter >= len(self.backup_hosts): + raise ConnectionError("No hosts left in cluster to continue operation %s" % self.connection.get_option("host")) + with open('/tmp/hosts.pkl', 'wb') as fi: pickle.dump(self.host_counter, fi) - try: - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - except IndexError: - pass + self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) return True def _verify_response(self, response, method, path, rdata): ''' Process the return code and response object from APIC ''' + number = self.host_counter resp_value = self._get_response_value(rdata) if path.find('.json') != -1: respond_data = self._response_to_json(resp_value) @@ -190,7 +159,7 @@ def _verify_response(self, response, method, path, rdata): msg = str(response) else: msg = '{0} ({1} bytes)'.format(response.msg, len(resp_value)) - return self._return_info(response_code, method, path, msg, respond_data) + return self._return_info(response_code, method, path, msg, number, respond_data) def _get_response_value(self, response_data): ''' Extract string data from response_data returned from APIC ''' @@ -204,14 +173,13 @@ def _response_to_json(self, response_text): except ValueError: return 'Invalid JSON response: {0}'.format(response_text) - def _return_info(self, response_code, method, path, msg, respond_data=None): + def _return_info(self, response_code, method, path, msg, number, respond_data=None): ''' Format success/error data and return with consistent format ''' - info = {} info['status'] = response_code info['method'] = method info['url'] = path info['msg'] = msg info['body'] = respond_data - + info['hosts'] = number return info diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index cbf14ca35..442364968 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -90,6 +90,7 @@ def aci_argument_spec(): return dict( +<<<<<<< HEAD <<<<<<< HEAD host=dict( type="str", @@ -98,9 +99,12 @@ def aci_argument_spec(): fallback=(env_fallback, ["ACI_HOST"]), ), ======= +======= +>>>>>>> 5ca45db ([ignore] Change location of persistent storage file) host=dict(type="str", required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"])), >>>>>>> 6341500 (retry w/o conflict) port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])), +<<<<<<< HEAD username=dict( type="str", default="admin", @@ -112,6 +116,16 @@ def aci_argument_spec(): no_log=True, fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"]), ), +======= + username=dict(type="str", default="admin", aliases=["user"], fallback=(env_fallback, ["ACI_USERNAME", "ANSIBLE_NET_USERNAME"])), + password=dict(type="str", no_log=True, fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"])), +======= + host=dict(type='str', required=True, aliases=['hostname'], fallback=(env_fallback, ['ACI_HOST'])), + port=dict(type='int', required=False, fallback=(env_fallback, ['ACI_PORT'])), + username=dict(type='str', required=False, aliases=['user'], fallback=(env_fallback, ['ACI_USERNAME', 'ANSIBLE_NET_USERNAME'])), + password=dict(type='str', no_log=True, fallback=(env_fallback, ['ACI_PASSWORD', 'ANSIBLE_NET_PASSWORD'])), +>>>>>>> eeef0dc ([ignore] Change location of persistent storage file) +>>>>>>> 5ca45db ([ignore] Change location of persistent storage file) # Beware, this is not the same as client_key ! private_key=dict( type="str", @@ -492,8 +506,8 @@ def cert_auth(self, path=None, payload="", method=None): else: self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params) - if self.params.get("certificate_name") is None: - self.params["certificate_name"] = self.params.get("username") + if self.params.get('certificate_name') is None: + self.params['certificate_name'] = self.params.get('username', 'admin') # NOTE: ACI documentation incorrectly adds a space between method and path sig_request = method + path + payload if HAS_CRYPTOGRAPHY: @@ -1250,7 +1264,11 @@ def delete_config(self): elif not self.module.check_mode: # Sign and encode request as to APIC's wishes +<<<<<<< HEAD self.call("DELETE") +======= + self.api_call('DELETE') +>>>>>>> eeef0dc ([ignore] Change location of persistent storage file) <<<<<<< HEAD resp, info = fetch_url( @@ -1403,6 +1421,7 @@ def get_existing(self): that this method can be used to supply the existing configuration when using the get_diff method. The response, status, and existing configuration will be added to the self.result dictionary. """ +<<<<<<< HEAD <<<<<<< HEAD uri = self.url + self.filter_string @@ -1436,6 +1455,12 @@ def get_existing(self): ======= self.call("GET") >>>>>>> 6341500 (retry w/o conflict) +======= + self.call("GET") +======= + self.api_call('GET') +>>>>>>> eeef0dc ([ignore] Change location of persistent storage file) +>>>>>>> 5ca45db ([ignore] Change location of persistent storage file) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1555,6 +1580,7 @@ def post_config(self, parent_class=None): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes +<<<<<<< HEAD <<<<<<< HEAD url = self.url if parent_class is not None: @@ -1595,6 +1621,12 @@ def post_config(self, parent_class=None): ======= self.call("POST") >>>>>>> da2af7d (retry w/o conflict) +======= + self.call("POST") +======= + self.api_call('POST') +>>>>>>> eeef0dc ([ignore] Change location of persistent storage file) +>>>>>>> d17daa6 ([ignore] Change location of persistent storage file) else: self.result["changed"] = True self.method = "POST" @@ -1705,6 +1737,7 @@ def dump_json(self): if self.result.get("changed") is True: json.dump([mo], output_file) +<<<<<<< HEAD <<<<<<< HEAD def delete_config_request(self, path): self._config_request(path, "absent") @@ -1735,6 +1768,9 @@ def ospf_spec(): ) ======= def call(self, method): +======= + def api_call(self, method): +>>>>>>> d17daa6 ([ignore] Change location of persistent storage file) if method == 'GET': call_path = self.path + self.filter_string call_url = self.url + self.filter_string @@ -1752,14 +1788,16 @@ def call(self, method): self.cert_auth(method=method, path=call_path, payload=data) if self.module._socket_path: - conn = Connection(self.module._socket_path) - conn.set_params(self.headers.get('Cookie'), self.params) - info = conn.send_request(method, '/{0}'.format(call_path), data) - try: - self.url = info.get('url') - except Exception: - info = conn.send_request(method, '/{0}'.format(call_path), data) - self.url = info.get('url') + connect = Connection(self.module._socket_path) + connect.set_params(self.headers.get('Cookie'), self.params) + info = connect.send_request(method, '/{0}'.format(call_path), data) + self.url = info.get('url') + self.stdout = str(info.get('hosts')) + # try: + # self.url = info.get('url') + # except Exception: + # info = connect.send_request(method, '/{0}'.format(call_path), data) + # self.url = info.get('url') else: resp, info = fetch_url(self.module, call_url, data=data, From f147038434598c6467f810c7970c2d2bee75ab61 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 19 Jun 2022 08:10:19 -0700 Subject: [PATCH 14/49] [ignore] Addition of queue messages --- plugins/httpapi/aci.py | 24 +++++++++++++++--------- plugins/module_utils/aci.py | 26 +++++++++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index ac36daaa0..292a3b9d6 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -51,6 +51,7 @@ def set_backup_hosts(self): def login(self, username, password): ''' Log in to APIC ''' # Perform login request + self.connection.queue_message('log', 'Establishing connection to {0}'.format(self.connection.get_option('host'))) method = 'POST' path = '/api/aaaLogin.json' payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} @@ -60,7 +61,9 @@ def login(self, username, password): response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} + self.connection.queue_message('vvvv', 'Establishing connection to {0} was successful'.format(self.connection.get_option('host'))) except Exception: + self.connection.queue_message('vvvv', 'Failed establishing connection to {0}'.format(self.connection.get_option('host'))) self.handle_error() def logout(self): @@ -79,11 +82,11 @@ def send_request(self, method, path, json): if json is None: json = {} # Case1: List of hosts is provided - if self.host_counter == 0: - with open('/tmp/hosts.pkl', 'wb') as fi: - pickle.dump(0, fi) + # if self.host_counter == 0: + # with open('/tmp/hosts.pkl', 'wb') as fi: + # pickle.dump(0, fi) self.backup_hosts = self.set_backup_hosts() - self.connection.set_option('persistent_command_timeout', 1) + #self.connection.set_option('persistent_command_timeout', 3) if not self.backup_hosts: # if self.connection._connected is True and self.params.get('host') != self.connection.get_option("host"): # self.connection._connected = False @@ -116,10 +119,11 @@ def send_request(self, method, path, json): # if self.auth is None and self.check_auth_from_private_key is not None: # self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) else: - with open('/tmp/hosts.pkl', 'rb') as fi: - self.host_counter = pickle.load(fi) + # with open('/tmp/hosts.pkl', 'rb') as fi: + # self.host_counter = pickle.load(fi) try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) + self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) except IndexError: pass # Perform some very basic path input validation. @@ -130,17 +134,19 @@ def send_request(self, method, path, json): response = None try: response, rdata = self.connection.send(path, json, method=method) + self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) return self._verify_response(response, method, path, rdata) except Exception: + self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) self.handle_error() def handle_error(self): - #raise ConnectionError("login") self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): raise ConnectionError("No hosts left in cluster to continue operation %s" % self.connection.get_option("host")) - with open('/tmp/hosts.pkl', 'wb') as fi: - pickle.dump(self.host_counter, fi) + # with open('/tmp/hosts.pkl', 'wb') as fi: + # pickle.dump(self.host_counter, fi) + self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) return True diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 442364968..08374265e 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -349,6 +349,7 @@ def __init__(self, module): self.response = None self.status = None self.url = None + self.httpapi_logs = list() # aci_rest output self.imdata = None @@ -1264,11 +1265,7 @@ def delete_config(self): elif not self.module.check_mode: # Sign and encode request as to APIC's wishes -<<<<<<< HEAD - self.call("DELETE") -======= - self.api_call('DELETE') ->>>>>>> eeef0dc ([ignore] Change location of persistent storage file) + self.api_call("DELETE") <<<<<<< HEAD resp, info = fetch_url( @@ -1422,6 +1419,7 @@ def get_existing(self): and existing configuration will be added to the self.result dictionary. """ <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD uri = self.url + self.filter_string @@ -1461,6 +1459,9 @@ def get_existing(self): self.api_call('GET') >>>>>>> eeef0dc ([ignore] Change location of persistent storage file) >>>>>>> 5ca45db ([ignore] Change location of persistent storage file) +======= + self.api_call("GET") +>>>>>>> 53e78e0 ([ignore] Addition of queue messages) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1581,6 +1582,7 @@ def post_config(self, parent_class=None): elif not self.module.check_mode: # Sign and encode request as to APIC's wishes <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD url = self.url if parent_class is not None: @@ -1627,6 +1629,9 @@ def post_config(self, parent_class=None): self.api_call('POST') >>>>>>> eeef0dc ([ignore] Change location of persistent storage file) >>>>>>> d17daa6 ([ignore] Change location of persistent storage file) +======= + self.api_call("POST") +>>>>>>> a77aefa ([ignore] Addition of queue messages) else: self.result["changed"] = True self.method = "POST" @@ -1653,6 +1658,7 @@ def exit_json(self, filter_existing=None, **kwargs): self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url + self.result["httpapi_logs"] = self.httpapi_logs if self.stdout: self.result["stdout"] = self.stdout @@ -1700,9 +1706,18 @@ def fail_json(self, msg, **kwargs): self.result["filter_string"] = self.filter_string self.result["method"] = self.method # self.result['path'] = self.path # Adding 'path' in result causes state: absent in output +<<<<<<< HEAD self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url +======= + self.result['response'] = self.response + self.result['status'] = self.status + self.result['url'] = self.url + self.result['httpapi_logs'] = self.httpapi_logs + if self.stdout: + self.result['stdout'] = self.stdout +>>>>>>> cecc2ae ([ignore] Addition of queue messages) if "state" in self.params: if self.params.get("output_level") in ("debug", "info"): @@ -1792,6 +1807,7 @@ def api_call(self, method): connect.set_params(self.headers.get('Cookie'), self.params) info = connect.send_request(method, '/{0}'.format(call_path), data) self.url = info.get('url') + self.httpapi_logs.extend(connect.pop_messages()) self.stdout = str(info.get('hosts')) # try: # self.url = info.get('url') From 59c9793b3e4244c91d5ea3fe038c46c467842452 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Tue, 28 Jun 2022 10:29:56 -0700 Subject: [PATCH 15/49] [ignore] Ability to switch from certificate based authentication to credential --- plugins/httpapi/aci.py | 88 ++++++++++++++++++++----------------- plugins/module_utils/aci.py | 28 ++++++------ 2 files changed, 61 insertions(+), 55 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 292a3b9d6..5532508f4 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -14,13 +14,10 @@ from __future__ import (absolute_import, division, print_function) -from ipaddress import ip_address __metaclass__ = type import json import re -import pickle -import ipaddress from ansible.module_utils._text import to_text from ansible.module_utils.connection import ConnectionError @@ -34,19 +31,22 @@ def __init__(self, *args, **kwargs): self.auth = None self.check_auth_from_private_key = None self.backup_hosts = None + self.list_of_hosts = [] self.host_counter = 0 - + def set_params(self, auth, params): self.params = params self.auth = auth - def set_backup_hosts(self): + def get_backup_hosts(self): try: - list_of_hosts = re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",") - ipaddress.ip_address(list_of_hosts[0]) - return list_of_hosts + # append is used here to store the first value of the variable self.connection.get_option("host") in the 0th position + # this is done because value of 'host' keeps changing constantly when list of hosts is provided. We always + # want access to the original set of hosts + self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) + return self.list_of_hosts except Exception: - return [] + return [None] def login(self, username, password): ''' Log in to APIC ''' @@ -77,21 +77,25 @@ def logout(self): self.connection._auth = None self._verify_response(response, method, path, response_data) - def send_request(self, method, path, json): + def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' - if json is None: - json = {} - # Case1: List of hosts is provided - # if self.host_counter == 0: - # with open('/tmp/hosts.pkl', 'wb') as fi: - # pickle.dump(0, fi) - self.backup_hosts = self.set_backup_hosts() - #self.connection.set_option('persistent_command_timeout', 3) - if not self.backup_hosts: + if data is None: + data = {} + + # Set backup host/hosts from the inventory if/when provided + self.get_backup_hosts() + self.backup_hosts = self.list_of_hosts[0] + + # The command timeout which is the response timeout from APIC can be set in the inventory + # self.connection.set_option('persistent_command_timeout', 3) + + # Case1: Host is provided in the task of a playbook + if self.params.get('host') is not None: + # if self.connection._connected is True and self.params.get('host') != self.connection.get_option("host"): # self.connection._connected = False - if self.params.get('host') is not None: - self.connection.set_option("host", self.params.get('host')) + + self.connection.set_option("host", self.params.get('host')) if self.params.get('port') is not None: self.connection.set_option("port", self.params.get('port')) @@ -102,9 +106,21 @@ def send_request(self, method, path, json): if self.params.get('password') is not None: self.connection.set_option("password", self.params.get('password')) - if self.auth is not None: + # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a + # task while ansible is running a playbook. + if self.auth is not None and self.check_auth_from_private_key is None: + self.connection._connected = False self.connection._auth = {'Cookie': '{0}'.format(self.auth)} self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} + self.connection.queue_message('vvvv', 'Going through certificate authentication') + self.connection._connected = True + + # Switch from certificate to credential authentication when private key is not specified in a + # task while ansible is running a playbook + elif self.auth is None and self.check_auth_from_private_key is not None: + self.check_auth_from_private_key = None + self.connection.queue_message('vvvv', 'Switching from certificate to credential authentication') + self.connection._connected = False if self.params.get('use_proxy') is not None: self.connection.set_option("use_proxy", self.params.get('use_proxy')) @@ -114,13 +130,9 @@ def send_request(self, method, path, json): if self.params.get('validate_certs') is not None: self.connection.set_option("validate_certs", self.params.get('validate_certs')) - - #Case2: Switch using private key to credential authentication when private key is not specified - # if self.auth is None and self.check_auth_from_private_key is not None: - # self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - else: - # with open('/tmp/hosts.pkl', 'rb') as fi: - # self.host_counter = pickle.load(fi) + + # Case2: Host is not provided in the task of a playbook + elif self.backup_hosts: try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) @@ -131,9 +143,8 @@ def send_request(self, method, path, json): if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) - response = None try: - response, rdata = self.connection.send(path, json, method=method) + response, rdata = self.connection.send(path, data, method=method) self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) return self._verify_response(response, method, path, rdata) except Exception: @@ -143,17 +154,13 @@ def send_request(self, method, path, json): def handle_error(self): self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation %s" % self.connection.get_option("host")) - # with open('/tmp/hosts.pkl', 'wb') as fi: - # pickle.dump(self.host_counter, fi) + raise ConnectionError("No hosts left in cluster to continue operation %s %s %s %s %s" % (self.connection.get_option("host"), self.host_counter, len(self.backup_hosts), self.backup_hosts, self.get_backup_hosts())) self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - return True - + def _verify_response(self, response, method, path, rdata): ''' Process the return code and response object from APIC ''' - number = self.host_counter resp_value = self._get_response_value(rdata) if path.find('.json') != -1: respond_data = self._response_to_json(resp_value) @@ -165,7 +172,7 @@ def _verify_response(self, response, method, path, rdata): msg = str(response) else: msg = '{0} ({1} bytes)'.format(response.msg, len(resp_value)) - return self._return_info(response_code, method, path, msg, number, respond_data) + return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): ''' Extract string data from response_data returned from APIC ''' @@ -176,10 +183,10 @@ def _response_to_json(self, response_text): try: return json.loads(response_text) if response_text else {} # JSONDecodeError only available on Python 3.5+ - except ValueError: + except Exception: return 'Invalid JSON response: {0}'.format(response_text) - def _return_info(self, response_code, method, path, msg, number, respond_data=None): + def _return_info(self, response_code, method, path, msg, respond_data=None): ''' Format success/error data and return with consistent format ''' info = {} info['status'] = response_code @@ -187,5 +194,4 @@ def _return_info(self, response_code, method, path, msg, number, respond_data=No info['url'] = path info['msg'] = msg info['body'] = respond_data - info['hosts'] = number return info diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 08374265e..f16ebe43d 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -91,6 +91,7 @@ def aci_argument_spec(): return dict( <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD host=dict( type="str", @@ -101,6 +102,8 @@ def aci_argument_spec(): ======= ======= >>>>>>> 5ca45db ([ignore] Change location of persistent storage file) +======= +>>>>>>> d11ea33 ([ignore] Ability to switch from certificate based authentication to credential) host=dict(type="str", required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"])), >>>>>>> 6341500 (retry w/o conflict) port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])), @@ -119,6 +122,7 @@ def aci_argument_spec(): ======= username=dict(type="str", default="admin", aliases=["user"], fallback=(env_fallback, ["ACI_USERNAME", "ANSIBLE_NET_USERNAME"])), password=dict(type="str", no_log=True, fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"])), +<<<<<<< HEAD ======= host=dict(type='str', required=True, aliases=['hostname'], fallback=(env_fallback, ['ACI_HOST'])), port=dict(type='int', required=False, fallback=(env_fallback, ['ACI_PORT'])), @@ -126,6 +130,8 @@ def aci_argument_spec(): password=dict(type='str', no_log=True, fallback=(env_fallback, ['ACI_PASSWORD', 'ANSIBLE_NET_PASSWORD'])), >>>>>>> eeef0dc ([ignore] Change location of persistent storage file) >>>>>>> 5ca45db ([ignore] Change location of persistent storage file) +======= +>>>>>>> d11ea33 ([ignore] Ability to switch from certificate based authentication to credential) # Beware, this is not the same as client_key ! private_key=dict( type="str", @@ -338,7 +344,6 @@ def __init__(self, module): self.original = None self.proposed = dict() self.stdout = None - self.counter = 0 # debug output self.filter_string = "" @@ -1706,18 +1711,13 @@ def fail_json(self, msg, **kwargs): self.result["filter_string"] = self.filter_string self.result["method"] = self.method # self.result['path'] = self.path # Adding 'path' in result causes state: absent in output -<<<<<<< HEAD + self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url -======= - self.result['response'] = self.response - self.result['status'] = self.status - self.result['url'] = self.url self.result['httpapi_logs'] = self.httpapi_logs if self.stdout: self.result['stdout'] = self.stdout ->>>>>>> cecc2ae ([ignore] Addition of queue messages) if "state" in self.params: if self.params.get("output_level") in ("debug", "info"): @@ -1806,14 +1806,13 @@ def api_call(self, method): connect = Connection(self.module._socket_path) connect.set_params(self.headers.get('Cookie'), self.params) info = connect.send_request(method, '/{0}'.format(call_path), data) - self.url = info.get('url') + try: + self.url = info.get('url') + except Exception: + info = connect.send_request(method, '/{0}'.format(call_path), data) + self.url = info.get('url') self.httpapi_logs.extend(connect.pop_messages()) - self.stdout = str(info.get('hosts')) - # try: - # self.url = info.get('url') - # except Exception: - # info = connect.send_request(method, '/{0}'.format(call_path), data) - # self.url = info.get('url') + self.stdout = str("Through plugin") else: resp, info = fetch_url(self.module, call_url, data=data, @@ -1839,6 +1838,7 @@ def api_call(self, method): self.existing = info['body']['imdata'] else: self.response_json(info.get('body')) + else: try: # APIC error From 8ec0740bef88de137157e6b64c90500ae5a2f1d2 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Fri, 1 Jul 2022 09:02:09 -0700 Subject: [PATCH 16/49] [ignore] Removed try/except block from module_utils aci.py and implemented a different approach for switching hosts --- plugins/httpapi/aci.py | 37 ++++++++++++++++++++++++++++++------- plugins/module_utils/aci.py | 7 +------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 5532508f4..66813f7cb 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -16,6 +16,18 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +DOCUMENTATION = """ +--- +author: +- Shreyas Srish (shrsr) +httpapi: aci +short_description: Ansible ACI HTTPAPI Plugin. +description: + - This ACI plugin provides the HTTPAPI transport methods needed to initiate + a connection to the APIC, send API requests and process the + response from the controller. +""" + import json import re @@ -33,6 +45,7 @@ def __init__(self, *args, **kwargs): self.backup_hosts = None self.list_of_hosts = [] self.host_counter = 0 + self.entered_exception = False def set_params(self, auth, params): self.params = params @@ -61,7 +74,7 @@ def login(self, username, password): response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} - self.connection.queue_message('vvvv', 'Establishing connection to {0} was successful'.format(self.connection.get_option('host'))) + self.connection.queue_message('vvvv', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) except Exception: self.connection.queue_message('vvvv', 'Failed establishing connection to {0}'.format(self.connection.get_option('host'))) self.handle_error() @@ -91,10 +104,7 @@ def send_request(self, method, path, data): # Case1: Host is provided in the task of a playbook if self.params.get('host') is not None: - - # if self.connection._connected is True and self.params.get('host') != self.connection.get_option("host"): - # self.connection._connected = False - + self.connection.set_option("host", self.params.get('host')) if self.params.get('port') is not None: @@ -143,18 +153,31 @@ def send_request(self, method, path, data): if path[0] != '/': msg = 'Value of does not appear to be formated properly' raise ConnectionError(self._return_info(None, method, path, msg)) + # Initiation of request try: response, rdata = self.connection.send(path, data, method=method) self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) - return self._verify_response(response, method, path, rdata) except Exception: + self.entered_exception = True self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) self.handle_error() + finally: + if self.entered_exception: + self.entered_exception = False + self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) + # Final try/except block to close/exit operation + try: + response, rdata = self.connection.send(path, data, method=method) + except: + self.handle_error() + return self._verify_response(response, method, path, rdata) + else: + return self._verify_response(response, method, path, rdata) def handle_error(self): self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation %s %s %s %s %s" % (self.connection.get_option("host"), self.host_counter, len(self.backup_hosts), self.backup_hosts, self.get_backup_hosts())) + raise ConnectionError("No hosts left in cluster to continue operation!!!") self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index f16ebe43d..c6766fffa 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1806,13 +1806,8 @@ def api_call(self, method): connect = Connection(self.module._socket_path) connect.set_params(self.headers.get('Cookie'), self.params) info = connect.send_request(method, '/{0}'.format(call_path), data) - try: - self.url = info.get('url') - except Exception: - info = connect.send_request(method, '/{0}'.format(call_path), data) - self.url = info.get('url') + self.url = info.get('url') self.httpapi_logs.extend(connect.pop_messages()) - self.stdout = str("Through plugin") else: resp, info = fetch_url(self.module, call_url, data=data, From f03862106e087e0418fc4a250704404c9e75690f Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sat, 2 Jul 2022 07:39:25 -0700 Subject: [PATCH 17/49] [ignore] Changed placement of return statement in second try/except block --- plugins/httpapi/aci.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 66813f7cb..611066a9d 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Cisco and/or its affiliates. +# Copyright (c) 2022 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -170,9 +170,7 @@ def send_request(self, method, path, data): response, rdata = self.connection.send(path, data, method=method) except: self.handle_error() - return self._verify_response(response, method, path, rdata) - else: - return self._verify_response(response, method, path, rdata) + return self._verify_response(response, method, path, rdata) def handle_error(self): self.host_counter += 1 From dfb60cf7c7ba6bb1b6f752382fa82da063d69698 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 6 Jul 2022 09:08:12 -0700 Subject: [PATCH 18/49] [ignore] Removed path verification --- plugins/httpapi/aci.py | 49 +++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 611066a9d..27bc017e3 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -31,6 +31,7 @@ import json import re +from ansible.errors import AnsibleConnectionFailure from ansible.module_utils._text import to_text from ansible.module_utils.connection import ConnectionError from ansible.plugins.httpapi import HttpApiBase @@ -46,6 +47,7 @@ def __init__(self, *args, **kwargs): self.list_of_hosts = [] self.host_counter = 0 self.entered_exception = False + self.exception_message = None def set_params(self, auth, params): self.params = params @@ -54,12 +56,12 @@ def set_params(self, auth, params): def get_backup_hosts(self): try: # append is used here to store the first value of the variable self.connection.get_option("host") in the 0th position - # this is done because value of 'host' keeps changing constantly when list of hosts is provided. We always - # want access to the original set of hosts + # this is done because we keep changing the value of 'host' constantly when a list of hosts is provided. We always + # want access to the original set of hosts. self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) return self.list_of_hosts except Exception: - return [None] + return [] def login(self, username, password): ''' Log in to APIC ''' @@ -75,8 +77,9 @@ def login(self, username, password): self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} self.connection.queue_message('vvvv', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) - except Exception: - self.connection.queue_message('vvvv', 'Failed establishing connection to {0}'.format(self.connection.get_option('host'))) + except Exception as exc: + self.exception_message = exc + self.connection.queue_message('vvvv', '{0}'.format(exc)) self.handle_error() def logout(self): @@ -104,7 +107,6 @@ def send_request(self, method, path, data): # Case1: Host is provided in the task of a playbook if self.params.get('host') is not None: - self.connection.set_option("host", self.params.get('host')) if self.params.get('port') is not None: @@ -140,6 +142,9 @@ def send_request(self, method, path, data): if self.params.get('validate_certs') is not None: self.connection.set_option("validate_certs", self.params.get('validate_certs')) + + if self.params.get('timeout') is not None: + self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) # Case2: Host is not provided in the task of a playbook elif self.backup_hosts: @@ -148,16 +153,13 @@ def send_request(self, method, path, data): self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) except IndexError: pass - # Perform some very basic path input validation. - path = str(path) - if path[0] != '/': - msg = 'Value of does not appear to be formated properly' - raise ConnectionError(self._return_info(None, method, path, msg)) + # Initiation of request try: - response, rdata = self.connection.send(path, data, method=method) + response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) - except Exception: + except Exception as exc: + self.exception_message = exc self.entered_exception = True self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) self.handle_error() @@ -167,12 +169,19 @@ def send_request(self, method, path, data): self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) # Final try/except block to close/exit operation try: - response, rdata = self.connection.send(path, data, method=method) + response, response_data = self.connection.send(path, data, method=method) except: self.handle_error() - return self._verify_response(response, method, path, rdata) + return self._verify_response(response, method, path, response_data) def handle_error(self): + # We break the flow of code here when we are operating on a host at task level and/or hosts are also present in the inventory file. + if self.params.get('host') is not None: + raise AnsibleConnectionFailure( + "{0}".format( + self.exception_message + ) + ) self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): raise ConnectionError("No hosts left in cluster to continue operation!!!") @@ -180,19 +189,19 @@ def handle_error(self): self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - def _verify_response(self, response, method, path, rdata): + def _verify_response(self, response, method, path, response_data): ''' Process the return code and response object from APIC ''' - resp_value = self._get_response_value(rdata) + response_value = self._get_response_value(response_data) if path.find('.json') != -1: - respond_data = self._response_to_json(resp_value) + respond_data = self._response_to_json(response_value) else: - respond_data = resp_value + respond_data = response_value response_code = response.getcode() path = response.geturl() if response_code == 400: msg = str(response) else: - msg = '{0} ({1} bytes)'.format(response.msg, len(resp_value)) + msg = '{0} ({1} bytes)'.format(response.msg, len(response_value)) return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): From e2d22b2a6177259180f5b88b5955d67b7ac6e35d Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 13 Jul 2022 07:27:13 -0700 Subject: [PATCH 19/49] [ignore] Changes to aci_rest and config_rollback to mimic aci.py in module_utils --- plugins/httpapi/aci.py | 2 ++ plugins/modules/aci_config_rollback.py | 23 +++++------------------ plugins/modules/aci_rest.py | 13 +++++-------- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 27bc017e3..ad6359d87 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -170,7 +170,9 @@ def send_request(self, method, path, data): # Final try/except block to close/exit operation try: response, response_data = self.connection.send(path, data, method=method) + self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) except: + self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) self.handle_error() return self._verify_response(response, method, path, response_data) diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 0e886116e..bb5e9afcf 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -306,24 +306,11 @@ def get_preview(aci): path = aci.path + aci.filter_string resp = None if aci.module._socket_path: - conn = Connection(aci.module._socket_path) - conn.set_auth( - aci.headers.get("Cookie"), - aci.module.params.get("host"), - aci.module.params.get("username"), - aci.module.params.get("password"), - aci.module.params.get("port"), - aci.module.params.get("use_ssl"), - aci.module.params.get("use_proxy"), - aci.module.params.get("validate_certs"), - ) - conn.set_params(aci.headers.get('Cookie'), aci.params) - info = conn.send_request('GET', '/{0}'.format(path), None) - try: - aci.url = info.get('url') - except Exception: - info = conn.send_request('GET', '/{0}'.format(path), None) - aci.url = info.get('url') + connect = Connection(aci.module._socket_path) + connect.set_params(aci.headers.get('Cookie'), aci.params) + info = connect.send_request('GET', '/{0}'.format(path), None) + aci.url = info.get('url') + aci.httpapi_logs.extend(connect.pop_messages()) else: resp, info = fetch_url( aci.module, diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 7983d29dd..557c82a85 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -425,14 +425,11 @@ def main(): # Perform request resp = None if module._socket_path: - conn = Connection(aci.module._socket_path) - conn.set_params(aci.headers.get('Cookie'), aci.params) - info = conn.send_request(aci.method, '/{0}'.format(path), payload) - try: - aci.url = info.get('url') - except Exception: - info = conn.send_request(aci.method, '/{0}'.format(path), payload) - aci.url = info.get('url') + connect = Connection(aci.module._socket_path) + connect.set_params(aci.headers.get('Cookie'), aci.params) + info = connect.send_request(aci.method, '/{0}'.format(path), payload) + aci.url = info.get('url') + aci.httpapi_logs.extend(connect.pop_messages()) else: resp, info = fetch_url(module, aci.url, data=payload, From c70a13bc3471abe98d135ac17450f1a9e021aad3 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 14 Jul 2022 22:22:59 -0700 Subject: [PATCH 20/49] [ignore] Addition of code to include High Availability at task level --- plugins/doc_fragments/aci.py | 4 +- plugins/httpapi/aci.py | 64 ++++++++++++++--------------- plugins/modules/aci_cloud_subnet.py | 1 + 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/plugins/doc_fragments/aci.py b/plugins/doc_fragments/aci.py index 2bed3dc59..33d37904b 100644 --- a/plugins/doc_fragments/aci.py +++ b/plugins/doc_fragments/aci.py @@ -18,7 +18,10 @@ class ModuleDocFragment(object): - IP Address or hostname of APIC resolvable by Ansible control host. - If the value is not specified in the task, the value of environment variable C(ACI_HOST) will be used instead. type: str +<<<<<<< HEAD required: true +======= +>>>>>>> 82a0f1c ([ignore] Addition of code to include High Availability at task level) aliases: [ hostname ] port: description: @@ -31,7 +34,6 @@ class ModuleDocFragment(object): - The username to use for authentication. - If the value is not specified in the task, the value of environment variables C(ACI_USERNAME) or C(ANSIBLE_NET_USERNAME) will be used instead. type: str - default: admin aliases: [ user ] password: description: diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index ad6359d87..54574fd44 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -28,8 +28,7 @@ response from the controller. """ -import json -import re +import ast, json, re from ansible.errors import AnsibleConnectionFailure from ansible.module_utils._text import to_text @@ -48,7 +47,7 @@ def __init__(self, *args, **kwargs): self.host_counter = 0 self.entered_exception = False self.exception_message = None - + def set_params(self, auth, params): self.params = params self.auth = auth @@ -58,10 +57,13 @@ def get_backup_hosts(self): # append is used here to store the first value of the variable self.connection.get_option("host") in the 0th position # this is done because we keep changing the value of 'host' constantly when a list of hosts is provided. We always # want access to the original set of hosts. - self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) - return self.list_of_hosts + + # Case1: Host is provided in the task of a playbook + self.list_of_hosts.append(ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",")) except Exception: - return [] + # Case2: Host is not provided in the task of a playbook + self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) + return self.list_of_hosts def login(self, username, password): ''' Log in to APIC ''' @@ -75,7 +77,7 @@ def login(self, username, password): response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' - .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} + .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} self.connection.queue_message('vvvv', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) except Exception as exc: self.exception_message = exc @@ -87,7 +89,7 @@ def logout(self): path = '/api/aaaLogout.json' try: response, response_data = self.connection.send(path, {}, method=method) - except Exception as e: + except Exception as exc: msg = 'Error on attempt to logout from APIC. {0}'.format(e) raise ConnectionError(self._return_info(None, method, path, msg)) self.connection._auth = None @@ -97,7 +99,7 @@ def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' if data is None: data = {} - + # Set backup host/hosts from the inventory if/when provided self.get_backup_hosts() self.backup_hosts = self.list_of_hosts[0] @@ -105,9 +107,7 @@ def send_request(self, method, path, data): # The command timeout which is the response timeout from APIC can be set in the inventory # self.connection.set_option('persistent_command_timeout', 3) - # Case1: Host is provided in the task of a playbook if self.params.get('host') is not None: - self.connection.set_option("host", self.params.get('host')) if self.params.get('port') is not None: self.connection.set_option("port", self.params.get('port')) @@ -118,7 +118,7 @@ def send_request(self, method, path, data): if self.params.get('password') is not None: self.connection.set_option("password", self.params.get('password')) - # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a + # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a # task while ansible is running a playbook. if self.auth is not None and self.check_auth_from_private_key is None: self.connection._connected = False @@ -126,8 +126,8 @@ def send_request(self, method, path, data): self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} self.connection.queue_message('vvvv', 'Going through certificate authentication') self.connection._connected = True - - # Switch from certificate to credential authentication when private key is not specified in a + + # Switch from certificate to credential authentication when private key is not specified in a # task while ansible is running a playbook elif self.auth is None and self.check_auth_from_private_key is not None: self.check_auth_from_private_key = None @@ -142,17 +142,15 @@ def send_request(self, method, path, data): if self.params.get('validate_certs') is not None: self.connection.set_option("validate_certs", self.params.get('validate_certs')) - + if self.params.get('timeout') is not None: self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) - - # Case2: Host is not provided in the task of a playbook - elif self.backup_hosts: - try: - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) - except IndexError: - pass + + try: + self.connection.set_option("host", self.backup_hosts[self.host_counter]) + self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) + except IndexError: + pass # Initiation of request try: @@ -161,7 +159,7 @@ def send_request(self, method, path, data): except Exception as exc: self.exception_message = exc self.entered_exception = True - self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) + self.connection.queue_message('vvvv', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc)) self.handle_error() finally: if self.entered_exception: @@ -170,15 +168,17 @@ def send_request(self, method, path, data): # Final try/except block to close/exit operation try: response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) - except: - self.connection.queue_message('vvvv', 'Failed to receive response from {0}'.format(self.connection.get_option('host'))) + self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), + response.getcode())) + except Exception as exc: + self.exception_message = exc self.handle_error() - return self._verify_response(response, method, path, response_data) + return self._verify_response(response, method, path, response_data) def handle_error(self): - # We break the flow of code here when we are operating on a host at task level and/or hosts are also present in the inventory file. - if self.params.get('host') is not None: + # We break the flow of code here when we are operating on a single host at task level or single host in the inventory file. + # We do this to replicate the behavior of Ansible running via local connection on a single host. + if len(self.backup_hosts) == 1: raise AnsibleConnectionFailure( "{0}".format( self.exception_message @@ -186,11 +186,11 @@ def handle_error(self): ) self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation!!!") + raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host: {0}".format(self.exception_message)) self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - + def _verify_response(self, response, method, path, response_data): ''' Process the return code and response object from APIC ''' response_value = self._get_response_value(response_data) diff --git a/plugins/modules/aci_cloud_subnet.py b/plugins/modules/aci_cloud_subnet.py index 31845f629..2811537e0 100644 --- a/plugins/modules/aci_cloud_subnet.py +++ b/plugins/modules/aci_cloud_subnet.py @@ -63,6 +63,7 @@ description: - Determine if a vNet Gateway Router will be deployed or not. - Only used when it is an azure cloud apic. + default: no type: bool default: false state: From 72fc79f12eec2979f75f8a9543afce5a262801e4 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 18 Jul 2022 07:16:37 -0700 Subject: [PATCH 21/49] [ignore] Code for certificate authentication via inventory --- plugins/httpapi/aci.py | 161 ++++++++++++++++++++++++++++-------- plugins/module_utils/aci.py | 5 +- 2 files changed, 130 insertions(+), 36 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 54574fd44..d288ccc0b 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -14,6 +14,8 @@ from __future__ import (absolute_import, division, print_function) +from importlib.resources import path +from operator import methodcaller __metaclass__ = type DOCUMENTATION = """ @@ -28,42 +30,61 @@ response from the controller. """ -import ast, json, re +import ast, base64, json, os, re -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text +from ansible.module_utils._text import to_text, to_native from ansible.module_utils.connection import ConnectionError from ansible.plugins.httpapi import HttpApiBase +# Optional, only used for APIC signature-based authentication +try: + from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign + HAS_OPENSSL = True +except ImportError: + HAS_OPENSSL = False + +# Signature-based authentication using cryptography +try: + from cryptography.hazmat.primitives import serialization, hashes + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.backends import default_backend + HAS_CRYPTOGRAPHY = True +except ImportError: + HAS_CRYPTOGRAPHY = False + class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None - self.check_auth_from_private_key = None + self.check_auth_from_private_key = False self.backup_hosts = None self.list_of_hosts = [] self.host_counter = 0 + self.counter_checker = False self.entered_exception = False self.exception_message = None + self.response_auth = None + self.response_data_auth = None - def set_params(self, auth, params): + def get_params(self, auth, params, method, call_path, data): self.params = params self.auth = auth + self.method = method + self.call_path = call_path + self.data = data - def get_backup_hosts(self): + def get_backup_hosts_from_inventory(self): try: - # append is used here to store the first value of the variable self.connection.get_option("host") in the 0th position - # this is done because we keep changing the value of 'host' constantly when a list of hosts is provided. We always - # want access to the original set of hosts. + # append is used here to store the list of hosts in self.connection.get_option("host") in the 0th position. + # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. + # We always want access to the original list of hosts. - # Case1: Host is provided in the task of a playbook - self.list_of_hosts.append(ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",")) - except Exception: - # Case2: Host is not provided in the task of a playbook + # Case: Host is provided in the inventory self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) - return self.list_of_hosts + except Exception: + pass def login(self, username, password): ''' Log in to APIC ''' @@ -90,25 +111,28 @@ def logout(self): try: response, response_data = self.connection.send(path, {}, method=method) except Exception as exc: - msg = 'Error on attempt to logout from APIC. {0}'.format(e) + msg = 'Error on attempt to logout from APIC. {0}'.format(exc) raise ConnectionError(self._return_info(None, method, path, msg)) self.connection._auth = None self._verify_response(response, method, path, response_data) def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' - if data is None: - data = {} - - # Set backup host/hosts from the inventory if/when provided - self.get_backup_hosts() - self.backup_hosts = self.list_of_hosts[0] # The command timeout which is the response timeout from APIC can be set in the inventory # self.connection.set_option('persistent_command_timeout', 3) if self.params.get('host') is not None: + # Case: Host is provided in the task of a playbook + # We check if the list of hosts provided in two consecutive tasks are the same. If they are not the same we begin operation from the first host on the next + # task (Memory of the host in the list-reset). + # If they are the same, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). + if self.backup_hosts != ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") or not self.counter_checker: + self.host_counter = 0 + + self.backup_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") + if self.params.get('port') is not None: self.connection.set_option("port", self.params.get('port')) @@ -120,19 +144,21 @@ def send_request(self, method, path, data): # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a # task while ansible is running a playbook. - if self.auth is not None and self.check_auth_from_private_key is None: - self.connection._connected = False + if self.auth is not None: self.connection._auth = {'Cookie': '{0}'.format(self.auth)} - self.check_auth_from_private_key = {'Cookie': '{0}'.format(self.auth)} + self.check_auth_from_private_key = True self.connection.queue_message('vvvv', 'Going through certificate authentication') + # Override @ensure_connect check self.connection._connected = True # Switch from certificate to credential authentication when private key is not specified in a # task while ansible is running a playbook - elif self.auth is None and self.check_auth_from_private_key is not None: - self.check_auth_from_private_key = None + elif self.auth is None and self.check_auth_from_private_key: + self.check_auth_from_private_key = False + # Continue Operation on the host via credential authentication. Memory of the host in the list-preserved. self.connection.queue_message('vvvv', 'Switching from certificate to credential authentication') - self.connection._connected = False + self.connection.set_option("host", self.backup_hosts[self.host_counter]) + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) if self.params.get('use_proxy') is not None: self.connection.set_option("use_proxy", self.params.get('use_proxy')) @@ -146,6 +172,24 @@ def send_request(self, method, path, data): if self.params.get('timeout') is not None: self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) + self.counter_checker = True + + else: + # Reset counter to start operation on first host in inventory. Memory of the host in the list-reset. + # This covers the scenario where a playbook contains back to back tasks with and without hosts specified at task level. + if self.counter_checker: + self.host_counter = 0 + self.counter_checker = False + # Set backup host/hosts from the inventory. Host is not provided in the task. + self.get_backup_hosts_from_inventory() + self.backup_hosts = self.list_of_hosts[0] + # Note: session_key takes precedence over password + if self.connection.get_option("session_key") is not None: + self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} + self.connection.queue_message('vvvv', 'Going through certificate authentication') + # Override @ensure_connect check + self.connection._connected = True + try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) @@ -164,6 +208,12 @@ def send_request(self, method, path, data): finally: if self.entered_exception: self.entered_exception = False + if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): + if self.host_counter >= len(self.backup_hosts): + raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host: {0}".format(self.exception_message)) + self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), + self.response_auth.getcode())) + return self._verify_response(self.response_auth, self.method, self.call_path, self.response_data_auth) self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) # Final try/except block to close/exit operation try: @@ -179,17 +229,25 @@ def handle_error(self): # We break the flow of code here when we are operating on a single host at task level or single host in the inventory file. # We do this to replicate the behavior of Ansible running via local connection on a single host. if len(self.backup_hosts) == 1: - raise AnsibleConnectionFailure( - "{0}".format( - self.exception_message - ) - ) + raise ConnectionError("{0}".format(self.exception_message)) self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host: {0}".format(self.exception_message)) self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + + if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): + self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) + try: + response_auth, response_data_auth = self.connection.send(self.call_path, self.data, method=self.method) + self.response_auth = response_auth + self.response_data_auth = response_data_auth + except Exception as exc: + self.exception_message = exc + self.connection.queue_message('vvvv', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc)) + self.handle_error() + else: + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) def _verify_response(self, response, method, path, response_data): ''' Process the return code and response object from APIC ''' @@ -227,3 +285,40 @@ def _return_info(self, response_code, method, path, msg, respond_data=None): info['msg'] = msg info['body'] = respond_data return info + + def cert_auth(self, path, method, payload=''): + ''' Perform APIC signature-based authentication, not the expected SSL client certificate authentication. ''' + + headers = dict() + + if payload is None: + payload = '' + + if os.path.exists(list(self.connection.get_option("session_key").values())[0]): + try: + permission = 'r' + if HAS_CRYPTOGRAPHY: + permission = 'rb' + with open(list(self.connection.get_option("session_key").values())[0], permission) as fh: + private_key_content = fh.read() + except Exception: + raise ConnectionError("Cannot open private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + try: + if HAS_CRYPTOGRAPHY: + sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend(),) + else: + sig_key = load_privatekey(FILETYPE_PEM, private_key_content) + except Exception: + raise ConnectionError("Cannot load private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + self.params['certificate_name'] = list(self.connection.get_option("session_key").keys())[0] + sig_request = method + path + payload + if HAS_CRYPTOGRAPHY: + sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) + else: + sig_signature = sign(sig_key, sig_request, 'sha256') + sig_dn = 'uni/userext/user-{0}/usercert-{1}'.format(self.connection.get_option("remote_user"), list(self.connection.get_option("session_key").keys())[0]) + headers['Cookie'] = 'APIC-Certificate-Algorithm=v1.0; ' +\ + 'APIC-Certificate-DN=%s; ' % sig_dn +\ + 'APIC-Certificate-Fingerprint=fingerprint; ' +\ + 'APIC-Request-Signature=%s' % to_native(base64.b64encode(sig_signature)) + return headers diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index c6766fffa..5d7138776 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1800,11 +1800,10 @@ def api_call(self, method): data = None resp = None if self.params.get('private_key'): - self.cert_auth(method=method, path=call_path, payload=data) - + self.cert_auth(path=call_path, payload=data, method=method) if self.module._socket_path: connect = Connection(self.module._socket_path) - connect.set_params(self.headers.get('Cookie'), self.params) + connect.get_params(self.headers.get('Cookie'), self.params, method, '/{0}'.format(call_path), data) info = connect.send_request(method, '/{0}'.format(call_path), data) self.url = info.get('url') self.httpapi_logs.extend(connect.pop_messages()) From 6a16675150d1c18488834acc497921d35355a241 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 18 Jul 2022 11:35:34 -0700 Subject: [PATCH 22/49] [ignore] Fixed login issue at the task level when session_key is present in the inventory --- plugins/httpapi/aci.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index d288ccc0b..03acf64ab 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -128,7 +128,7 @@ def send_request(self, method, path, data): # We check if the list of hosts provided in two consecutive tasks are the same. If they are not the same we begin operation from the first host on the next # task (Memory of the host in the list-reset). # If they are the same, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.backup_hosts != ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") or not self.counter_checker: + if self.backup_hosts != ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") or self.counter_checker is False: self.host_counter = 0 self.backup_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") @@ -148,7 +148,7 @@ def send_request(self, method, path, data): self.connection._auth = {'Cookie': '{0}'.format(self.auth)} self.check_auth_from_private_key = True self.connection.queue_message('vvvv', 'Going through certificate authentication') - # Override @ensure_connect check + # Override parameter in @ensure_connect self.connection._connected = True # Switch from certificate to credential authentication when private key is not specified in a @@ -172,6 +172,10 @@ def send_request(self, method, path, data): if self.params.get('timeout') is not None: self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) + # If session_key is present in the inventory, password in the task is ignored. In order to avoid this, we explicitly set session_key to None. + if self.connection.get_option("session_key") is not None: + self.connection.set_option("session_key", None) + self.counter_checker = True else: @@ -187,7 +191,7 @@ def send_request(self, method, path, data): if self.connection.get_option("session_key") is not None: self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} self.connection.queue_message('vvvv', 'Going through certificate authentication') - # Override @ensure_connect check + # Override parameter in @ensure_connect self.connection._connected = True try: From 6250b6e228d81ec27616ea64ddbcf75e62ceb493 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sat, 23 Jul 2022 08:23:12 -0700 Subject: [PATCH 23/49] [minor_change] Revert complete to remove HTTP error handling --- plugins/httpapi/aci.py | 169 ++++++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 77 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 03acf64ab..2c8d94b12 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -58,38 +58,39 @@ class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None - self.check_auth_from_private_key = False + self.check_auth_from_private_key_task = False + self.check_auth_from_credential_task = False self.backup_hosts = None - self.list_of_hosts = [] + self.inventory_hosts = [] self.host_counter = 0 - self.counter_checker = False + self.counter_task = False + self.counter_inventory = False self.entered_exception = False + self.entered_http_error = None + self.entered_connection_error_on_last_host = False self.exception_message = None self.response_auth = None self.response_data_auth = None + self.r_d = None + self.r = None def get_params(self, auth, params, method, call_path, data): self.params = params self.auth = auth - self.method = method - self.call_path = call_path - self.data = data + def get_backup_hosts_from_inventory(self): try: - # append is used here to store the list of hosts in self.connection.get_option("host") in the 0th position. - # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. - # We always want access to the original list of hosts. - # Case: Host is provided in the inventory - self.list_of_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) + self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) except Exception: - pass + # Case: Host is provided in the memory inventory + self.inventory_hosts.append(self.connection.get_option("host")) def login(self, username, password): ''' Log in to APIC ''' # Perform login request - self.connection.queue_message('log', 'Establishing connection to {0}'.format(self.connection.get_option('host'))) + self.connection.queue_message('step:', 'Establishing connection to {0}'.format(self.connection.get_option('host'))) method = 'POST' path = '/api/aaaLogin.json' payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} @@ -98,12 +99,11 @@ def login(self, username, password): response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) self.connection._auth = {'Cookie': 'APIC-Cookie={0}' - .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} - self.connection.queue_message('vvvv', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) - except Exception as exc: - self.exception_message = exc - self.connection.queue_message('vvvv', '{0}'.format(exc)) - self.handle_error() + .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} + self.connection.queue_message('step:', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) + except Exception as exc_login: + self.connection.queue_message('step:', '{0}'.format(exc_login)) + self.handle_connection_error(exc_login) def logout(self): method = 'POST' @@ -119,19 +119,24 @@ def logout(self): def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' - # The command timeout which is the response timeout from APIC can be set in the inventory - # self.connection.set_option('persistent_command_timeout', 3) + #The command timeout which is the response timeout from APIC can be set in the inventory + #self.connection.set_option('persistent_command_timeout', 2) if self.params.get('host') is not None: + self.counter_task = True # Case: Host is provided in the task of a playbook + task_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") + # We check if the list of hosts provided in two consecutive tasks are the same. If they are not the same we begin operation from the first host on the next # task (Memory of the host in the list-reset). # If they are the same, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.backup_hosts != ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") or self.counter_checker is False: + if self.counter_inventory or self.backup_hosts != task_hosts: self.host_counter = 0 + self.counter_inventory = False + self.connection._connected = False - self.backup_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") + self.backup_hosts = task_hosts if self.params.get('port') is not None: self.connection.set_option("port", self.params.get('port')) @@ -145,20 +150,26 @@ def send_request(self, method, path, data): # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a # task while ansible is running a playbook. if self.auth is not None: + if self.check_auth_from_credential_task: + self.host_counter = 0 + self.check_auth_from_credential_task = False self.connection._auth = {'Cookie': '{0}'.format(self.auth)} - self.check_auth_from_private_key = True - self.connection.queue_message('vvvv', 'Going through certificate authentication') + self.check_auth_from_private_key_task = True + self.connection.queue_message('step:', 'Setting certificate authentication at task level') # Override parameter in @ensure_connect self.connection._connected = True # Switch from certificate to credential authentication when private key is not specified in a # task while ansible is running a playbook - elif self.auth is None and self.check_auth_from_private_key: - self.check_auth_from_private_key = False + elif self.auth is None and self.check_auth_from_private_key_task: + self.host_counter = 0 + self.check_auth_from_private_key_task = False + self.check_auth_from_credential_task = True # Continue Operation on the host via credential authentication. Memory of the host in the list-preserved. - self.connection.queue_message('vvvv', 'Switching from certificate to credential authentication') - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + self.connection.queue_message('step:', 'Switching from certificate to credential authentication at the task level') + self.connection._connected = False + else: + self.check_auth_from_credential_task = True if self.params.get('use_proxy') is not None: self.connection.set_option("use_proxy", self.params.get('use_proxy')) @@ -176,83 +187,87 @@ def send_request(self, method, path, data): if self.connection.get_option("session_key") is not None: self.connection.set_option("session_key", None) - self.counter_checker = True - else: + self.counter_inventory = True + # Case: Hosts from the inventory are used + self.get_backup_hosts_from_inventory() # Reset counter to start operation on first host in inventory. Memory of the host in the list-reset. # This covers the scenario where a playbook contains back to back tasks with and without hosts specified at task level. - if self.counter_checker: + if self.counter_task or self.backup_hosts != self.inventory_hosts[0]: self.host_counter = 0 - self.counter_checker = False + self.counter_task = False + self.connection._connected = False + # Set backup host/hosts from the inventory. Host is not provided in the task. - self.get_backup_hosts_from_inventory() - self.backup_hosts = self.list_of_hosts[0] + self.backup_hosts = self.inventory_hosts[0] + # Note: session_key takes precedence over password if self.connection.get_option("session_key") is not None: + self.check_auth_from_private_key_task = True + self.connection.queue_message('step:', 'Setting certificate authentication from inventory') self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} - self.connection.queue_message('vvvv', 'Going through certificate authentication') # Override parameter in @ensure_connect self.connection._connected = True try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.connection.queue_message('vvvv', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) - except IndexError: - pass + self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) + except IndexError as exc: + raise ConnectionError("HERE {0}, {1}".format(self.backup_hosts, self.host_counter)) + + self.method = method + self.call_path = path + self.data = data - # Initiation of request try: response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), response.getcode())) - except Exception as exc: - self.exception_message = exc + self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) + except Exception as exc_response: self.entered_exception = True - self.connection.queue_message('vvvv', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc)) - self.handle_error() + self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_response)) + self.handle_connection_error(exc_response) finally: if self.entered_exception: self.entered_exception = False if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - if self.host_counter >= len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host: {0}".format(self.exception_message)) - self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), - self.response_auth.getcode())) - return self._verify_response(self.response_auth, self.method, self.call_path, self.response_data_auth) - self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) - # Final try/except block to close/exit operation - try: - response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('vvvv', 'Received response from {0} with HTTP: {1}'.format(self.connection.get_option('host'), - response.getcode())) - except Exception as exc: - self.exception_message = exc - self.handle_error() + # TO DO + return self.handle_connection_error(None) + else: + self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) + # Final try/except block to close/exit operation + try: + response, response_data = self.connection.send(path, data, method=method) + self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) + except Exception as exc_credential: + self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_credential)) + self.handle_connection_error(exc_credential) return self._verify_response(response, method, path, response_data) - def handle_error(self): - # We break the flow of code here when we are operating on a single host at task level or single host in the inventory file. - # We do this to replicate the behavior of Ansible running via local connection on a single host. - if len(self.backup_hosts) == 1: - raise ConnectionError("{0}".format(self.exception_message)) + def handle_connection_error(self, exc): self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host: {0}".format(self.exception_message)) - self.connection.queue_message('vvvv', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - + # The host_counter is reset here before the final error to accommodate the use of ignore_errors in the tasks + self.host_counter = 0 + raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option('host'), exc)) + self.connection.queue_message('step:', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) + self.connection.set_option("host", self.backup_hosts[self.host_counter]) if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - self.connection.queue_message('vvvv', 'Retrying request on {0}'.format(self.connection.get_option('host'))) + self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) try: - response_auth, response_data_auth = self.connection.send(self.call_path, self.data, method=self.method) - self.response_auth = response_auth - self.response_data_auth = response_data_auth - except Exception as exc: - self.exception_message = exc - self.connection.queue_message('vvvv', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc)) - self.handle_error() + response, response_data = self.connection.send(self.call_path, self.data, method=self.method) + self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), self.method, response.getcode())) + return self._verify_response(response, self.method, self.call_path, response_data) + except Exception as exc_certificate: + self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_certificate)) + self.handle_connection_error(exc_certificate) else: self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + def handle_httperror(self, exc_http_response): + self.entered_http_error = exc_http_response.code + self.connection.queue_message('step:', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc_http_response)) + return exc_http_response + def _verify_response(self, response, method, path, response_data): ''' Process the return code and response object from APIC ''' response_value = self._get_response_value(response_data) From edccf6ca6b99f0ffaf5f76b4a2323881427ccfa4 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 25 Jul 2022 07:11:25 -0700 Subject: [PATCH 24/49] [minor_change] Implemented a recursive function for certificate authentication --- plugins/httpapi/aci.py | 181 +++++++++++++++++++++--------------- plugins/module_utils/aci.py | 2 +- 2 files changed, 107 insertions(+), 76 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 2c8d94b12..b427ad9d7 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -59,27 +59,26 @@ def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None self.check_auth_from_private_key_task = False - self.check_auth_from_credential_task = False + self.check_auth_from_credentials_task = False + self.check_auth_from_private_key_inventory = False + self.check_auth_from_credentials_inventory = False self.backup_hosts = None self.inventory_hosts = [] self.host_counter = 0 self.counter_task = False self.counter_inventory = False - self.entered_exception = False - self.entered_http_error = None - self.entered_connection_error_on_last_host = False - self.exception_message = None - self.response_auth = None - self.response_data_auth = None - self.r_d = None - self.r = None - - def get_params(self, auth, params, method, call_path, data): + self.entered_exception_certificate = False + self.entered_exception_credentials = False + self.ignore_error_check = False + + def get_params(self, auth, params): self.params = params self.auth = auth - def get_backup_hosts_from_inventory(self): + # append is used here to store the list of hosts in self.connection.get_option("host") in the 0th position of the list. + # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. + # We always want access to the original list of hosts from the inventory when the tasks are running on them. try: # Case: Host is provided in the inventory self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) @@ -87,6 +86,7 @@ def get_backup_hosts_from_inventory(self): # Case: Host is provided in the memory inventory self.inventory_hosts.append(self.connection.get_option("host")) + # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted def login(self, username, password): ''' Log in to APIC ''' # Perform login request @@ -103,6 +103,7 @@ def login(self, username, password): self.connection.queue_message('step:', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) except Exception as exc_login: self.connection.queue_message('step:', '{0}'.format(exc_login)) + # Indirect recursion self.handle_connection_error(exc_login) def logout(self): @@ -116,24 +117,37 @@ def logout(self): self.connection._auth = None self._verify_response(response, method, path, response_data) + # One API call is made via each call to send_request from aci.py in module_utils + # As long as a host is active in the list we make sure that the API call goes through + # A switch like mechanism is heavily utilized in order to transition from - + # tasks opearating on hosts using credentials to tasks opearting on hosts using certificate and vice-versa in the same playbook + # tasks using hosts in inventory via session_key (certificate) to tasks using a password in the inventory and vice-versa in the same playbook + # tasks using hosts at task level to tasks using hosts in inventory and vice versa in the same playbook def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' #The command timeout which is the response timeout from APIC can be set in the inventory - #self.connection.set_option('persistent_command_timeout', 2) + #self.connection.set_option('persistent_command_timeout', 30) + # Note: Preference is given to Hosts mentioned at task level in the playbook if self.params.get('host') is not None: self.counter_task = True # Case: Host is provided in the task of a playbook task_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") - # We check if the list of hosts provided in two consecutive tasks are the same. If they are not the same we begin operation from the first host on the next - # task (Memory of the host in the list-reset). - # If they are the same, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.counter_inventory or self.backup_hosts != task_hosts: + # We check whether: + # 1.The list of hosts provided in two consecutive tasks are not the same + # 2.The previous task was running on the hosts in the inventory + # 3.ignore_check was set in the previous task + # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). + # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). + if self.counter_inventory or self.backup_hosts != task_hosts or self.ignore_error_check: self.host_counter = 0 + # We set the following to false as the list of hosts have changed from the previous task self.counter_inventory = False + self.ignore_error_check = False + # Enforce @ensure_connect self.connection._connected = False self.backup_hosts = task_hosts @@ -147,30 +161,27 @@ def send_request(self, method, path, data): if self.params.get('password') is not None: self.connection.set_option("password", self.params.get('password')) - # Start with certificate authentication (or) Switch from credential authentication to certificate authentication when private key is specified in a - # task while ansible is running a playbook. + # Start with certificate authentication if self.auth is not None: - if self.check_auth_from_credential_task: + # Check if the previous task was running with the credentials + if self.check_auth_from_credentials_task: self.host_counter = 0 - self.check_auth_from_credential_task = False - self.connection._auth = {'Cookie': '{0}'.format(self.auth)} + self.check_auth_from_credentials_task = False self.check_auth_from_private_key_task = True + self.connection._auth = {'Cookie': '{0}'.format(self.auth)} self.connection.queue_message('step:', 'Setting certificate authentication at task level') # Override parameter in @ensure_connect self.connection._connected = True - - # Switch from certificate to credential authentication when private key is not specified in a - # task while ansible is running a playbook - elif self.auth is None and self.check_auth_from_private_key_task: - self.host_counter = 0 - self.check_auth_from_private_key_task = False - self.check_auth_from_credential_task = True - # Continue Operation on the host via credential authentication. Memory of the host in the list-preserved. - self.connection.queue_message('step:', 'Switching from certificate to credential authentication at the task level') - self.connection._connected = False + # Start with credential authentication else: - self.check_auth_from_credential_task = True - + # Check if the previous task was running on certificate auth + if self.check_auth_from_private_key_task: + self.host_counter = 0 + # Enforce @ensure_connect + self.connection._connected = False + self.check_auth_from_private_key_task = False + self.check_auth_from_credentials_task = True + if self.params.get('use_proxy') is not None: self.connection.set_option("use_proxy", self.params.get('use_proxy')) @@ -183,19 +194,22 @@ def send_request(self, method, path, data): if self.params.get('timeout') is not None: self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) - # If session_key is present in the inventory, password in the task is ignored. In order to avoid this, we explicitly set session_key to None. + # If session_key is present in the inventory, password in the task is ignored. In order to avoid this, we explicitly set session_key to None + # to give preference to credentials/certificate in the task level if self.connection.get_option("session_key") is not None: self.connection.set_option("session_key", None) - else: + # If the task has no hosts and their credentials/certificate we need to operate on the hosts in the inventory self.counter_inventory = True # Case: Hosts from the inventory are used self.get_backup_hosts_from_inventory() # Reset counter to start operation on first host in inventory. Memory of the host in the list-reset. # This covers the scenario where a playbook contains back to back tasks with and without hosts specified at task level. - if self.counter_task or self.backup_hosts != self.inventory_hosts[0]: + if self.counter_task or self.backup_hosts != self.inventory_hosts[0] or self.ignore_error_check: self.host_counter = 0 self.counter_task = False + self.ignore_error_check = False + # Enforce @ensure_connect self.connection._connected = False # Set backup host/hosts from the inventory. Host is not provided in the task. @@ -203,68 +217,85 @@ def send_request(self, method, path, data): # Note: session_key takes precedence over password if self.connection.get_option("session_key") is not None: - self.check_auth_from_private_key_task = True + if self.check_auth_from_credentials_inventory: + self.host_counter = 0 + self.check_auth_from_credentials_inventory = False + self.check_auth_from_private_key_inventory = True self.connection.queue_message('step:', 'Setting certificate authentication from inventory') self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} # Override parameter in @ensure_connect self.connection._connected = True - + # No session_key provided. Use password instead + else: + if self.check_auth_from_private_key_inventory: + self.host_counter = 0 + # Enforce @ensure_connect + self.connection._connected = False + self.check_auth_from_private_key_inventory = False + self.check_auth_from_credentials_inventory = True + + # Start operation on the host try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) - except IndexError as exc: - raise ConnectionError("HERE {0}, {1}".format(self.backup_hosts, self.host_counter)) - - self.method = method - self.call_path = path - self.data = data + # This is only executed if there are no hosts mentioned in the task or the inventory + except Exception as exception_hosts: + raise ConnectionError("Critical Error {0}".format(exception_hosts)) + # If credentials are mentioned, the first attempt at login is perofrmed automatically via @ensure_connect before making a request. + # First attempt at making a request. try: response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) except Exception as exc_response: - self.entered_exception = True self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_response)) - self.handle_connection_error(exc_response) - finally: - if self.entered_exception: - self.entered_exception = False - if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - # TO DO - return self.handle_connection_error(None) - else: - self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) - # Final try/except block to close/exit operation - try: - response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) - except Exception as exc_credential: - self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_credential)) - self.handle_connection_error(exc_credential) + if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): + self.entered_exception_certificate = True + else: + self.entered_exception_credentials = True + # We don't call the handle_connection_error when ignore_errors is set. This check is to avoid switching of hosts + # when host_counter is reset. + if not self.ignore_error_check: + self.handle_connection_error(exc_response) + # finally block is always executed + finally: + if self.entered_exception_credentials: + self.entered_exception_credentials = False + self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) + #Final try/except block to close/exit operation when credentials are used + try: + response, response_data = self.connection.send(path, data, method=method) + self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) + except Exception as exc_credential: + self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_credential)) + self.handle_connection_error(exc_credential) + elif self.entered_exception_certificate: + self.entered_exception_certificate = False + if not self.ignore_error_check: + # Recursive function, if certificate is used + return self.send_request(method, path, data) + # Final return statement for response from the request function return self._verify_response(response, method, path, response_data) - def handle_connection_error(self, exc): + # Custom error handler + def handle_connection_error(self, exception): self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - # The host_counter is reset here before the final error to accommodate the use of ignore_errors in the tasks - self.host_counter = 0 - raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option('host'), exc)) + # We set ignore_error_check to True here to accommodate the use of ignore_errors and its consequence on the next task + self.ignore_error_check = True + # Base Case Connection Error + raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1} {2}".format(self.connection.get_option('host'), exception)) self.connection.queue_message('step:', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) - try: - response, response_data = self.connection.send(self.call_path, self.data, method=self.method) - self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), self.method, response.getcode())) - return self._verify_response(response, self.method, self.call_path, response_data) - except Exception as exc_certificate: - self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_certificate)) - self.handle_connection_error(exc_certificate) + return else: + # Login function is called until connection to a host is established or until all the hosts in the list are exhausted + # Indirect recursion self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + # Built-in-function def handle_httperror(self, exc_http_response): - self.entered_http_error = exc_http_response.code self.connection.queue_message('step:', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc_http_response)) return exc_http_response diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 5d7138776..fedf51693 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1803,7 +1803,7 @@ def api_call(self, method): self.cert_auth(path=call_path, payload=data, method=method) if self.module._socket_path: connect = Connection(self.module._socket_path) - connect.get_params(self.headers.get('Cookie'), self.params, method, '/{0}'.format(call_path), data) + connect.get_params(self.headers.get('Cookie'), self.params) info = connect.send_request(method, '/{0}'.format(call_path), data) self.url = info.get('url') self.httpapi_logs.extend(connect.pop_messages()) From e7995935b0bd395b38fdec060279a74ad413a7d2 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Tue, 26 Jul 2022 06:51:53 -0700 Subject: [PATCH 25/49] [minor_change] Use recursion for both certificate and credentials request function --- plugins/httpapi/aci.py | 116 +++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 63 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index b427ad9d7..46401a3ce 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -65,18 +65,17 @@ def __init__(self, *args, **kwargs): self.backup_hosts = None self.inventory_hosts = [] self.host_counter = 0 - self.counter_task = False - self.counter_inventory = False - self.entered_exception_certificate = False - self.entered_exception_credentials = False - self.ignore_error_check = False + self.entered_task = False + self.entered_inventory = False + self.entered_exception = False + self.connection_error_check = False def get_params(self, auth, params): self.params = params self.auth = auth def get_backup_hosts_from_inventory(self): - # append is used here to store the list of hosts in self.connection.get_option("host") in the 0th position of the list. + # append is used here to store the first list of hosts available in self.connection.get_option("host") in the 0th position of the list inventory_host. # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. # We always want access to the original list of hosts from the inventory when the tasks are running on them. try: @@ -120,7 +119,7 @@ def logout(self): # One API call is made via each call to send_request from aci.py in module_utils # As long as a host is active in the list we make sure that the API call goes through # A switch like mechanism is heavily utilized in order to transition from - - # tasks opearating on hosts using credentials to tasks opearting on hosts using certificate and vice-versa in the same playbook + # tasks operating on hosts using credentials to tasks opearting on hosts using certificate and vice-versa in the same playbook # tasks using hosts in inventory via session_key (certificate) to tasks using a password in the inventory and vice-versa in the same playbook # tasks using hosts at task level to tasks using hosts in inventory and vice versa in the same playbook def send_request(self, method, path, data): @@ -131,7 +130,7 @@ def send_request(self, method, path, data): # Note: Preference is given to Hosts mentioned at task level in the playbook if self.params.get('host') is not None: - self.counter_task = True + self.entered_task = True # Case: Host is provided in the task of a playbook task_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") @@ -139,14 +138,14 @@ def send_request(self, method, path, data): # We check whether: # 1.The list of hosts provided in two consecutive tasks are not the same # 2.The previous task was running on the hosts in the inventory - # 3.ignore_check was set in the previous task + # 3.connection_error_check was set in the previous task # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.counter_inventory or self.backup_hosts != task_hosts or self.ignore_error_check: + if self.entered_inventory or self.backup_hosts != task_hosts or self.connection_error_check: self.host_counter = 0 # We set the following to false as the list of hosts have changed from the previous task - self.counter_inventory = False - self.ignore_error_check = False + self.entered_inventory = False + self.connection_error_check = False # Enforce @ensure_connect self.connection._connected = False @@ -195,20 +194,20 @@ def send_request(self, method, path, data): self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) # If session_key is present in the inventory, password in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate in the task level + # to give preference to credentials/certificate at the task level if self.connection.get_option("session_key") is not None: self.connection.set_option("session_key", None) else: # If the task has no hosts and their credentials/certificate we need to operate on the hosts in the inventory - self.counter_inventory = True + self.entered_inventory = True # Case: Hosts from the inventory are used self.get_backup_hosts_from_inventory() # Reset counter to start operation on first host in inventory. Memory of the host in the list-reset. # This covers the scenario where a playbook contains back to back tasks with and without hosts specified at task level. - if self.counter_task or self.backup_hosts != self.inventory_hosts[0] or self.ignore_error_check: + if self.entered_task or self.backup_hosts != self.inventory_hosts[0] or self.connection_error_check: self.host_counter = 0 - self.counter_task = False - self.ignore_error_check = False + self.entered_task = False + self.connection_error_check = False # Enforce @ensure_connect self.connection._connected = False @@ -238,53 +237,36 @@ def send_request(self, method, path, data): try: self.connection.set_option("host", self.backup_hosts[self.host_counter]) self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) - # This is only executed if there are no hosts mentioned in the task or the inventory except Exception as exception_hosts: raise ConnectionError("Critical Error {0}".format(exception_hosts)) - # If credentials are mentioned, the first attempt at login is perofrmed automatically via @ensure_connect before making a request. - # First attempt at making a request. + # If the credentials are mentioned, the first attempt at login is performed automatically via @ensure_connect before making a request. try: response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) except Exception as exc_response: self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_response)) - if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - self.entered_exception_certificate = True - else: - self.entered_exception_credentials = True - # We don't call the handle_connection_error when ignore_errors is set. This check is to avoid switching of hosts - # when host_counter is reset. - if not self.ignore_error_check: - self.handle_connection_error(exc_response) + self.entered_exception = True + self.handle_connection_error(exc_response) + # finally block is always executed finally: - if self.entered_exception_credentials: - self.entered_exception_credentials = False + if self.entered_exception: + self.entered_exception = False self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) - #Final try/except block to close/exit operation when credentials are used - try: - response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) - except Exception as exc_credential: - self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_credential)) - self.handle_connection_error(exc_credential) - elif self.entered_exception_certificate: - self.entered_exception_certificate = False - if not self.ignore_error_check: - # Recursive function, if certificate is used + # Exit recursive function. All the hosts are exhausted + if not self.connection_error_check: return self.send_request(method, path, data) - # Final return statement for response from the request function + # return statement executed upon each successful response from the request function return self._verify_response(response, method, path, response_data) # Custom error handler def handle_connection_error(self, exception): self.host_counter += 1 if self.host_counter >= len(self.backup_hosts): - # We set ignore_error_check to True here to accommodate the use of ignore_errors and its consequence on the next task - self.ignore_error_check = True - # Base Case Connection Error - raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1} {2}".format(self.connection.get_option('host'), exception)) + # Base Case for send_request. All hosts are exhausted in the list. + self.connection_error_check = True + raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option('host'), exception)) self.connection.queue_message('step:', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) self.connection.set_option("host", self.backup_hosts[self.host_counter]) if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): @@ -344,23 +326,31 @@ def cert_auth(self, path, method, payload=''): if payload is None: payload = '' - if os.path.exists(list(self.connection.get_option("session_key").values())[0]): - try: - permission = 'r' - if HAS_CRYPTOGRAPHY: - permission = 'rb' - with open(list(self.connection.get_option("session_key").values())[0], permission) as fh: - private_key_content = fh.read() - except Exception: - raise ConnectionError("Cannot open private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) - try: - if HAS_CRYPTOGRAPHY: - sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend(),) - else: - sig_key = load_privatekey(FILETYPE_PEM, private_key_content) - except Exception: - raise ConnectionError("Cannot load private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) - self.params['certificate_name'] = list(self.connection.get_option("session_key").keys())[0] + try: + if HAS_CRYPTOGRAPHY: + key = list(self.connection.get_option("session_key").values())[0].encode() + sig_key = serialization.load_pem_private_key(key, password=None, backend=default_backend(),) + else: + sig_key = load_privatekey(FILETYPE_PEM, list(self.connection.get_option("session_key").values())[0]) + except Exception: + if os.path.exists(list(self.connection.get_option("session_key").values())[0]): + try: + permission = 'r' + if HAS_CRYPTOGRAPHY: + permission = 'rb' + with open(list(self.connection.get_option("session_key").values())[0], permission) as fh: + private_key_content = fh.read() + except Exception: + raise ConnectionError("Cannot open private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + try: + if HAS_CRYPTOGRAPHY: + sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend(),) + else: + sig_key = load_privatekey(FILETYPE_PEM, private_key_content) + except Exception: + raise ConnectionError("Cannot load private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + else: + raise ConnectionError("Provided private key {0} does not appear to be a private key.".format(list(self.connection.get_option("session_key").values())[0])) sig_request = method + path + payload if HAS_CRYPTOGRAPHY: sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) From 190ab735b0135927bc0e0bd1e9f579053550d9b8 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Tue, 23 Aug 2022 14:06:54 -0400 Subject: [PATCH 26/49] add test file to check connection --- plugins/httpapi/aci.py | 220 +- plugins/module_utils/aci.py | 14 +- plugins/modules/aci_rest.py | 2 +- .../targets/aci_tenant/tasks/certs/admin.crt | 14 + .../targets/aci_tenant/tasks/certs/admin.key | 16 + .../aci_tenant/tasks/certs/admin_invalid.key | 3 + .../tasks/httpapi_inventory_password.yml | 5055 +++++++++++++++++ .../targets/aci_tenant/tasks/main.yml | 12 +- 8 files changed, 5224 insertions(+), 112 deletions(-) create mode 100644 tests/integration/targets/aci_tenant/tasks/certs/admin.crt create mode 100644 tests/integration/targets/aci_tenant/tasks/certs/admin.key create mode 100644 tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key create mode 100644 tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 46401a3ce..14c10ef0f 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -58,6 +58,7 @@ class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None + self.params = None self.check_auth_from_private_key_task = False self.check_auth_from_credentials_task = False self.check_auth_from_private_key_inventory = False @@ -65,11 +66,9 @@ def __init__(self, *args, **kwargs): self.backup_hosts = None self.inventory_hosts = [] self.host_counter = 0 - self.entered_task = False - self.entered_inventory = False self.entered_exception = False self.connection_error_check = False - + def get_params(self, auth, params): self.params = params self.auth = auth @@ -100,34 +99,116 @@ def login(self, username, password): self.connection._auth = {'Cookie': 'APIC-Cookie={0}' .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} self.connection.queue_message('step:', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) - except Exception as exc_login: - self.connection.queue_message('step:', '{0}'.format(exc_login)) - # Indirect recursion - self.handle_connection_error(exc_login) + except Exception: + # The exception here is transferred to the response exception + raise def logout(self): method = 'POST' path = '/api/aaaLogout.json' + payload = {"aaaUser":{"attributes":{"name":self.connection.get_option("remote_user")}}} + data = json.dumps(payload) try: - response, response_data = self.connection.send(path, {}, method=method) - except Exception as exc: - msg = 'Error on attempt to logout from APIC. {0}'.format(exc) + response, response_data = self.connection.send(path, data, method=method) + except Exception as exc_logout: + msg = 'Error on attempt to logout from APIC. {0}'.format(exc_logout) raise ConnectionError(self._return_info(None, method, path, msg)) self.connection._auth = None self._verify_response(response, method, path, response_data) + def set_parameters(self, method, path, data): + if self.params.get('port') is not None: + self.connection.set_option("port", self.params.get('port')) + + if self.params.get('username') is not None: + self.connection.set_option("remote_user", self.params.get('username')) + + if self.params.get('use_proxy') is not None: + self.connection.set_option("use_proxy", self.params.get('use_proxy')) + + if self.params.get('use_ssl') is not None: + self.connection.set_option("use_ssl", self.params.get('use_ssl')) + + if self.params.get('validate_certs') is not None: + self.connection.set_option("validate_certs", self.params.get('validate_certs')) + + # The command timeout which is the response timeout from APIC + if self.params.get('timeout') is not None: + self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) + + # If the persistent_connect_timeout is less than the response timeout from APIC the persistent socket connection will fail + if self.params.get('timeout') is not None: + self.connection.set_option('persistent_connect_timeout', self.params.get('timeout') + 30) + else: + # For inventory + self.connection.set_option('persistent_connect_timeout', self.connection.get_option('persistent_command_timeout') + 30) + + # Start with certificate authentication + if self.auth is not None: + # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None + # to give preference to credentials/certificate at the task level. + if self.connection.get_option("session_key") is not None: + self.connection.set_option("session_key", None) + # Check if the previous task was running with the credentials at the task level/inventory or the certificate in the inventory + if self.check_auth_from_credentials_task or self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_inventory: + self.host_counter = 0 + self.check_auth_from_credentials_task = False + self.check_auth_from_credentials_inventory = False + self.check_auth_from_private_key_inventory = False + self.check_auth_from_private_key_task = True + self.connection._auth = {'Cookie': '{0}'.format(self.auth)} + self.connection.queue_message('step:', 'Setting certificate authentication at task level') + # Override parameter in @ensure_connect + self.connection._connected = True + # Start with credential authentication + elif self.params.get('password') is not None: + # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None + # to give preference to credentials/certificate at the task level. + if self.connection.get_option("session_key") is not None: + self.connection.set_option("session_key", None) + self.connection.set_option("password", self.params.get('password')) + # Check if the previous task was running with the certificate in at the task level/inventory or the credentials in the inventory + if self.check_auth_from_private_key_task or self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_inventory: + self.host_counter = 0 + # Enforce @ensure_connect + self.connection._connected = False + self.check_auth_from_private_key_task = False + self.check_auth_from_credentials_inventory = False + self.check_auth_from_private_key_inventory = False + self.check_auth_from_credentials_task = True + self.connection.queue_message('step:', 'Setting credential authentication at task level') + # Note: session_key takes precedence over password if both of them are present in the inventory + elif self.connection.get_option("session_key") is not None: + # Check if the previous task was running with the certificate at the task level or the credentials in the inventory/task + if self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_task or self.check_auth_from_credentials_task: + self.host_counter = 0 + self.check_auth_from_credentials_inventory = False + self.check_auth_from_private_key_task = False + self.check_auth_from_credentials_task = False + self.check_auth_from_private_key_inventory = True + self.connection.queue_message('step:', 'Setting certificate authentication from inventory') + self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} + # Override parameter in @ensure_connect + self.connection._connected = True + # No session_key provided. Use password in the inventory instead + else: + # Check if the previous task was running with the certificate at the task/inventory level or the credentials in the task + if self.check_auth_from_private_key_inventory or self.check_auth_from_private_key_task or self.check_auth_from_credentials_task: + self.host_counter = 0 + # Enforce @ensure_connect + self.connection._connected = False + self.check_auth_from_private_key_inventory = False + self.check_auth_from_private_key_task = False + self.check_auth_from_credentials_task = False + self.check_auth_from_credentials_inventory = True + self.connection.queue_message('step:', 'Setting credential authentication from inventory') + # One API call is made via each call to send_request from aci.py in module_utils # As long as a host is active in the list we make sure that the API call goes through - # A switch like mechanism is heavily utilized in order to transition from - - # tasks operating on hosts using credentials to tasks opearting on hosts using certificate and vice-versa in the same playbook - # tasks using hosts in inventory via session_key (certificate) to tasks using a password in the inventory and vice-versa in the same playbook - # tasks using hosts at task level to tasks using hosts in inventory and vice versa in the same playbook + # A switch like mechanism is heavily utilized to support the same playbook consisting of tasks running on different hosts def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' - #The command timeout which is the response timeout from APIC can be set in the inventory - #self.connection.set_option('persistent_command_timeout', 30) - # Note: Preference is given to Hosts mentioned at task level in the playbook if self.params.get('host') is not None: self.entered_task = True @@ -137,108 +218,38 @@ def send_request(self, method, path, data): # We check whether: # 1.The list of hosts provided in two consecutive tasks are not the same - # 2.The previous task was running on the hosts in the inventory - # 3.connection_error_check was set in the previous task + # 2.connection_error_check was set in the previous task # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.entered_inventory or self.backup_hosts != task_hosts or self.connection_error_check: + if self.backup_hosts != task_hosts or self.connection_error_check: self.host_counter = 0 - # We set the following to false as the list of hosts have changed from the previous task - self.entered_inventory = False self.connection_error_check = False - # Enforce @ensure_connect self.connection._connected = False self.backup_hosts = task_hosts - - if self.params.get('port') is not None: - self.connection.set_option("port", self.params.get('port')) - - if self.params.get('username') is not None: - self.connection.set_option("remote_user", self.params.get('username')) - - if self.params.get('password') is not None: - self.connection.set_option("password", self.params.get('password')) - - # Start with certificate authentication - if self.auth is not None: - # Check if the previous task was running with the credentials - if self.check_auth_from_credentials_task: - self.host_counter = 0 - self.check_auth_from_credentials_task = False - self.check_auth_from_private_key_task = True - self.connection._auth = {'Cookie': '{0}'.format(self.auth)} - self.connection.queue_message('step:', 'Setting certificate authentication at task level') - # Override parameter in @ensure_connect - self.connection._connected = True - # Start with credential authentication - else: - # Check if the previous task was running on certificate auth - if self.check_auth_from_private_key_task: - self.host_counter = 0 - # Enforce @ensure_connect - self.connection._connected = False - self.check_auth_from_private_key_task = False - self.check_auth_from_credentials_task = True - - if self.params.get('use_proxy') is not None: - self.connection.set_option("use_proxy", self.params.get('use_proxy')) - - if self.params.get('use_ssl') is not None: - self.connection.set_option("use_ssl", self.params.get('use_ssl')) - - if self.params.get('validate_certs') is not None: - self.connection.set_option("validate_certs", self.params.get('validate_certs')) - - if self.params.get('timeout') is not None: - self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) - - # If session_key is present in the inventory, password in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate at the task level - if self.connection.get_option("session_key") is not None: - self.connection.set_option("session_key", None) else: - # If the task has no hosts and their credentials/certificate we need to operate on the hosts in the inventory - self.entered_inventory = True # Case: Hosts from the inventory are used self.get_backup_hosts_from_inventory() - # Reset counter to start operation on first host in inventory. Memory of the host in the list-reset. - # This covers the scenario where a playbook contains back to back tasks with and without hosts specified at task level. - if self.entered_task or self.backup_hosts != self.inventory_hosts[0] or self.connection_error_check: + + # We check whether: + # 1.The list of hosts provided in two consecutive tasks are not the same + # 2.connection_error_check was set in the previous task + # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). + # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). + if self.backup_hosts != self.inventory_hosts[0] or self.connection_error_check: self.host_counter = 0 - self.entered_task = False self.connection_error_check = False - # Enforce @ensure_connect self.connection._connected = False # Set backup host/hosts from the inventory. Host is not provided in the task. self.backup_hosts = self.inventory_hosts[0] - - # Note: session_key takes precedence over password - if self.connection.get_option("session_key") is not None: - if self.check_auth_from_credentials_inventory: - self.host_counter = 0 - self.check_auth_from_credentials_inventory = False - self.check_auth_from_private_key_inventory = True - self.connection.queue_message('step:', 'Setting certificate authentication from inventory') - self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} - # Override parameter in @ensure_connect - self.connection._connected = True - # No session_key provided. Use password instead - else: - if self.check_auth_from_private_key_inventory: - self.host_counter = 0 - # Enforce @ensure_connect - self.connection._connected = False - self.check_auth_from_private_key_inventory = False - self.check_auth_from_credentials_inventory = True + # Set parameters from the task + self.set_parameters(method, path, data) + # Start operation on the host - try: - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) - except Exception as exception_hosts: - raise ConnectionError("Critical Error {0}".format(exception_hosts)) + self.connection.set_option("host", self.backup_hosts[self.host_counter]) + self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) # If the credentials are mentioned, the first attempt at login is performed automatically via @ensure_connect before making a request. try: @@ -273,7 +284,6 @@ def handle_connection_error(self, exception): return else: # Login function is called until connection to a host is established or until all the hosts in the list are exhausted - # Indirect recursion self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) # Built-in-function @@ -290,10 +300,7 @@ def _verify_response(self, response, method, path, response_data): respond_data = response_value response_code = response.getcode() path = response.geturl() - if response_code == 400: - msg = str(response) - else: - msg = '{0} ({1} bytes)'.format(response.msg, len(response_value)) + msg = '{0} ({1} bytes)'.format(response.msg, len(response_value)) return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): @@ -362,3 +369,4 @@ def cert_auth(self, path, method, payload=''): 'APIC-Certificate-Fingerprint=fingerprint; ' +\ 'APIC-Request-Signature=%s' % to_native(base64.b64encode(sig_signature)) return headers + diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index fedf51693..1d889f65a 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -327,6 +327,7 @@ def destination_epg_spec(): class ACIModule(object): def __init__(self, module): + self.default_username = 'admin' self.module = module self.params = module.params self.result = dict(changed=False) @@ -428,6 +429,7 @@ def login(self): if self.params.get("port") is not None: url = "%(protocol)s://%(host)s:%(port)s/api/aaaLogin.json" % self.params else: +<<<<<<< HEAD url = "%(protocol)s://%(host)s/api/aaaLogin.json" % self.params payload = { "aaaUser": { @@ -445,6 +447,15 @@ def login(self): timeout=self.params.get("timeout"), use_proxy=self.params.get("use_proxy"), ) +======= + url = '%(protocol)s://%(host)s/api/aaaLogin.json' % self.params + payload = {'aaaUser': {'attributes': {'name': self.params.get('username', self.default_username), 'pwd': self.params.get('password')}}} + resp, auth = fetch_url(self.module, url, + data=json.dumps(payload), + method='POST', + timeout=self.params.get('timeout'), + use_proxy=self.params.get('use_proxy')) +>>>>>>> cde59eb (add test file to check connection) # Handle APIC response if auth.get("status") != 200: @@ -513,7 +524,7 @@ def cert_auth(self, path=None, payload="", method=None): self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params) if self.params.get('certificate_name') is None: - self.params['certificate_name'] = self.params.get('username', 'admin') + self.params['certificate_name'] = self.params.get('username', self.default_username) # NOTE: ACI documentation incorrectly adds a space between method and path sig_request = method + path + payload if HAS_CRYPTOGRAPHY: @@ -1802,6 +1813,7 @@ def api_call(self, method): if self.params.get('private_key'): self.cert_auth(path=call_path, payload=data, method=method) if self.module._socket_path: + self.params['validate_certs'] = False connect = Connection(self.module._socket_path) connect.get_params(self.headers.get('Cookie'), self.params) info = connect.send_request(method, '/{0}'.format(call_path), data) diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 557c82a85..935651243 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -426,7 +426,7 @@ def main(): resp = None if module._socket_path: connect = Connection(aci.module._socket_path) - connect.set_params(aci.headers.get('Cookie'), aci.params) + connect.get_params(aci.headers.get('Cookie'), aci.params) info = connect.send_request(aci.method, '/{0}'.format(path), payload) aci.url = info.get('url') aci.httpapi_logs.extend(connect.pop_messages()) diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin.crt b/tests/integration/targets/aci_tenant/tasks/certs/admin.crt new file mode 100644 index 000000000..cfac5531e --- /dev/null +++ b/tests/integration/targets/aci_tenant/tasks/certs/admin.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICODCCAaGgAwIBAgIJAIt8XMntue0VMA0GCSqGSIb3DQEBCwUAMDQxDjAMBgNV +BAMMBUFkbWluMRUwEwYDVQQKDAxZb3VyIENvbXBhbnkxCzAJBgNVBAYTAlVTMCAX +DTE4MDEwOTAwNTk0NFoYDzIxMTcxMjE2MDA1OTQ0WjA0MQ4wDAYDVQQDDAVBZG1p +bjEVMBMGA1UECgwMWW91ciBDb21wYW55MQswCQYDVQQGEwJVUzCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAohG/7axtt7CbSaMP7r+2mhTKbNgh0Ww36C7Ta14i +v+VmLyKkQHnXinKGhp6uy3Nug+15a+eIu7CrgpBVMQeCiWfsnwRocKcQJWIYDrWl +XHxGQn31yYKR6mylE7Dcj3rMFybnyhezr5D8GcP85YRPmwG9H2hO/0Y1FUnWu9Iw +AQkCAwEAAaNQME4wHQYDVR0OBBYEFD0jLXfpkrU/ChzRvfruRs/fy1VXMB8GA1Ud +IwQYMBaAFD0jLXfpkrU/ChzRvfruRs/fy1VXMAwGA1UdEwQFMAMBAf8wDQYJKoZI +hvcNAQELBQADgYEAOmvre+5tgZ0+F3DgsfxNQqLTrGiBgGCIymPkP/cBXXkNuJyl +3ac7tArHQc7WEA4U2R2rZbEq8FC3UJJm4nUVtCPvEh3G9OhN2xwYev79yt6pIn/l +KU0Td2OpVyo0eLqjoX5u2G90IBWzhyjFbo+CcKMrSVKj1YOdG0E3OuiJf00= +-----END CERTIFICATE----- diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin.key b/tests/integration/targets/aci_tenant/tasks/certs/admin.key new file mode 100644 index 000000000..63bb00cc0 --- /dev/null +++ b/tests/integration/targets/aci_tenant/tasks/certs/admin.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKIRv+2sbbewm0mj +D+6/tpoUymzYIdFsN+gu02teIr/lZi8ipEB514pyhoaerstzboPteWvniLuwq4KQ +VTEHgoln7J8EaHCnECViGA61pVx8RkJ99cmCkepspROw3I96zBcm58oXs6+Q/BnD +/OWET5sBvR9oTv9GNRVJ1rvSMAEJAgMBAAECgYByu3QO0qF9h7X3JEu0Ld4cKBnB +giQ2uJC/et7KxIJ/LOvw9GopBthyt27KwG1ntBkJpkTuAaQHkyNns7vLkNB0S0IR ++owVFEcKYq9VCHTaiQU8TDp24gN+yPTrpRuH8YhDVq5SfVdVuTMgHVQdj4ya4VlF +Gj+a7+ipxtGiLsVGrQJBAM7p0Fm0xmzi+tBOASUAcVrPLcteFIaTBFwfq16dm/ON +00Khla8Et5kMBttTbqbukl8mxFjBEEBlhQqb6EdQQ0sCQQDIhHx1a9diG7y/4DQA +4KvR3FCYwP8PBORlSamegzCo+P1OzxiEo0amX7yQMA5UyiP/kUsZrme2JBZgna8S +p4R7AkEAr7rMhSOPUnMD6V4WgsJ5g1Jp5kqkzBaYoVUUSms5RASz4+cwJVCwTX91 +Y1jcpVIBZmaaY3a0wrx13ajEAa0dOQJBAIpjnb4wqpsEh7VpmJqOdSdGxb1XXfFQ +sA0T1OQYqQnFppWwqrxIL+d9pZdiA1ITnNqyvUFBNETqDSOrUHwwb2cCQGArE+vu +ffPUWQ0j+fiK+covFG8NL7H+26NSGB5+Xsn9uwOGLj7K/YT6CbBtr9hJiuWjM1Al +0V4ltlTuu2mTMaw= +-----END PRIVATE KEY----- diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key b/tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key new file mode 100644 index 000000000..22f5fae45 --- /dev/null +++ b/tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +This is an invalid private key +-----END PRIVATE KEY----- diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml new file mode 100644 index 000000000..2630239f4 --- /dev/null +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml @@ -0,0 +1,5055 @@ +##TEST CONNECTION + +## If credentials in the inventory are used. Remove session_key from inventory ## +## For testing on the CI we hardcode the session_key in the playbook ## + +## Active Host (PASS) +# Run on single active Host at task level using credentials +# Run on single active Host at task level using certificate +# Run on single active Host at task level using credentials +# Run on single active Host in inventory using credentials +# Run on single active Host at task level using credentials +# Run on single active Host at task level using certificate +# Run on single active Host in inventory using credentials +# Run on single active Host at task level using certificate + +## Inactive Host (FAIL use ignore errors) +# Run on single active Host at task level using credentials +# Run on single active Host at task level using certificate +# Run on single active Host at task level using credentials +# Run on single active Host in inventory using credentials +# Run on single active Host at task level using credentials +# Run on single active Host at task level using certificate +# Run on single active Host at task level using credentials +# Run on single active Host in inventory using credentials + +## First Host is Active (PASS on First Host) +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials + +## Second Host is Active (PASS on Second Host) +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials + +## Last Host is Active (PASS on last Host) +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials + +#None of the Hosts are Active (FAIL use ignore errors) +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts at task level using certificate +# Run on multiple Hosts at task level using credentials +# Run on multiple Hosts in inventory using credentials + + - name: Set vars + set_fact: + aci_info_single_host_active: &aci_info_single_host_active + host: "{{ ansible_single_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_single_host_active_certificate: &aci_info_single_host_active_certificate + host: "{{ ansible_single_host_active }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_second_single_host_active: &aci_info_second_single_host_active + host: "{{ ansible_second_single_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + use_proxy: '{{ use_proxy | default(true) }}' + port: '{{ ansible_httpapi_port }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_third_single_host_active: &aci_info_third_single_host_active + host: "{{ ansible_third_single_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Add a tenant using an XML string + cisco.aci.aci_rest: + <<: *aci_info_single_host_active + path: /api/mo/uni.xml + method: post + content: '' + ignore_errors: yes + + - name: Add user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: present + + - name: Add user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_second_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: present + + - name: Add user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_third_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: present + + - name: Set Single active host in inventory + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_single_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + output_level: debug + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_1 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_2 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_2 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_2 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_3 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_3 + + - name: Add a new AP on single active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_3 + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_4 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_4 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_4 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_5 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_5 + + - name: Add a new AP on single active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_5 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_6 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_6 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_6 + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_7 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_7 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_7 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_8 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_8 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_8 + + - name: Verify operations on single active host + assert: + that: + - task_pwd_delete_tenant_single_active_host_1.current == [] + - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1 is changed + - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_1 is changed + - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_2 is changed + - task_cert_delete_tenant_single_active_host_2.current == [] + - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2 is changed + - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_2 is changed + - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_single_active_host_3 is changed + - task_pwd_delete_tenant_single_active_host_3.current == [] + - task_pwd_delete_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_3 is changed + - task_pwd_add_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_3 is changed + - task_pwd_add_ap_single_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_4 is changed + - inventory_pwd_delete_tenant_single_active_host_4.current == [] + - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4 is changed + - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_4 is changed + - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_single_active_host_5 is changed + - task_pwd_delete_tenant_single_active_host_5.current == [] + - task_pwd_delete_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_5 is changed + - task_pwd_add_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_5 is changed + - task_pwd_add_ap_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_6 is changed + - task_cert_delete_tenant_single_active_host_6.current == [] + - task_cert_delete_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_6 is changed + - task_cert_add_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_6 is changed + - task_cert_add_ap_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_7 is changed + - inventory_pwd_delete_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_single_active_host_7.current == [] + - inventory_pwd_add_tenant_single_active_host_7 is changed + - inventory_pwd_add_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_7 is changed + - inventory_pwd_add_ap_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_single_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_8 is changed + - task_cert_delete_tenant_single_active_host_8.current == [] + - task_cert_delete_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_8 is changed + - task_cert_add_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_8 is changed + - task_cert_add_ap_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + + +###################################################################### + + - name: Set vars + set_fact: + aci_info_single_host_inactive: &aci_info_single_host_inactive + host: "{{ ansible_single_host_inactive }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_single_host_inactive_certificate: &aci_info_single_host_inactive_certificate + host: "{{ ansible_single_host_inactive }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set Single inactive host in inventory + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_single_host_inactive }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + +############################################################################### + + - name: Set vars + set_fact: + aci_info_first_host_active: &aci_info_first_host_active + host: "{{ ansible_first_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_first_host_active_certificate: &aci_info_first_host_active_certificate + host: "{{ ansible_first_host_active }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set the inventory with the first host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_first_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_1 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_1 + + - name: Add a new AP on the first active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_1 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_2 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_2 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_2 + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_3 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_3 + + - name: Add a new AP on the first active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_3 + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_4 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_4 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_4 + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_5 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_5 + + - name: Add a new AP on the first active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_5 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_6 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_6 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_6 + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_7 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_7 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_7 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_8 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_8 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_8 + + - name: Verify operations on first active host + assert: + that: + - task_pwd_delete_tenant_first_active_host_1.current == [] + - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1 is changed + - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_1 is changed + - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_2 is changed + - task_cert_delete_tenant_first_active_host_2.current == [] + - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2 is changed + - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_2 is changed + - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_3 is changed + - task_pwd_delete_tenant_first_active_host_3.current == [] + - task_pwd_delete_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_3 is changed + - task_pwd_add_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_3 is changed + - task_pwd_add_ap_first_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_4 is changed + - inventory_pwd_delete_tenant_first_active_host_4.current == [] + - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4 is changed + - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_4 is changed + - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_5 is changed + - task_pwd_delete_tenant_first_active_host_5.current == [] + - task_pwd_delete_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_5 is changed + - task_pwd_add_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_5 is changed + - task_pwd_add_ap_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_6 is changed + - task_cert_delete_tenant_first_active_host_6.current == [] + - task_cert_delete_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_6 is changed + - task_cert_add_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_6 is changed + - task_cert_add_ap_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_7 is changed + - inventory_pwd_delete_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_first_active_host_7.current == [] + - inventory_pwd_add_tenant_first_active_host_7 is changed + - inventory_pwd_add_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_7 is changed + - inventory_pwd_add_ap_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_first_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_8 is changed + - task_cert_delete_tenant_first_active_host_8.current == [] + - task_cert_delete_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_8 is changed + - task_cert_add_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_8 is changed + - task_cert_add_ap_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################## + + - name: Set vars + set_fact: + aci_info_second_active_host: &aci_info_second_active_host + host: "{{ ansible_second_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_second_active_host_certificate: &aci_info_second_active_host_certificate + host: "{{ ansible_second_host_active }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set the inventory with the second host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_second_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_1 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_1 + + - name: Add a new AP on the second active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_1 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_2 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_2 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_2 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_3 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_3 + + - name: Add a new AP on the second active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_3 + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_4 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_4 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_4 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_5 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_5 + + - name: Add a new AP on the second active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_5 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_6 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_6 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_6 + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_7 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_7 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_7 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_8 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_8 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_8 + + - name: Verify operations on second active host + assert: + that: + - task_pwd_delete_tenant_second_active_host_1.current == [] + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_1 is changed + - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_1 is changed + - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_2 is changed + - task_cert_delete_tenant_second_active_host_2.current == [] + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_2 is changed + - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_2 is changed + - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_3 is changed + - task_pwd_delete_tenant_second_active_host_3.current == [] + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_3 is changed + - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_3 is changed + - task_pwd_add_ap_second_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_4 is changed + - inventory_pwd_delete_tenant_second_active_host_4.current == [] + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_second_active_host_4 is changed + - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_4 is changed + - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_5 is changed + - task_pwd_delete_tenant_second_active_host_5.current == [] + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_5 is changed + - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_5 is changed + - task_pwd_add_ap_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_6 is changed + - task_cert_delete_tenant_second_active_host_6.current == [] + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_6 is changed + - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_6 is changed + - task_cert_add_ap_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_7 is changed + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_7.current == [] + - inventory_pwd_add_tenant_second_active_host_7 is changed + - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_7 is changed + - inventory_pwd_add_ap_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_second_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_8 is changed + - task_cert_delete_tenant_second_active_host_8.current == [] + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_8 is changed + - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_8 is changed + - task_cert_add_ap_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################### + + - name: Set vars + set_fact: + aci_info_last_active_host: &aci_info_last_active_host + host: "{{ ansible_last_host_active }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_last_active_host_certificate: &aci_info_last_active_host_certificate + host: "{{ ansible_last_host_active }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set the inventory with the last host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_last_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_1 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_1 + + - name: Add a new AP on the last active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_1 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_2 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_2 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_2 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_3 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_3 + + - name: Add a new AP on the last active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_3 + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_4 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_4 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_4 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_5 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_5 + + - name: Add a new AP on the last active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_5 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_6 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_6 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_6 + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_7 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_7 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_7 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_8 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_8 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_8 + + - name: Verify operations on last active host + assert: + that: + - task_pwd_delete_tenant_last_active_host_1.current == [] + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_1 is changed + - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_1 is changed + - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_2 is changed + - task_cert_delete_tenant_last_active_host_2.current == [] + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_2 is changed + - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_2 is changed + - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_3 is changed + - task_pwd_delete_tenant_last_active_host_3.current == [] + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_3 is changed + - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_3 is changed + - task_pwd_add_ap_last_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_4 is changed + - inventory_pwd_delete_tenant_last_active_host_4.current == [] + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_last_active_host_4 is changed + - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_4 is changed + - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_5 is changed + - task_pwd_delete_tenant_last_active_host_5.current == [] + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_5 is changed + - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_5 is changed + - task_pwd_add_ap_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_6 is changed + - task_cert_delete_tenant_last_active_host_6.current == [] + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_6 is changed + - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_6 is changed + - task_cert_add_ap_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_7 is changed + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_7.current == [] + - inventory_pwd_add_tenant_last_active_host_7 is changed + - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_7 is changed + - inventory_pwd_add_ap_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_last_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_8 is changed + - task_cert_delete_tenant_last_active_host_8.current == [] + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_8 is changed + - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_8 is changed + - task_cert_add_ap_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################### + + - name: Set vars + set_fact: + aci_info_all_hosts_inactive: &aci_info_all_hosts_inactive + host: "{{ ansible_all_hosts_inactive }}" + username: "{{ ansible_user }}" + password: "{{ ansible_ssh_pass }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set vars + set_fact: + aci_info_all_hosts_inactive_certificate: &aci_info_all_hosts_inactive_certificate + host: "{{ ansible_all_hosts_inactive }}" + username: "{{ ansible_user }}" + validate_certs: '{{ validate_certs | default(false) }}' + use_ssl: '{{ use_ssl | default(true) }}' + timeout: "{{ ansible_command_timeout }}" + output_level: debug + + - name: Set the inventory with all the hosts in the inventory as inactive + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_all_hosts_inactive }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + +############################################################################################# +#### Random Tets + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_1 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_single_active_host_1 + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_1 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_1 + + - name: Add a new AP on the first active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_1 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_1 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_1 + + - name: Add a new AP on the second active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_1 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_1 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_1 + + - name: Add a new AP on the last active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_1 + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_2 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_2 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_2 + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_2 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_2 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_2 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_2 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_2 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_2 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_2 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_2 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_2 + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Set hosts 1 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host1 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_4_random1 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_4_random1 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_4_random1 + + ## HTTP Error + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Set hosts 2 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host2 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Set hosts 3 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host3 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_4_random2 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_4_random2 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_4_random2 + + - name: Set hosts 4 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host4 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_4_random3 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_4_random3 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_4_random3 + + - name: Set hosts 5 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host5 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_4_random4 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_4_random4 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_4_random4 + + - name: Set hosts 6 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host6 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Verify random operations + assert: + that: + - task_pwd_delete_tenant_single_active_host_1.current == [] + - task_pwd_delete_tenant_second_single_active_host_1.current == [] + - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1 is changed + - task_pwd_add_tenant_second_single_active_host_1 is changed + - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_tenant_second_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_1 is changed + - task_pwd_add_ap_second_single_active_host_1 is changed + - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_add_ap_second_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_1.current == [] + - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1 is changed + - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_1 is changed + - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_1.current == [] + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_1 is changed + - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_1 is changed + - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_1.current == [] + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_1 is changed + - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_1 is changed + - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_2 is changed + - task_cert_delete_tenant_single_active_host_2.current == [] + - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2 is changed + - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_2 is changed + - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_2 is changed + - task_cert_delete_tenant_first_active_host_2.current == [] + - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2 is changed + - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_2 is changed + - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_2 is changed + - task_cert_delete_tenant_second_active_host_2.current == [] + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_2 is changed + - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_2 is changed + - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_2 is changed + - task_cert_delete_tenant_last_active_host_2.current == [] + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_2 is changed + - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_2 is changed + - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_4_random1 is changed + - inventory_pwd_delete_tenant_single_active_host_4_random1.current == [] + - inventory_pwd_delete_tenant_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4_random1 is changed + - inventory_pwd_add_tenant_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4_random1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_4_random1 is changed + - inventory_pwd_add_ap_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_single_active_host_4_random1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_4_random2 is not changed + - inventory_pwd_delete_tenant_first_active_host_4_random2.current == [] + - inventory_pwd_delete_tenant_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4_random2 is changed + - inventory_pwd_add_tenant_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4_random2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_4_random2 is changed + - inventory_pwd_add_ap_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_first_active_host_4_random2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_4_random3 is changed + - inventory_pwd_delete_tenant_second_active_host_4_random3.current == [] + - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_second_active_host_4_random3 is changed + - inventory_pwd_add_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_4_random3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_4_random3 is changed + - inventory_pwd_add_ap_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_second_active_host_4_random3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_4_random4 is changed + - inventory_pwd_delete_tenant_last_active_host_4_random4.current == [] + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_last_active_host_4_random4 is changed + - inventory_pwd_add_tenant_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_4_random4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_4_random4 is changed + - inventory_pwd_add_ap_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" + - inventory_pwd_add_ap_last_active_host_4_random4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + + - name: Set host 7 + ansible.builtin.add_host: + name: inventory_hosts_random7 + ansible_host: "{{ ansible_host }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + delegate_to: inventory_hosts_random7 + + - name: Set host 8 + ansible.builtin.add_host: + name: inventory_hosts_random7 + ansible_host: "{{ ansible_host }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + delegate_to: inventory_hosts_random7 + + ## Plugin Tests using session_key from inventory + - name: Set Single active host in inventory + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_single_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + output_level: debug + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_1 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_2 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_2 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_2 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_3 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_3 + + - name: Add a new AP on single active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_3 + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_4 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_4 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_4 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_5 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_5 + + - name: Add a new AP on single active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_5 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_6 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_6 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_6 + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_7 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_7 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_7 + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_8 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_8 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_8 + + - name: Verify operations on single active host + assert: + that: + - task_pwd_delete_tenant_single_active_host_1.current == [] + - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1 is changed + - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_1 is changed + - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_2 is changed + - task_cert_delete_tenant_single_active_host_2.current == [] + - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2 is changed + - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_2 is changed + - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_single_active_host_3 is changed + - task_pwd_delete_tenant_single_active_host_3.current == [] + - task_pwd_delete_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_3 is changed + - task_pwd_add_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_3 is changed + - task_pwd_add_ap_single_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_4 is changed + - inventory_pwd_delete_tenant_single_active_host_4.current == [] + - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4 is changed + - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_4 is changed + - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_single_active_host_5 is changed + - task_pwd_delete_tenant_single_active_host_5.current == [] + - task_pwd_delete_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_5 is changed + - task_pwd_add_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_5 is changed + - task_pwd_add_ap_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_6 is changed + - task_cert_delete_tenant_single_active_host_6.current == [] + - task_cert_delete_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_6 is changed + - task_cert_add_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_6 is changed + - task_cert_add_ap_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_7 is changed + - inventory_pwd_delete_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_single_active_host_7.current == [] + - inventory_pwd_add_tenant_single_active_host_7 is changed + - inventory_pwd_add_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_7 is changed + - inventory_pwd_add_ap_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_single_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_8 is changed + - task_cert_delete_tenant_single_active_host_8.current == [] + - task_cert_delete_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_8 is changed + - task_cert_add_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_8 is changed + - task_cert_add_ap_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + + +###################################################################### + + - name: Set Single inactive host in inventory + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_single_host_inactive }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + +############################################################################### + + - name: Set the inventory with the first host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_first_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_1 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_1 + + - name: Add a new AP on the first active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_1 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_2 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_2 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_2 + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_3 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_3 + + - name: Add a new AP on the first active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_3 + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_4 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_4 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_4 + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_5 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_5 + + - name: Add a new AP on the first active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_5 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_6 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_6 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_6 + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_7 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_7 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_7 + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_8 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_8 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_8 + + - name: Verify operations on first active host + assert: + that: + - task_pwd_delete_tenant_first_active_host_1.current == [] + - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1 is changed + - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_1 is changed + - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_2 is changed + - task_cert_delete_tenant_first_active_host_2.current == [] + - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2 is changed + - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_2 is changed + - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_3 is changed + - task_pwd_delete_tenant_first_active_host_3.current == [] + - task_pwd_delete_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_3 is changed + - task_pwd_add_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_3 is changed + - task_pwd_add_ap_first_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_4 is changed + - inventory_pwd_delete_tenant_first_active_host_4.current == [] + - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4 is changed + - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_4 is changed + - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_5 is changed + - task_pwd_delete_tenant_first_active_host_5.current == [] + - task_pwd_delete_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_5 is changed + - task_pwd_add_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_5 is changed + - task_pwd_add_ap_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_6 is changed + - task_cert_delete_tenant_first_active_host_6.current == [] + - task_cert_delete_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_6 is changed + - task_cert_add_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_6 is changed + - task_cert_add_ap_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_7 is changed + - inventory_pwd_delete_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_first_active_host_7.current == [] + - inventory_pwd_add_tenant_first_active_host_7 is changed + - inventory_pwd_add_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_7 is changed + - inventory_pwd_add_ap_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_first_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_8 is changed + - task_cert_delete_tenant_first_active_host_8.current == [] + - task_cert_delete_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_8 is changed + - task_cert_add_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_8 is changed + - task_cert_add_ap_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################## + + - name: Set the inventory with the second host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_second_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_1 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_1 + + - name: Add a new AP on the second active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_1 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_2 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_2 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_2 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_3 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_3 + + - name: Add a new AP on the second active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_3 + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_4 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_4 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_4 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_5 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_5 + + - name: Add a new AP on the second active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_5 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_6 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_6 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_6 + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_7 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_7 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_7 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_8 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_8 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_8 + + - name: Verify operations on second active host + assert: + that: + - task_pwd_delete_tenant_second_active_host_1.current == [] + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_1 is changed + - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_1 is changed + - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_2 is changed + - task_cert_delete_tenant_second_active_host_2.current == [] + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_2 is changed + - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_2 is changed + - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_3 is changed + - task_pwd_delete_tenant_second_active_host_3.current == [] + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_3 is changed + - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_3 is changed + - task_pwd_add_ap_second_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_4 is changed + - inventory_pwd_delete_tenant_second_active_host_4.current == [] + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_second_active_host_4 is changed + - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_4 is changed + - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_5 is changed + - task_pwd_delete_tenant_second_active_host_5.current == [] + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_5 is changed + - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_5 is changed + - task_pwd_add_ap_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_6 is changed + - task_cert_delete_tenant_second_active_host_6.current == [] + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_6 is changed + - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_6 is changed + - task_cert_add_ap_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_7 is changed + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_7.current == [] + - inventory_pwd_add_tenant_second_active_host_7 is changed + - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_7 is changed + - inventory_pwd_add_ap_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_second_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_8 is changed + - task_cert_delete_tenant_second_active_host_8.current == [] + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_8 is changed + - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_8 is changed + - task_cert_add_ap_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################### + + - name: Set the inventory with the last host in the list as active + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_last_host_active }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_1 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_1 + + - name: Add a new AP on the last active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_1 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_2 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_2 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_2 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_3 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_3 + + - name: Add a new AP on the last active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_3 + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_4 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_4 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_4 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_5 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_5 + + - name: Add a new AP on the last active host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_5 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_6 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_6 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_6 + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_7 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_7 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_7 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_8 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_8 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_8 + + - name: Verify operations on last active host + assert: + that: + - task_pwd_delete_tenant_last_active_host_1.current == [] + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_1 is changed + - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_1 is changed + - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_2 is changed + - task_cert_delete_tenant_last_active_host_2.current == [] + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_2 is changed + - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_2 is changed + - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_3 is changed + - task_pwd_delete_tenant_last_active_host_3.current == [] + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_3 is changed + - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_3 is changed + - task_pwd_add_ap_last_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_4 is changed + - inventory_pwd_delete_tenant_last_active_host_4.current == [] + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_last_active_host_4 is changed + - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_4 is changed + - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_5 is changed + - task_pwd_delete_tenant_last_active_host_5.current == [] + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_5 is changed + - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_5 is changed + - task_pwd_add_ap_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_6 is changed + - task_cert_delete_tenant_last_active_host_6.current == [] + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_6 is changed + - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_6 is changed + - task_cert_add_ap_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_7 is changed + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_7.current == [] + - inventory_pwd_add_tenant_last_active_host_7 is changed + - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_7 is changed + - inventory_pwd_add_ap_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_last_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_8 is changed + - task_cert_delete_tenant_last_active_host_8.current == [] + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_8 is changed + - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_8 is changed + - task_cert_add_ap_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + +# ############################################################################### + + - name: Set the inventory with all the hosts in the inventory as inactive + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_all_hosts_inactive }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + +############################################################################################# +#### Random Tets + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_single_active_host_1 + + - name: Delete Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_single_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_single_active_host_1 + + - name: Add Tenant on single active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_single_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_single_active_host_1 + + - name: Add a new AP on single active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_single_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_single_active_host_1 + + - name: Delete Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with password + cisco.aci.aci_ap: + <<: *aci_info_single_host_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_first_active_host_1 + + - name: Add Tenant on the first active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active + tenant: ansible_test + state: present + register: task_pwd_add_tenant_first_active_host_1 + + - name: Add a new AP on the first active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_first_host_active + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_first_active_host_1 + + - name: Delete Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_second_active_host_1 + + - name: Add Tenant on the second active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_second_active_host_1 + + - name: Add a new AP on the second active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_second_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_second_active_host_1 + + - name: Delete Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: absent + register: task_pwd_delete_tenant_last_active_host_1 + + - name: Add Tenant on the last active host present in the task with password + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host + tenant: ansible_test + state: present + register: task_pwd_add_tenant_last_active_host_1 + + - name: Add a new AP on the last active host present in the task password + cisco.aci.aci_ap: + <<: *aci_info_last_active_host + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_pwd_add_ap_last_active_host_1 + + - name: Delete Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts credential + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts credential + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_single_active_host_2 + + - name: Add Tenant on single active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_single_active_host_2 + + - name: Add a new AP on single active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_single_active_host_2 + + - name: Delete Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_single_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Delete Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_first_active_host_2 + + - name: Add Tenant on the first active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_first_active_host_2 + + - name: Add a new AP on the first active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_first_host_active_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_first_active_host_2 + + - name: Delete Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_second_active_host_2 + + - name: Add Tenant on the second active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_second_active_host_2 + + - name: Add a new AP on the second active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_second_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_second_active_host_2 + + - name: Delete Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: task_cert_delete_tenant_last_active_host_2 + + - name: Add Tenant on the last active host present in the task with certificate + cisco.aci.aci_tenant: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: task_cert_add_tenant_last_active_host_2 + + - name: Add a new AP on the last active host present in the task with certificate + cisco.aci.aci_ap: + <<: *aci_info_last_active_host_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: task_cert_add_ap_last_active_host_2 + + - name: Delete Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via task hosts certificate + cisco.aci.aci_tenant: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via task hosts certificate + cisco.aci.aci_ap: + <<: *aci_info_all_hosts_inactive_certificate + certificate_name: admin + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Set hosts 1 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host1 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_single_active_host_4_random1 + + - name: Add Tenant on single active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_single_active_host_4_random1 + + - name: Add a new AP on single active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_single_active_host_4_random1 + + - name: Set hosts 2 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host2 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant on single inactive host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP on single inactive host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Set hosts 3 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host3 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_first_active_host_4_random3 + + - name: Add Tenant on the first active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_first_active_host_4_random3 + + - name: Add a new AP on the first active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_first_active_host_4_random3 + + - name: Set hosts 4 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host4 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_second_active_host_4_random4 + + - name: Add Tenant on the second active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_second_active_host_4_random4 + + - name: Add a new AP on the second active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_second_active_host_4_random4 + + - name: Set hosts 5 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host5 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: inventory_pwd_delete_tenant_last_active_host_4_random5 + + - name: Add Tenant on the last active host present in the inventory with password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: inventory_pwd_add_tenant_last_active_host_4_random5 + + - name: Add a new AP on the last active host present in the inventory with password + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + register: inventory_pwd_add_ap_last_active_host_4_random5 + + - name: Set hosts 6 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: "{{ ansible_host6 }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + ## HTTP Error + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Add a new AP via inventory host + cisco.aci.aci_ap: + output_level: debug + tenant: ansible_test + ap: ap + description: default ap + state: present + ignore_errors: yes + + - name: Verify random operations + assert: + that: + - task_pwd_delete_tenant_single_active_host_1.current == [] + - task_pwd_delete_tenant_second_single_active_host_1.current == [] + - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1 is changed + - task_pwd_add_tenant_second_single_active_host_1 is changed + - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_tenant_second_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_single_active_host_1 is changed + - task_pwd_add_ap_second_single_active_host_1 is changed + - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_add_ap_second_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_first_active_host_1.current == [] + - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1 is changed + - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_first_active_host_1 is changed + - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_second_active_host_1.current == [] + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_second_active_host_1 is changed + - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_second_active_host_1 is changed + - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_pwd_delete_tenant_last_active_host_1.current == [] + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_add_tenant_last_active_host_1 is changed + - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_pwd_add_ap_last_active_host_1 is changed + - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" + - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_single_active_host_2 is changed + - task_cert_delete_tenant_single_active_host_2.current == [] + - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2 is changed + - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_single_active_host_2 is changed + - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_first_active_host_2 is changed + - task_cert_delete_tenant_first_active_host_2.current == [] + - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2 is changed + - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_first_active_host_2 is changed + - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_second_active_host_2 is changed + - task_cert_delete_tenant_second_active_host_2.current == [] + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_second_active_host_2 is changed + - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_second_active_host_2 is changed + - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - task_cert_delete_tenant_last_active_host_2 is changed + - task_cert_delete_tenant_last_active_host_2.current == [] + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_add_tenant_last_active_host_2 is changed + - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - task_cert_add_ap_last_active_host_2 is changed + - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" + - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_single_active_host_4 is changed + - inventory_pwd_delete_tenant_single_active_host_4.current == [] + - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4 is changed + - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_single_active_host_4 is changed + - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_first_active_host_4 is changed + - inventory_pwd_delete_tenant_first_active_host_4.current == [] + - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4 is changed + - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_first_active_host_4 is changed + - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_second_active_host_4 is changed + - inventory_pwd_delete_tenant_second_active_host_4.current == [] + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_second_active_host_4 is changed + - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_second_active_host_4 is changed + - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + - inventory_pwd_delete_tenant_last_active_host_4 is changed + - inventory_pwd_delete_tenant_last_active_host_4.current == [] + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_add_tenant_last_active_host_4 is changed + - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" + - inventory_pwd_add_ap_last_active_host_4 is changed + - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" + - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" + + - name: Set host 7 + ansible.builtin.add_host: + name: inventory_hosts_random7 + ansible_host: "{{ ansible_host }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + delegate_to: inventory_hosts_random7 + + - name: Set host 8 + ansible.builtin.add_host: + name: inventory_hosts_random7 + ansible_host: "{{ ansible_host }}" + ansible_user: "{{ ansible_user }}" + ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant via inventory host + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + delegate_to: inventory_hosts_random7 + \ No newline at end of file diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index f3eba556e..76c43944f 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -4,10 +4,14 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -- name: Test that we have an ACI APIC host, ACI username and ACI password - fail: - msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' - when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined +# - name: Test that we have an ACI APIC host, ACI username and ACI password +# fail: +# msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' +# when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: httpapi_inventory_password.yml + tags: httpapi_inventory_password + when: inventory_hostname == play_hosts[5] - name: Delete old log files to clean test directory file: From 471e6100ceeda40a378be96c0699f145586b9412 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Fri, 13 Jan 2023 12:21:44 -0500 Subject: [PATCH 27/49] [minor_change] Fix comments and formatting --- plugins/httpapi/aci.py | 10 +- plugins/module_utils/aci.py | 183 ++++++++---------------------------- 2 files changed, 44 insertions(+), 149 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 14c10ef0f..7f24e6fc5 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -81,7 +81,7 @@ def get_backup_hosts_from_inventory(self): # Case: Host is provided in the inventory self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) except Exception: - # Case: Host is provided in the memory inventory + # Case: Host is provided in a different format self.inventory_hosts.append(self.connection.get_option("host")) # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted @@ -205,14 +205,11 @@ def set_parameters(self, method, path, data): # One API call is made via each call to send_request from aci.py in module_utils # As long as a host is active in the list we make sure that the API call goes through - # A switch like mechanism is heavily utilized to support the same playbook consisting of tasks running on different hosts def send_request(self, method, path, data): ''' This method handles all APIC REST API requests other than login ''' # Note: Preference is given to Hosts mentioned at task level in the playbook if self.params.get('host') is not None: - self.entered_task = True - # Case: Host is provided in the task of a playbook task_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") @@ -221,7 +218,7 @@ def send_request(self, method, path, data): # 2.connection_error_check was set in the previous task # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.backup_hosts != task_hosts or self.connection_error_check: + if (self.backup_hosts is not None and self.backup_hosts != task_hosts) or self.connection_error_check: self.host_counter = 0 self.connection_error_check = False self.connection._connected = False @@ -236,7 +233,7 @@ def send_request(self, method, path, data): # 2.connection_error_check was set in the previous task # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if self.backup_hosts != self.inventory_hosts[0] or self.connection_error_check: + if (self.backup_hosts is not None and self.backup_hosts != self.inventory_hosts[0]) or self.connection_error_check: self.host_counter = 0 self.connection_error_check = False self.connection._connected = False @@ -369,4 +366,3 @@ def cert_auth(self, path, method, payload=''): 'APIC-Certificate-Fingerprint=fingerprint; ' +\ 'APIC-Request-Signature=%s' % to_native(base64.b64encode(sig_signature)) return headers - diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 1d889f65a..d1eec17c2 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1,3 +1,4 @@ + # -*- coding: utf-8 -*- # This code is part of Ansible, but is an independent component @@ -90,24 +91,13 @@ def aci_argument_spec(): return dict( -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD host=dict( type="str", - required=True, + required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"]), ), -======= -======= ->>>>>>> 5ca45db ([ignore] Change location of persistent storage file) -======= ->>>>>>> d11ea33 ([ignore] Ability to switch from certificate based authentication to credential) - host=dict(type="str", required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"])), ->>>>>>> 6341500 (retry w/o conflict) port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])), -<<<<<<< HEAD username=dict( type="str", default="admin", @@ -119,19 +109,6 @@ def aci_argument_spec(): no_log=True, fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"]), ), -======= - username=dict(type="str", default="admin", aliases=["user"], fallback=(env_fallback, ["ACI_USERNAME", "ANSIBLE_NET_USERNAME"])), - password=dict(type="str", no_log=True, fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"])), -<<<<<<< HEAD -======= - host=dict(type='str', required=True, aliases=['hostname'], fallback=(env_fallback, ['ACI_HOST'])), - port=dict(type='int', required=False, fallback=(env_fallback, ['ACI_PORT'])), - username=dict(type='str', required=False, aliases=['user'], fallback=(env_fallback, ['ACI_USERNAME', 'ANSIBLE_NET_USERNAME'])), - password=dict(type='str', no_log=True, fallback=(env_fallback, ['ACI_PASSWORD', 'ANSIBLE_NET_PASSWORD'])), ->>>>>>> eeef0dc ([ignore] Change location of persistent storage file) ->>>>>>> 5ca45db ([ignore] Change location of persistent storage file) -======= ->>>>>>> d11ea33 ([ignore] Ability to switch from certificate based authentication to credential) # Beware, this is not the same as client_key ! private_key=dict( type="str", @@ -327,7 +304,6 @@ def destination_epg_spec(): class ACIModule(object): def __init__(self, module): - self.default_username = 'admin' self.module = module self.params = module.params self.result = dict(changed=False) @@ -429,7 +405,6 @@ def login(self): if self.params.get("port") is not None: url = "%(protocol)s://%(host)s:%(port)s/api/aaaLogin.json" % self.params else: -<<<<<<< HEAD url = "%(protocol)s://%(host)s/api/aaaLogin.json" % self.params payload = { "aaaUser": { @@ -447,15 +422,6 @@ def login(self): timeout=self.params.get("timeout"), use_proxy=self.params.get("use_proxy"), ) -======= - url = '%(protocol)s://%(host)s/api/aaaLogin.json' % self.params - payload = {'aaaUser': {'attributes': {'name': self.params.get('username', self.default_username), 'pwd': self.params.get('password')}}} - resp, auth = fetch_url(self.module, url, - data=json.dumps(payload), - method='POST', - timeout=self.params.get('timeout'), - use_proxy=self.params.get('use_proxy')) ->>>>>>> cde59eb (add test file to check connection) # Handle APIC response if auth.get("status") != 200: @@ -524,7 +490,7 @@ def cert_auth(self, path=None, payload="", method=None): self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params) if self.params.get('certificate_name') is None: - self.params['certificate_name'] = self.params.get('username', self.default_username) + self.params['certificate_name'] = self.params.get('username', 'admin') # NOTE: ACI documentation incorrectly adds a space between method and path sig_request = method + path + payload if HAS_CRYPTOGRAPHY: @@ -1278,39 +1244,9 @@ def delete_config(self): if not self.existing: return - elif not self.module.check_mode: # Sign and encode request as to APIC's wishes self.api_call("DELETE") - -<<<<<<< HEAD - resp, info = fetch_url( - self.module, - self.url, - headers=self.headers, - method="DELETE", - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) - - self.response = info.get("msg") - self.status = info.get("status") - self.method = "DELETE" - - # Handle APIC response - if info.get("status") == 200: - self.result["changed"] = True - self.response_json(resp.read()) - else: - try: - # APIC error - self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) -======= ->>>>>>> 6341500 (retry w/o conflict) else: self.result["changed"] = True self.method = "DELETE" @@ -1320,7 +1256,6 @@ def get_diff(self, aci_class): This method is used to get the difference between the proposed and existing configurations. Each module should call the get_existing method before this method, and add the proposed config to the module results using the module's config parameters. The new config will added to the self.result dictionary. - :param aci_class: Type str. This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. """ @@ -1357,7 +1292,6 @@ def get_diff_child(child_class, proposed_child, existing_child): """ This method is used to get the difference between a proposed and existing child configs. The get_nested_config() method should be used to return the proposed and existing config portions of child. - :param child_class: Type str. The root class (dict key) for the child dictionary. :param proposed_child: Type dict. @@ -1434,57 +1368,14 @@ def get_existing(self): that this method can be used to supply the existing configuration when using the get_diff method. The response, status, and existing configuration will be added to the self.result dictionary. """ -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - uri = self.url + self.filter_string - - # Sign and encode request as to APIC's wishes - if self.params.get("private_key"): - self.cert_auth(path=self.path + self.filter_string, method="GET") - resp, info = fetch_url( - self.module, - uri, - headers=self.headers, - method="GET", - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) - self.response = info.get("msg") - self.status = info.get("status") - self.method = "GET" - - # Handle APIC response - if info.get("status") == 200: - self.existing = json.loads(resp.read())["imdata"] - else: - try: - # APIC error - self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) -======= - self.call("GET") ->>>>>>> 6341500 (retry w/o conflict) -======= - self.call("GET") -======= - self.api_call('GET') ->>>>>>> eeef0dc ([ignore] Change location of persistent storage file) ->>>>>>> 5ca45db ([ignore] Change location of persistent storage file) -======= self.api_call("GET") ->>>>>>> 53e78e0 ([ignore] Addition of queue messages) @staticmethod def get_nested_config(proposed_child, existing_children): """ This method is used for stiping off the outer layers of the child dictionaries so only the configuration key, value pairs are returned. - :param proposed_child: Type dict. The dictionary that represents the child config. :param existing_children: Type list. @@ -1549,7 +1440,6 @@ def payload(self, aci_class, class_config, child_configs=None): This method is used to dynamically build the proposed configuration dictionary from the config related parameters passed into the module. All values that were not passed values from the playbook task will be removed so as to not inadvertently change configurations. - :param aci_class: Type str This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. :param class_config: Type dict @@ -1722,13 +1612,10 @@ def fail_json(self, msg, **kwargs): self.result["filter_string"] = self.filter_string self.result["method"] = self.method # self.result['path'] = self.path # Adding 'path' in result causes state: absent in output - self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url - self.result['httpapi_logs'] = self.httpapi_logs - if self.stdout: - self.result['stdout'] = self.stdout + self.result["httpapi_logs"] = self.httpapi_logs if "state" in self.params: if self.params.get("output_level") in ("debug", "info"): @@ -1796,65 +1683,77 @@ def ospf_spec(): def call(self, method): ======= def api_call(self, method): +<<<<<<< HEAD >>>>>>> d17daa6 ([ignore] Change location of persistent storage file) if method == 'GET': +======= + if method == "GET": +>>>>>>> 5d0ead5 ([minor_change] Fix comments and formatting) call_path = self.path + self.filter_string call_url = self.url + self.filter_string data = None - elif method == 'POST': + elif method == "POST": call_path = self.path call_url = self.url data = json.dumps(self.config) - elif method == 'DELETE': + elif method == "DELETE": call_path = self.path call_url = self.url data = None resp = None - if self.params.get('private_key'): + if self.params.get("private_key"): self.cert_auth(path=call_path, payload=data, method=method) if self.module._socket_path: - self.params['validate_certs'] = False + self.params["validate_certs"] = False connect = Connection(self.module._socket_path) - connect.get_params(self.headers.get('Cookie'), self.params) - info = connect.send_request(method, '/{0}'.format(call_path), data) - self.url = info.get('url') + connect.get_params(self.headers.get("Cookie"), self.params) + info = connect.send_request(method, "/{0}".format(call_path), data) + self.url = info.get("url") self.httpapi_logs.extend(connect.pop_messages()) + self.stdout = str("Through plugin") else: - resp, info = fetch_url(self.module, call_url, - data=data, - headers=self.headers, - method=method, - timeout=self.params.get('timeout'), - use_proxy=self.params.get('use_proxy')) - self.response = info.get('msg') - self.status = info.get('status') + resp, info = fetch_url( + self.module, + call_url, + data=data, + headers=self.headers, + method=method, + timeout=self.params.get("timeout"), + use_proxy=self.params.get("use_proxy"), + ) + self.response = info.get("msg") + self.status = info.get("status") self.method = method # Handle APIC response - if info.get('status') == 200: - if method == 'POST' or method == 'DELETE': - self.result['changed'] = True + if info.get("status") == 200: + if method == "POST" or method == "DELETE": + self.result["changed"] = True try: - if method == 'GET': - self.existing = json.loads(resp.read())['imdata'] + if method == "GET": + self.existing = json.loads(resp.read())["imdata"] else: self.response_json(resp.read()) except AttributeError: - if method == 'GET': - self.existing = info['body']['imdata'] + if method == "GET": + self.existing = info["body"]["imdata"] else: - self.response_json(info.get('body')) + self.response_json(info.get("body")) else: try: # APIC error - self.response_json(info['body']) - self.fail_json(msg='APIC Error %(code)s: %(text)s' % self.error) + self.response_json(info["body"]) + self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) except KeyError: # Connection error +<<<<<<< HEAD self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) <<<<<<< HEAD >>>>>>> da2af7d (retry w/o conflict) ======= >>>>>>> 02c53f6 (Check Sanity) +======= + self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) +>>>>>>> 5d0ead5 ([minor_change] Fix comments and formatting) From 7207980efdad4146aeda6cd154bf1efa6a4b6f74 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sat, 14 Jan 2023 06:55:51 -0500 Subject: [PATCH 28/49] [minor_change] Fix rebase comments --- plugins/module_utils/aci.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index d1eec17c2..006e14a4f 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -889,15 +889,7 @@ def construct_url( self.child_classes = set(child_classes) if subclass_5 is not None: - self._construct_url_6( - root_class, - subclass_1, - subclass_2, - subclass_3, - subclass_4, - subclass_5, - config_only, - ) + self._construct_url_6(root_class, subclass_1, subclass_2, subclass_3, subclass_4, subclass_5, config_only) elif subclass_4 is not None: self._construct_url_5(root_class, subclass_1, subclass_2, subclass_3, subclass_4, config_only) elif subclass_3 is not None: @@ -1316,7 +1308,6 @@ def get_diff_children(self, aci_class, proposed_obj=None, existing_obj=None): """ This method is used to retrieve the updated child configs by comparing the proposed children configs against the objects existing children configs. - :param aci_class: Type str. This is the root dictionary key for the MO's configuration body, or the ACI class of the MO. :return: The list of updated child config dictionaries. None is returned if there are no changes to the child @@ -1405,7 +1396,6 @@ def get_nested_config(proposed_child, existing_children): def get_nested_children(proposed_child, existing_children): """ This method is used for stiping off the outer layers of the child dictionaries so only the children are returned. - :param proposed_child: Type dict. The dictionary that represents the child config. :param existing_children: Type list. From 6a140b140a8d364d1dabb5a27ffc11973acdf767 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 16 Jan 2023 08:08:09 -0500 Subject: [PATCH 29/49] [minor_change] Fixed default username --- plugins/httpapi/aci.py | 17 +-- .../tasks/httpapi_inventory_password.yml | 132 ++++++------------ 2 files changed, 48 insertions(+), 101 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 7f24e6fc5..131b836d2 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -77,12 +77,8 @@ def get_backup_hosts_from_inventory(self): # append is used here to store the first list of hosts available in self.connection.get_option("host") in the 0th position of the list inventory_host. # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. # We always want access to the original list of hosts from the inventory when the tasks are running on them. - try: - # Case: Host is provided in the inventory - self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) - except Exception: - # Case: Host is provided in a different format - self.inventory_hosts.append(self.connection.get_option("host")) + # Case: Host is provided in the inventory + self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted def login(self, username, password): @@ -131,17 +127,12 @@ def set_parameters(self, method, path, data): if self.params.get('validate_certs') is not None: self.connection.set_option("validate_certs", self.params.get('validate_certs')) - + # The command timeout which is the response timeout from APIC - if self.params.get('timeout') is not None: - self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) - # If the persistent_connect_timeout is less than the response timeout from APIC the persistent socket connection will fail if self.params.get('timeout') is not None: + self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) self.connection.set_option('persistent_connect_timeout', self.params.get('timeout') + 30) - else: - # For inventory - self.connection.set_option('persistent_connect_timeout', self.connection.get_option('persistent_command_timeout') + 30) # Start with certificate authentication if self.auth is not None: diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml index 2630239f4..033ec2108 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml @@ -1,7 +1,6 @@ ##TEST CONNECTION ## If credentials in the inventory are used. Remove session_key from inventory ## -## For testing on the CI we hardcode the session_key in the playbook ## ## Active Host (PASS) # Run on single active Host at task level using credentials @@ -53,7 +52,7 @@ # Run on multiple Hosts at task level using credentials # Run on multiple Hosts in inventory using credentials -#None of the Hosts are Active (FAIL use ignore errors) +## None of the Hosts are Active (FAIL use ignore errors) # Run on multiple Hosts at task level using credentials # Run on multiple Hosts at task level using certificate # Run on multiple Hosts at task level using credentials @@ -63,12 +62,13 @@ # Run on multiple Hosts at task level using credentials # Run on multiple Hosts in inventory using credentials + ## Setup Environment ## - name: Set vars set_fact: aci_info_single_host_active: &aci_info_single_host_active host: "{{ ansible_single_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -89,7 +89,7 @@ aci_info_second_single_host_active: &aci_info_second_single_host_active host: "{{ ansible_second_single_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' use_proxy: '{{ use_proxy | default(true) }}' @@ -102,7 +102,7 @@ aci_info_third_single_host_active: &aci_info_third_single_host_active host: "{{ ansible_third_single_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -139,13 +139,15 @@ name: admin certificate: "{{ lookup('file', 'certs/admin.crt') }}" state: present + + ## Plugin Tests using password from inventory (Part1) ## - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts ansible_host: "{{ ansible_single_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -439,7 +441,7 @@ aci_info_single_host_inactive: &aci_info_single_host_inactive host: "{{ ansible_single_host_inactive }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -460,7 +462,7 @@ name: inventory_hosts ansible_host: "{{ ansible_single_host_inactive }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -676,7 +678,7 @@ aci_info_first_host_active: &aci_info_first_host_active host: "{{ ansible_first_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -697,7 +699,7 @@ name: inventory_hosts ansible_host: "{{ ansible_first_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -989,7 +991,7 @@ aci_info_second_active_host: &aci_info_second_active_host host: "{{ ansible_second_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1010,7 +1012,7 @@ name: inventory_hosts ansible_host: "{{ ansible_second_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -1310,7 +1312,7 @@ aci_info_last_active_host: &aci_info_last_active_host host: "{{ ansible_last_host_active }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1331,7 +1333,7 @@ name: inventory_hosts ansible_host: "{{ ansible_last_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -1663,7 +1665,7 @@ aci_info_all_hosts_inactive: &aci_info_all_hosts_inactive host: "{{ ansible_all_hosts_inactive }}" username: "{{ ansible_user }}" - password: "{{ ansible_ssh_pass }}" + password: "{{ ansible_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1684,7 +1686,7 @@ name: inventory_hosts ansible_host: "{{ ansible_all_hosts_inactive }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2237,7 +2239,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host1 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2290,7 +2292,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host2 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2326,7 +2328,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host3 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2362,7 +2364,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host4 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2398,7 +2400,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host5 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2434,7 +2436,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host6 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -2599,57 +2601,13 @@ - inventory_pwd_add_ap_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - inventory_pwd_add_ap_last_active_host_4_random4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - name: Set host 7 - ansible.builtin.add_host: - name: inventory_hosts_random7 - ansible_host: "{{ ansible_host }}" - ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" - ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - delegate_to: inventory_hosts_random7 - - - name: Set host 8 - ansible.builtin.add_host: - name: inventory_hosts_random7 - ansible_host: "{{ ansible_host }}" - ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" - ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - delegate_to: inventory_hosts_random7 - - ## Plugin Tests using session_key from inventory + ## Plugin Tests using session_key from inventory (Part2) ## - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts ansible_host: "{{ ansible_single_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -2944,7 +2902,7 @@ name: inventory_hosts ansible_host: "{{ ansible_single_host_inactive }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -3161,7 +3119,7 @@ name: inventory_hosts ansible_host: "{{ ansible_first_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -3454,7 +3412,7 @@ name: inventory_hosts ansible_host: "{{ ansible_second_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -3755,7 +3713,7 @@ name: inventory_hosts ansible_host: "{{ ansible_last_host_active }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4088,7 +4046,7 @@ name: inventory_hosts ansible_host: "{{ ansible_all_hosts_inactive }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4642,7 +4600,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host1 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4679,7 +4637,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host2 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" @@ -4715,7 +4673,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host3 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4752,7 +4710,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host4 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4789,7 +4747,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host5 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -4826,7 +4784,7 @@ name: inventory_hosts ansible_host: "{{ ansible_host6 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -5011,10 +4969,10 @@ - name: Set host 7 ansible.builtin.add_host: - name: inventory_hosts_random7 - ansible_host: "{{ ansible_host }}" + name: inventory_hosts + ansible_host: "{{ ansible_host7 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -5029,14 +4987,13 @@ tenant: ansible_test state: absent ignore_errors: yes - delegate_to: inventory_hosts_random7 - name: Set host 8 ansible.builtin.add_host: - name: inventory_hosts_random7 - ansible_host: "{{ ansible_host }}" + name: inventory_hosts + ansible_host: "{{ ansible_host7 }}" ansible_user: "{{ ansible_user }}" - ansible_ssh_pass: "{{ ansible_ssh_pass }}" + ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} ansible_command_timeout: "{{ ansible_command_timeout }}" ansible_connection: "{{ ansible_connection }}" @@ -5045,11 +5002,10 @@ ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - name: Delete Tenant via inventory host + - name: Delete Tenant via inventory host (Final Task in the Test) cisco.aci.aci_tenant: output_level: debug tenant: ansible_test state: absent ignore_errors: yes - delegate_to: inventory_hosts_random7 \ No newline at end of file From bb3d58b47e4342ad1f5f2d62ca0edb62e81b8341 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 30 Jan 2023 06:48:43 -0500 Subject: [PATCH 30/49] [ignore_changes] Add inventory_hosts=6 to the test file --- tests/integration/inventory.networking | 5 +++++ tests/integration/targets/aci_tenant/tasks/main.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 16e3d1ee1..5d3726af5 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -4,7 +4,12 @@ cn-dmz-apic-m1-03-v52 ansible_host=173.36.219.69 aci_hostname=173.36.219.69 cn-dmz-apic-m1-04-v60 ansible_host=173.36.219.70 aci_hostname=173.36.219.70 cn-dmz-apic-m1-07-v32 ansible_host=173.36.219.73 aci_hostname=173.36.219.73 aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a +<<<<<<< HEAD azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +======= +azure_cloud ansible_host=104.42.26.226 aci_hostname=104.42.26.226 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +inventory_hosts aci_hostname=173.36.219.68 ansible_connection=ansible.netcommon.httpapi +>>>>>>> 1293725 ([ignore_changes] Add inventory_hosts=6 to the test file) [aci:vars] aci_username=ansible_github_ci diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index 76c43944f..bb55fb555 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -11,7 +11,7 @@ - include_tasks: httpapi_inventory_password.yml tags: httpapi_inventory_password - when: inventory_hostname == play_hosts[5] + when: inventory_hostname == play_hosts[6] - name: Delete old log files to clean test directory file: From dfd1085a0a11ba687e9c6672c67d1cb455bbeb25 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 9 Feb 2023 10:40:57 -0500 Subject: [PATCH 31/49] [minor_change] Removed different functions to make requests which can now be done by using just one function --- plugins/httpapi/aci.py | 239 ++++++++++-------- plugins/module_utils/aci.py | 179 ++++--------- plugins/modules/aci_cloud_subnet.py | 1 - plugins/modules/aci_config_rollback.py | 21 +- plugins/modules/aci_config_snapshot.py | 16 +- plugins/modules/aci_rest.py | 26 +- tests/integration/inventory.networking | 4 + .../tasks/httpapi_inventory_password.yml | 214 +++++++++++----- .../targets/aci_tenant/tasks/main.yml | 110 +++++--- 9 files changed, 422 insertions(+), 388 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 131b836d2..811175a41 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -1,4 +1,6 @@ # Copyright (c) 2022 Cisco and/or its affiliates. +# Copyright: (c) 2020, Shreyas Srish (@shrsr) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,16 +15,17 @@ # limitations under the License. -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from importlib.resources import path from operator import methodcaller + __metaclass__ = type DOCUMENTATION = """ --- +name: aci author: -- Shreyas Srish (shrsr) -httpapi: aci +- Shreyas Srish (@shrsr) short_description: Ansible ACI HTTPAPI Plugin. description: - This ACI plugin provides the HTTPAPI transport methods needed to initiate @@ -30,7 +33,11 @@ response from the controller. """ -import ast, base64, json, os, re +import ast +import base64 +import json +import os +import re from ansible.module_utils._text import to_text, to_native from ansible.module_utils.connection import ConnectionError @@ -39,6 +46,7 @@ # Optional, only used for APIC signature-based authentication try: from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign + HAS_OPENSSL = True except ImportError: HAS_OPENSSL = False @@ -48,13 +56,13 @@ from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend + HAS_CRYPTOGRAPHY = True except ImportError: HAS_CRYPTOGRAPHY = False class HttpApi(HttpApiBase): - def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None @@ -78,66 +86,68 @@ def get_backup_hosts_from_inventory(self): # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. # We always want access to the original list of hosts from the inventory when the tasks are running on them. # Case: Host is provided in the inventory - self.inventory_hosts.append(re.sub(r'[[\]]', '', self.connection.get_option("host")).split(",")) + self.inventory_hosts.append(re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",")) - # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted + # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted def login(self, username, password): - ''' Log in to APIC ''' + """Log in to APIC""" # Perform login request - self.connection.queue_message('step:', 'Establishing connection to {0}'.format(self.connection.get_option('host'))) - method = 'POST' - path = '/api/aaaLogin.json' - payload = {'aaaUser': {'attributes': {'name': username, 'pwd': password}}} + self.connection.queue_message("step:", "Establishing connection to {0}".format(self.connection.get_option("host"))) + method = "POST" + path = "/api/aaaLogin.json" + payload = {"aaaUser": {"attributes": {"name": username, "pwd": password}}} data = json.dumps(payload) try: response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) - self.connection._auth = {'Cookie': 'APIC-Cookie={0}' - .format(self._response_to_json(response_value).get('imdata')[0]['aaaLogin']['attributes']['token'])} - self.connection.queue_message('step:', 'Connection to {0} was successful'.format(self.connection.get_option('host'))) - except Exception: - # The exception here is transferred to the response exception - raise + self.connection._auth = { + "Cookie": "APIC-Cookie={0}".format(self._response_to_json(response_value).get("imdata")[0]["aaaLogin"]["attributes"]["token"]) + } + self.connection.queue_message("step:", "Connection to {0} was successful".format(self.connection.get_option("host"))) + except Exception as exc: + # Catch exception from HTTPError() + if "401" in str(exc): + raise def logout(self): - method = 'POST' - path = '/api/aaaLogout.json' - payload = {"aaaUser":{"attributes":{"name":self.connection.get_option("remote_user")}}} + method = "POST" + path = "/api/aaaLogout.json" + payload = {"aaaUser": {"attributes": {"name": self.connection.get_option("remote_user")}}} data = json.dumps(payload) try: response, response_data = self.connection.send(path, data, method=method) except Exception as exc_logout: - msg = 'Error on attempt to logout from APIC. {0}'.format(exc_logout) + msg = "Error on attempt to logout from APIC. {0}".format(exc_logout) raise ConnectionError(self._return_info(None, method, path, msg)) self.connection._auth = None self._verify_response(response, method, path, response_data) def set_parameters(self, method, path, data): - if self.params.get('port') is not None: - self.connection.set_option("port", self.params.get('port')) + if self.params.get("port") is not None: + self.connection.set_option("port", self.params.get("port")) + + if self.params.get("username") is not None: + self.connection.set_option("remote_user", self.params.get("username")) - if self.params.get('username') is not None: - self.connection.set_option("remote_user", self.params.get('username')) - - if self.params.get('use_proxy') is not None: - self.connection.set_option("use_proxy", self.params.get('use_proxy')) + if self.params.get("use_proxy") is not None: + self.connection.set_option("use_proxy", self.params.get("use_proxy")) - if self.params.get('use_ssl') is not None: - self.connection.set_option("use_ssl", self.params.get('use_ssl')) + if self.params.get("use_ssl") is not None: + self.connection.set_option("use_ssl", self.params.get("use_ssl")) + + if self.params.get("validate_certs") is not None: + self.connection.set_option("validate_certs", self.params.get("validate_certs")) - if self.params.get('validate_certs') is not None: - self.connection.set_option("validate_certs", self.params.get('validate_certs')) - # The command timeout which is the response timeout from APIC - # If the persistent_connect_timeout is less than the response timeout from APIC the persistent socket connection will fail - if self.params.get('timeout') is not None: - self.connection.set_option('persistent_command_timeout', self.params.get('timeout')) - self.connection.set_option('persistent_connect_timeout', self.params.get('timeout') + 30) - + # If the persistent_connect_timeout is less than the response timeout from APIC the persistent socket connection will fail + if self.params.get("timeout") is not None: + self.connection.set_option("persistent_command_timeout", self.params.get("timeout")) + self.connection.set_option("persistent_connect_timeout", self.params.get("timeout") + 30) + # Start with certificate authentication if self.auth is not None: - # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate at the task level. + # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None + # to give preference to credentials/certificate at the task level. if self.connection.get_option("session_key") is not None: self.connection.set_option("session_key", None) # Check if the previous task was running with the credentials at the task level/inventory or the certificate in the inventory @@ -147,17 +157,17 @@ def set_parameters(self, method, path, data): self.check_auth_from_credentials_inventory = False self.check_auth_from_private_key_inventory = False self.check_auth_from_private_key_task = True - self.connection._auth = {'Cookie': '{0}'.format(self.auth)} - self.connection.queue_message('step:', 'Setting certificate authentication at task level') + self.connection._auth = {"Cookie": "{0}".format(self.auth)} + self.connection.queue_message("step:", "Setting certificate authentication at task level") # Override parameter in @ensure_connect self.connection._connected = True # Start with credential authentication - elif self.params.get('password') is not None: - # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate at the task level. + elif self.params.get("password") is not None: + # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None + # to give preference to credentials/certificate at the task level. if self.connection.get_option("session_key") is not None: self.connection.set_option("session_key", None) - self.connection.set_option("password", self.params.get('password')) + self.connection.set_option("password", self.params.get("password")) # Check if the previous task was running with the certificate in at the task level/inventory or the credentials in the inventory if self.check_auth_from_private_key_task or self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_inventory: self.host_counter = 0 @@ -167,7 +177,7 @@ def set_parameters(self, method, path, data): self.check_auth_from_credentials_inventory = False self.check_auth_from_private_key_inventory = False self.check_auth_from_credentials_task = True - self.connection.queue_message('step:', 'Setting credential authentication at task level') + self.connection.queue_message("step:", "Setting credential authentication at task level") # Note: session_key takes precedence over password if both of them are present in the inventory elif self.connection.get_option("session_key") is not None: # Check if the previous task was running with the certificate at the task level or the credentials in the inventory/task @@ -177,8 +187,8 @@ def set_parameters(self, method, path, data): self.check_auth_from_private_key_task = False self.check_auth_from_credentials_task = False self.check_auth_from_private_key_inventory = True - self.connection.queue_message('step:', 'Setting certificate authentication from inventory') - self.connection._auth = {'Cookie': '{0}'.format(self.cert_auth(path, method, data).get('Cookie'))} + self.connection.queue_message("step:", "Setting certificate authentication from inventory") + self.connection._auth = {"Cookie": "{0}".format(self.cert_auth(path, method, data).get("Cookie"))} # Override parameter in @ensure_connect self.connection._connected = True # No session_key provided. Use password in the inventory instead @@ -192,21 +202,21 @@ def set_parameters(self, method, path, data): self.check_auth_from_private_key_task = False self.check_auth_from_credentials_task = False self.check_auth_from_credentials_inventory = True - self.connection.queue_message('step:', 'Setting credential authentication from inventory') + self.connection.queue_message("step:", "Setting credential authentication from inventory") # One API call is made via each call to send_request from aci.py in module_utils # As long as a host is active in the list we make sure that the API call goes through def send_request(self, method, path, data): - ''' This method handles all APIC REST API requests other than login ''' + """This method handles all APIC REST API requests other than login""" # Note: Preference is given to Hosts mentioned at task level in the playbook - if self.params.get('host') is not None: + if self.params.get("host") is not None: # Case: Host is provided in the task of a playbook - task_hosts = ast.literal_eval(self.params.get('host')) if '[' in self.params.get('host') else self.params.get('host').split(",") - + task_hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") + # We check whether: # 1.The list of hosts provided in two consecutive tasks are not the same - # 2.connection_error_check was set in the previous task + # 2.connection_error_check was set in the previous task # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). if (self.backup_hosts is not None and self.backup_hosts != task_hosts) or self.connection_error_check: @@ -228,34 +238,29 @@ def send_request(self, method, path, data): self.host_counter = 0 self.connection_error_check = False self.connection._connected = False - + # Set backup host/hosts from the inventory. Host is not provided in the task. self.backup_hosts = self.inventory_hosts[0] - + # Set parameters from the task self.set_parameters(method, path, data) - + # Start operation on the host self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.connection.queue_message('step:', 'Initializing operation on host {0}'.format(self.connection.get_option('host'))) + self.connection.queue_message("step:", "Initializing operation on host {0}".format(self.connection.get_option("host"))) # If the credentials are mentioned, the first attempt at login is performed automatically via @ensure_connect before making a request. try: response, response_data = self.connection.send(path, data, method=method) - self.connection.queue_message('step:', 'Received response from {0} for {1} operation with HTTP: {2}'.format(self.connection.get_option('host'), method, response.getcode())) + self.connection.queue_message( + "step:", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) + ) except Exception as exc_response: - self.connection.queue_message('step:', 'Connection to {0} has failed: {1}'.format(self.connection.get_option('host'), exc_response)) - self.entered_exception = True + self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) self.handle_connection_error(exc_response) - - # finally block is always executed - finally: - if self.entered_exception: - self.entered_exception = False - self.connection.queue_message('step:', 'Retrying request on {0}'.format(self.connection.get_option('host'))) - # Exit recursive function. All the hosts are exhausted - if not self.connection_error_check: - return self.send_request(method, path, data) + self.connection.queue_message("step:", "Retrying request on {0}".format(self.connection.get_option("host"))) + # recurse through function for retrying the request + return self.send_request(method, path, data) # return statement executed upon each successful response from the request function return self._verify_response(response, method, path, response_data) @@ -265,95 +270,115 @@ def handle_connection_error(self, exception): if self.host_counter >= len(self.backup_hosts): # Base Case for send_request. All hosts are exhausted in the list. self.connection_error_check = True - raise ConnectionError("No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option('host'), exception)) - self.connection.queue_message('step:', 'Switching host from {0} to {1}'.format(self.connection.get_option('host'), self.backup_hosts[self.host_counter])) - self.connection.set_option("host", self.backup_hosts[self.host_counter]) + raise ConnectionError( + "No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option("host"), exception) + ) + self.connection.queue_message( + "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.backup_hosts[self.host_counter]) + ) + self.connection.set_option("host", self.backup_hosts[self.host_counter]) if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - return + return else: - # Login function is called until connection to a host is established or until all the hosts in the list are exhausted + # Login function is called until connection to a host is established or until all the hosts in the list are exhausted self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) # Built-in-function - def handle_httperror(self, exc_http_response): - self.connection.queue_message('step:', 'Failed to receive response from {0}: {1}'.format(self.connection.get_option('host'), exc_http_response)) - return exc_http_response + def handle_httperror(self, exc): + self.connection.queue_message("step:", "Failed to receive response from {0}: {1}".format(self.connection.get_option("host"), exc)) + if str(exc) == "HTTP Error 401: Unauthorized": + raise ConnectionError("Connection to {0} has failed with {1}".format(self.connection.get_option("host"), exc)) + return exc def _verify_response(self, response, method, path, response_data): - ''' Process the return code and response object from APIC ''' + """Process the return code and response object from APIC""" response_value = self._get_response_value(response_data) - if path.find('.json') != -1: + if path.find(".json") != -1: respond_data = self._response_to_json(response_value) else: respond_data = response_value response_code = response.getcode() - path = response.geturl() - msg = '{0} ({1} bytes)'.format(response.msg, len(response_value)) + path = re.match(r'^.*?\.json|^.*?\.xml', response.url).group(0) + # Response check to remain consistent with fetch_url's response + if str(response) == "HTTP Error 400: Bad Request": + msg = "{0}".format(response) + else: + msg = "{0} ({1} bytes)".format(response.msg, len(response_value)) return self._return_info(response_code, method, path, msg, respond_data) def _get_response_value(self, response_data): - ''' Extract string data from response_data returned from APIC ''' + """Extract string data from response_data returned from APIC""" return to_text(response_data.getvalue()) def _response_to_json(self, response_text): - ''' Convert response_text to json format ''' + """Convert response_text to json format""" try: return json.loads(response_text) if response_text else {} # JSONDecodeError only available on Python 3.5+ except Exception: - return 'Invalid JSON response: {0}'.format(response_text) + return "Invalid JSON response: {0}".format(response_text) def _return_info(self, response_code, method, path, msg, respond_data=None): - ''' Format success/error data and return with consistent format ''' + """Format success/error data and return with consistent format""" info = {} - info['status'] = response_code - info['method'] = method - info['url'] = path - info['msg'] = msg - info['body'] = respond_data + info["status"] = response_code + info["method"] = method + info["url"] = path + info["msg"] = msg + info["body"] = respond_data return info - def cert_auth(self, path, method, payload=''): - ''' Perform APIC signature-based authentication, not the expected SSL client certificate authentication. ''' + def cert_auth(self, path, method, payload=""): + """Perform APIC signature-based authentication, not the expected SSL client certificate authentication.""" headers = dict() if payload is None: - payload = '' + payload = "" try: if HAS_CRYPTOGRAPHY: key = list(self.connection.get_option("session_key").values())[0].encode() - sig_key = serialization.load_pem_private_key(key, password=None, backend=default_backend(),) + sig_key = serialization.load_pem_private_key( + key, + password=None, + backend=default_backend(), + ) else: sig_key = load_privatekey(FILETYPE_PEM, list(self.connection.get_option("session_key").values())[0]) except Exception: if os.path.exists(list(self.connection.get_option("session_key").values())[0]): try: - permission = 'r' + permission = "r" if HAS_CRYPTOGRAPHY: - permission = 'rb' + permission = "rb" with open(list(self.connection.get_option("session_key").values())[0], permission) as fh: private_key_content = fh.read() except Exception: raise ConnectionError("Cannot open private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) try: if HAS_CRYPTOGRAPHY: - sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend(),) + sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend()) else: sig_key = load_privatekey(FILETYPE_PEM, private_key_content) except Exception: raise ConnectionError("Cannot load private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) else: - raise ConnectionError("Provided private key {0} does not appear to be a private key.".format(list(self.connection.get_option("session_key").values())[0])) + raise ConnectionError( + "Provided private key {0} does not appear to be a private key.".format(list(self.connection.get_option("session_key").values())[0]) + ) sig_request = method + path + payload if HAS_CRYPTOGRAPHY: sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) else: - sig_signature = sign(sig_key, sig_request, 'sha256') - sig_dn = 'uni/userext/user-{0}/usercert-{1}'.format(self.connection.get_option("remote_user"), list(self.connection.get_option("session_key").keys())[0]) - headers['Cookie'] = 'APIC-Certificate-Algorithm=v1.0; ' +\ - 'APIC-Certificate-DN=%s; ' % sig_dn +\ - 'APIC-Certificate-Fingerprint=fingerprint; ' +\ - 'APIC-Request-Signature=%s' % to_native(base64.b64encode(sig_signature)) + sig_signature = sign(sig_key, sig_request, "sha256") + sig_dn = "uni/userext/user-{0}/usercert-{1}".format( + self.connection.get_option("remote_user"), list(self.connection.get_option("session_key").keys())[0] + ) + headers["Cookie"] = ( + "APIC-Certificate-Algorithm=v1.0; " + + "APIC-Certificate-DN=%s; " % sig_dn + + "APIC-Certificate-Fingerprint=fingerprint; " + + "APIC-Request-Signature=%s" % to_native(base64.b64encode(sig_signature)) + ) return headers diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 006e14a4f..90dc2e17a 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -52,6 +52,7 @@ from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_bytes, to_native from ansible.module_utils.basic import env_fallback +from ansible.module_utils._text import to_text from ansible.module_utils.connection import Connection # Optional, only used for APIC signature-based authentication @@ -74,7 +75,7 @@ # Optional, only used for XML payload try: - import lxml.etree + import lxml.etree # noqa HAS_LXML_ETREE = True except ImportError: @@ -100,7 +101,6 @@ def aci_argument_spec(): port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])), username=dict( type="str", - default="admin", aliases=["user"], fallback=(env_fallback, ["ACI_USERNAME", "ANSIBLE_NET_USERNAME"]), ), @@ -409,19 +409,12 @@ def login(self): payload = { "aaaUser": { "attributes": { - "name": self.params.get("username"), + "name": self.params.get("username", "admin"), "pwd": self.params.get("password"), } } } - resp, auth = fetch_url( - self.module, - url, - data=json.dumps(payload), - method="POST", - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) + resp, auth = self.api_call("POST", None, url, data=json.dumps(payload), output=True) # Handle APIC response if auth.get("status") != 200: @@ -559,100 +552,6 @@ def response_error(self): except (AttributeError, IndexError, KeyError): pass - def request(self, path, payload=None): - """Perform a REST request""" - - # Ensure method is set (only do this once) - self.define_method() - self.path = path - - if self.params.get("port") is not None: - self.url = "%(protocol)s://%(host)s:%(port)s/" % self.params + path.lstrip("/") - else: - self.url = "%(protocol)s://%(host)s/" % self.params + path.lstrip("/") - - # Sign and encode request as to APIC's wishes - if self.params.get("private_key"): - self.cert_auth(path=path, payload=payload) - - # Perform request - resp, info = fetch_url( - self.module, - self.url, - data=payload, - headers=self.headers, - method=self.params.get("method").upper(), - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) - - self.response = info.get("msg") - self.status = info.get("status") - - # Handle APIC response - if info.get("status") != 200: - try: - # APIC error - self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) - - self.response_json(resp.read()) - - def query(self, path): - """Perform a query with no payload""" - - self.path = path - - if self.params.get("port") is not None: - self.url = "%(protocol)s://%(host)s:%(port)s/" % self.params + path.lstrip("/") - else: - self.url = "%(protocol)s://%(host)s/" % self.params + path.lstrip("/") - - # Sign and encode request as to APIC's wishes - if self.params.get("private_key"): - self.cert_auth(path=path, method="GET") - - # Perform request - resp, query = fetch_url( - self.module, - self.url, - data=None, - headers=self.headers, - method="GET", - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) - - # Handle APIC response - if query.get("status") != 200: - self.response = query.get("msg") - self.status = query.get("status") - try: - # APIC error - self.response_json(query["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % query) - - query = json.loads(resp.read()) - - return json.dumps(query.get("imdata"), sort_keys=True, indent=2) + "\n" - - def request_diff(self, path, payload=None): - """Perform a request, including a proper diff output""" - self.result["diff"] = dict() - self.result["diff"]["before"] = self.query(path) - self.request(path, payload=payload) - # TODO: Check if we can use the request output for the 'after' diff - self.result["diff"]["after"] = self.query(path) - - if self.result.get("diff", {}).get("before") != self.result.get("diff", {}).get("after"): - self.result["changed"] = True - # TODO: This could be designed to update existing keys def update_qs(self, params): """Append key-value pairs to self.filter_string""" @@ -1238,7 +1137,7 @@ def delete_config(self): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes - self.api_call("DELETE") + self.api_call("DELETE", self.path, self.url, None, output=False) else: self.result["changed"] = True self.method = "DELETE" @@ -1359,8 +1258,10 @@ def get_existing(self): that this method can be used to supply the existing configuration when using the get_diff method. The response, status, and existing configuration will be added to the self.result dictionary. """ + uri = self.url + self.filter_string + path = self.path + self.filter_string - self.api_call("GET") + self.api_call("GET", path, uri, data=None, output=False) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1479,6 +1380,7 @@ def post_config(self, parent_class=None): # Sign and encode request as to APIC's wishes <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD url = self.url if parent_class is not None: @@ -1528,6 +1430,9 @@ def post_config(self, parent_class=None): ======= self.api_call("POST") >>>>>>> a77aefa ([ignore] Addition of queue messages) +======= + self.api_call("POST", self.path, self.url, json.dumps(self.config), output=False) +>>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) else: self.result["changed"] = True self.method = "POST" @@ -1554,7 +1459,8 @@ def exit_json(self, filter_existing=None, **kwargs): self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url - self.result["httpapi_logs"] = self.httpapi_logs + if self.httpapi_logs: + self.result["httpapi_logs"] = self.httpapi_logs if self.stdout: self.result["stdout"] = self.stdout @@ -1605,6 +1511,7 @@ def fail_json(self, msg, **kwargs): self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url + if self.httpapi_logs: self.result["httpapi_logs"] = self.httpapi_logs if "state" in self.params: @@ -1640,6 +1547,7 @@ def dump_json(self): if self.result.get("changed") is True: json.dump([mo], output_file) +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD def delete_config_request(self, path): @@ -1690,47 +1598,38 @@ def api_call(self, method): call_path = self.path call_url = self.url data = None +======= + def api_call(self, method, path, url, data=None, output=False): +>>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) resp = None if self.params.get("private_key"): - self.cert_auth(path=call_path, payload=data, method=method) + self.cert_auth(path=path, payload=data, method=method) if self.module._socket_path: self.params["validate_certs"] = False connect = Connection(self.module._socket_path) connect.get_params(self.headers.get("Cookie"), self.params) - info = connect.send_request(method, "/{0}".format(call_path), data) - self.url = info.get("url") + info = connect.send_request(method, "/{0}".format(path), data) self.httpapi_logs.extend(connect.pop_messages()) - self.stdout = str("Through plugin") + self.url = info.get("url") else: resp, info = fetch_url( self.module, - call_url, + url, data=data, headers=self.headers, method=method, timeout=self.params.get("timeout"), use_proxy=self.params.get("use_proxy"), ) + self.response = info.get("msg") self.status = info.get("status") self.method = method - # Handle APIC response - if info.get("status") == 200: - if method == "POST" or method == "DELETE": - self.result["changed"] = True - try: - if method == "GET": - self.existing = json.loads(resp.read())["imdata"] - else: - self.response_json(resp.read()) - except AttributeError: - if method == "GET": - self.existing = info["body"]["imdata"] - else: - self.response_json(info.get("body")) - + if output: + return resp, info else: +<<<<<<< HEAD try: # APIC error self.response_json(info["body"]) @@ -1747,3 +1646,27 @@ def api_call(self, method): ======= self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) >>>>>>> 5d0ead5 ([minor_change] Fix comments and formatting) +======= + # Handle APIC response + if info.get("status") == 200: + if method == "POST" or method == "DELETE": + self.result["changed"] = True + try: + if method == "GET": + self.existing = json.loads(resp.read())["imdata"] + else: + self.response_json(resp.read()) + except AttributeError: + if method == "GET": + self.existing = info["body"]["imdata"] + else: + self.response_json(info.get("body")) + else: + try: + # APIC error + self.response_json(info["body"]) + self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) + except KeyError: + # Connection error + self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) +>>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) diff --git a/plugins/modules/aci_cloud_subnet.py b/plugins/modules/aci_cloud_subnet.py index 2811537e0..31845f629 100644 --- a/plugins/modules/aci_cloud_subnet.py +++ b/plugins/modules/aci_cloud_subnet.py @@ -63,7 +63,6 @@ description: - Determine if a vNet Gateway Router will be deployed or not. - Only used when it is an azure cloud apic. - default: no type: bool default: false state: diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index bb5e9afcf..7763af0d3 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -304,25 +304,8 @@ def get_preview(aci): """ uri = aci.url + aci.filter_string path = aci.path + aci.filter_string - resp = None - if aci.module._socket_path: - connect = Connection(aci.module._socket_path) - connect.set_params(aci.headers.get('Cookie'), aci.params) - info = connect.send_request('GET', '/{0}'.format(path), None) - aci.url = info.get('url') - aci.httpapi_logs.extend(connect.pop_messages()) - else: - resp, info = fetch_url( - aci.module, - uri, - headers=aci.headers, - method="GET", - timeout=aci.module.params.get("timeout"), - use_proxy=aci.module.params.get("use_proxy"), - ) - aci.method = "GET" - aci.response = info.get("msg") - aci.status = info.get("status") + + resp, info = aci.api_call('GET', path, uri, data=None, output=True) # Handle APIC response if info.get("status") == 200: diff --git a/plugins/modules/aci_config_snapshot.py b/plugins/modules/aci_config_snapshot.py index e66b203fe..ceed6ecdd 100644 --- a/plugins/modules/aci_config_snapshot.py +++ b/plugins/modules/aci_config_snapshot.py @@ -227,6 +227,8 @@ sample: https://10.11.12.13/api/mo/uni/tn-production.json """ +import json + from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec @@ -302,9 +304,17 @@ def main(): aci.post_config() # Query for job information and add to results - # Change state to query else aci.request() will not execute a GET request but POST - aci.params["state"] = "query" - aci.request(path="/api/node/mo/uni/backupst/jobs-[uni/fabric/configexp-{0}].json".format(export_policy)) + path = "/api/node/mo/uni/backupst/jobs-[uni/fabric/configexp-{0}].json".format(export_policy) + if "port" in aci.params and aci.params.get("port") is not None: + port_url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") + else: + port_url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") + resp, info = aci.api_call('GET', path, port_url, data=None, output=True) + try: + aci.imdata = json.loads(resp.read())["imdata"] + except AttributeError: + aci.imdata = info["body"]["imdata"] + aci.result["job_details"] = aci.imdata[0].get("configJobCont", {}) # Reset state and url to display correct in output and trigger get_existing() function with correct url aci.url = reset_url diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 935651243..2722e63ec 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -279,9 +279,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec -from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_text -from ansible.module_utils.connection import Connection def update_qsl(url, params): @@ -416,30 +414,10 @@ def main(): path += "?rsp-subtree=modified" aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) - # Sign and encode request as to APIC's wishes - if aci.params.get("private_key") is not None: - aci.cert_auth(path=path, payload=payload) - - aci.method = aci.params.get("method").upper() + method = aci.params.get("method").upper() # Perform request - resp = None - if module._socket_path: - connect = Connection(aci.module._socket_path) - connect.get_params(aci.headers.get('Cookie'), aci.params) - info = connect.send_request(aci.method, '/{0}'.format(path), payload) - aci.url = info.get('url') - aci.httpapi_logs.extend(connect.pop_messages()) - else: - resp, info = fetch_url(module, aci.url, - data=payload, - headers=aci.headers, - method=aci.method, - timeout=aci.params.get('timeout'), - use_proxy=aci.params.get('use_proxy')) - - aci.response = info.get("msg") - aci.status = info.get("status") + resp, info = aci.api_call(method, path, aci.url, data=payload, output=True) # Report failure if info.get("status") != 200: diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 5d3726af5..424226081 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -8,8 +8,12 @@ aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 aci_password="sJ94 azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true ======= azure_cloud ansible_host=104.42.26.226 aci_hostname=104.42.26.226 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +<<<<<<< HEAD inventory_hosts aci_hostname=173.36.219.68 ansible_connection=ansible.netcommon.httpapi >>>>>>> 1293725 ([ignore_changes] Add inventory_hosts=6 to the test file) +======= +#inventory_hosts aci_hostname=173.36.219.69 ansible_user=ansible_github_ci ansible_password="sJ94G92#8dq2hx*K4qh" ansible_command_timeout=3 ansible_connection=ansible.netcommon.httpapi ansible_network_os=cisco.aci.aci ansible_httpapi_validate_certs=False ansible_httpapi_use_ssl=True ansible_httpapi_use_proxy=True ansible_httpapi_port=443 +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) [aci:vars] aci_username=ansible_github_ci diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml index 033ec2108..2b1c52ceb 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml @@ -62,13 +62,16 @@ # Run on multiple Hosts at task level using credentials # Run on multiple Hosts in inventory using credentials - ## Setup Environment ## +# NOTE - ansible.builtin.add_host is only used for testing!!! ansible_host variable needs to be absent in the inventory while using ansible.builtin.add_host +# - If you set same variable names across the playbook to tasks and inventory, the precedence changes where the higher precedence is given to the inventory and not vars set by set_fact. We make sure that the variable names are different. + + # Setup Environment ## - name: Set vars set_fact: aci_info_single_host_active: &aci_info_single_host_active - host: "{{ ansible_single_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 173.36.219.68 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -77,8 +80,8 @@ - name: Set vars set_fact: aci_info_single_host_active_certificate: &aci_info_single_host_active_certificate - host: "{{ ansible_single_host_active }}" - username: "{{ ansible_user }}" + host: 173.36.219.68 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -87,9 +90,9 @@ - name: Set vars set_fact: aci_info_second_single_host_active: &aci_info_second_single_host_active - host: "{{ ansible_second_single_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 173.36.219.69 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' use_proxy: '{{ use_proxy | default(true) }}' @@ -100,9 +103,9 @@ - name: Set vars set_fact: aci_info_third_single_host_active: &aci_info_third_single_host_active - host: "{{ ansible_third_single_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 173.36.219.73 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -145,7 +148,7 @@ - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_single_host_active }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -434,14 +437,14 @@ - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" -###################################################################### +# ###################################################################### - name: Set vars set_fact: aci_info_single_host_inactive: &aci_info_single_host_inactive - host: "{{ ansible_single_host_inactive }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 124.145.225.187 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -450,8 +453,8 @@ - name: Set vars set_fact: aci_info_single_host_inactive_certificate: &aci_info_single_host_inactive_certificate - host: "{{ ansible_single_host_inactive }}" - username: "{{ ansible_user }}" + host: 124.145.225.187 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -460,7 +463,7 @@ - name: Set Single inactive host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_single_host_inactive }}" + ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -676,9 +679,9 @@ - name: Set vars set_fact: aci_info_first_host_active: &aci_info_first_host_active - host: "{{ ansible_first_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -687,8 +690,8 @@ - name: Set vars set_fact: aci_info_first_host_active_certificate: &aci_info_first_host_active_certificate - host: "{{ ansible_first_host_active }}" - username: "{{ ansible_user }}" + host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -697,7 +700,7 @@ - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_first_host_active }}" + ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -989,9 +992,9 @@ - name: Set vars set_fact: aci_info_second_active_host: &aci_info_second_active_host - host: "{{ ansible_second_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1000,8 +1003,8 @@ - name: Set vars set_fact: aci_info_second_active_host_certificate: &aci_info_second_active_host_certificate - host: "{{ ansible_second_host_active }}" - username: "{{ ansible_user }}" + host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1010,7 +1013,7 @@ - name: Set the inventory with the second host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_second_host_active }}" + ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -1310,9 +1313,9 @@ - name: Set vars set_fact: aci_info_last_active_host: &aci_info_last_active_host - host: "{{ ansible_last_host_active }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1321,8 +1324,8 @@ - name: Set vars set_fact: aci_info_last_active_host_certificate: &aci_info_last_active_host_certificate - host: "{{ ansible_last_host_active }}" - username: "{{ ansible_user }}" + host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1331,7 +1334,7 @@ - name: Set the inventory with the last host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_last_host_active }}" + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -1663,9 +1666,9 @@ - name: Set vars set_fact: aci_info_all_hosts_inactive: &aci_info_all_hosts_inactive - host: "{{ ansible_all_hosts_inactive }}" - username: "{{ ansible_user }}" - password: "{{ ansible_password }}" + host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 + username: "{{ aci_username }}" + password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1674,8 +1677,8 @@ - name: Set vars set_fact: aci_info_all_hosts_inactive_certificate: &aci_info_all_hosts_inactive_certificate - host: "{{ ansible_all_hosts_inactive }}" - username: "{{ ansible_user }}" + host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 + username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' timeout: "{{ ansible_command_timeout }}" @@ -1684,7 +1687,7 @@ - name: Set the inventory with all the hosts in the inventory as inactive ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_all_hosts_inactive }}" + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2237,7 +2240,7 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host1 }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2290,7 +2293,7 @@ - name: Set hosts 2 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host2 }}" + ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2326,7 +2329,7 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host3 }}" + ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2362,7 +2365,7 @@ - name: Set hosts 4 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host4 }}" + ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2398,7 +2401,7 @@ - name: Set hosts 5 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host5 }}" + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2434,7 +2437,7 @@ - name: Set hosts 6 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host6 }}" + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -2605,7 +2608,7 @@ - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_single_host_active }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -2900,7 +2903,7 @@ - name: Set Single inactive host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_single_host_inactive }}" + ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -3117,7 +3120,7 @@ - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_first_host_active }}" + ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -3410,7 +3413,7 @@ - name: Set the inventory with the second host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_second_host_active }}" + ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -3711,7 +3714,7 @@ - name: Set the inventory with the last host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_last_host_active }}" + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4044,7 +4047,7 @@ - name: Set the inventory with all the hosts in the inventory as inactive ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_all_hosts_inactive }}" + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4598,7 +4601,7 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host1 }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4635,7 +4638,7 @@ - name: Set hosts 2 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host2 }}" + ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: "{{ ansible_command_timeout }}" @@ -4671,7 +4674,7 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host3 }}" + ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4708,7 +4711,7 @@ - name: Set hosts 4 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host4 }}" + ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4745,7 +4748,7 @@ - name: Set hosts 5 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host5 }}" + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4782,7 +4785,7 @@ - name: Set hosts 6 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host6 }}" + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4967,10 +4970,11 @@ - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" +# Invalid session_key - name: Set host 7 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host7 }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} @@ -4988,10 +4992,11 @@ state: absent ignore_errors: yes +# Non existing session_key - name: Set host 8 ansible.builtin.add_host: name: inventory_hosts - ansible_host: "{{ ansible_host7 }}" + ansible_host: 173.36.219.68 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} @@ -5002,10 +5007,87 @@ ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - name: Delete Tenant via inventory host (Final Task in the Test) + - name: Delete Tenant via inventory host cisco.aci.aci_tenant: output_level: debug tenant: ansible_test state: absent ignore_errors: yes - \ No newline at end of file + +# Incorrect Password + - name: Set host 9 + ansible.builtin.add_host: + name: inventory_hosts + ansible_host: 173.36.219.68 + ansible_user: "{{ ansible_user }}" + ansible_password: "incorrect_password" + ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_connection: "{{ ansible_connection }}" + ansible_network_os: "{{ ansible_network_os }}" + ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" + ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" + ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" + + - name: Delete Tenant with incorrect password + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: yes + +# Clean up Environment + - name: Delete a tenant using an XML string + cisco.aci.aci_rest: + <<: *aci_info_single_host_active + path: /api/mo/uni/tn-[Sales].xml + method: delete + content: '' + ignore_errors: yes + + - name: Remove user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: absent + ignore_errors: yes + + - name: Remove user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_second_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: absent + ignore_errors: yes + + - name: Remove user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info_third_single_host_active + aaa_user: "{{ ansible_user }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: absent + ignore_errors: yes + + - name: Delete Tenant + cisco.aci.aci_tenant: + <<: *aci_info_single_host_active + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Delete Tenant + cisco.aci.aci_tenant: + <<: *aci_info_second_single_host_active + tenant: ansible_test + state: absent + ignore_errors: yes + + - name: Delete Tenant (Final Task in the Test) + cisco.aci.aci_tenant: + <<: *aci_info_third_single_host_active + tenant: ansible_test + state: absent + ignore_errors: yes diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index bb55fb555..7d2c9ed13 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -1,13 +1,13 @@ -# Test code for the ACI modules -# Copyright: (c) 2017, Dag Wieers (@dagwieers) -# Copyright: (c) 2020, Lionel Hercot (@lhercot) +# # Test code for the ACI modules +# # Copyright: (c) 2017, Dag Wieers (@dagwieers) +# # Copyright: (c) 2020, Lionel Hercot (@lhercot) -# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +# # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -# - name: Test that we have an ACI APIC host, ACI username and ACI password -# fail: -# msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' -# when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined - include_tasks: httpapi_inventory_password.yml tags: httpapi_inventory_password @@ -18,18 +18,18 @@ path: "{{ item }}" state: absent with_items: - - "{{ ansible_host }}_cm_add_tenant.json" - - "{{ ansible_host }}_nm_add_tenant.json" - - "{{ ansible_host }}_cm_add_tenant_again.json" - - "{{ ansible_host }}_nm_add_tenant_again.json" - - "{{ ansible_host }}_cm_add_tenant_descr.json" - - "{{ ansible_host }}_nm_add_tenant_descr.json" - - "{{ ansible_host }}_cm_add_tenant_descr_again.json" - - "{{ ansible_host }}_nm_add_tenant_descr_again.json" - - "{{ ansible_host }}_cm_remove_tenant.json" - - "{{ ansible_host }}_nm_remove_tenant.json" - - "{{ ansible_host }}_cm_remove_tenant_again.json" - - "{{ ansible_host }}_nm_remove_tenant_again.json" + - "{{ aci_hostname }}_cm_add_tenant.json" + - "{{ aci_hostname }}_nm_add_tenant.json" + - "{{ aci_hostname }}_cm_add_tenant_again.json" + - "{{ aci_hostname }}_nm_add_tenant_again.json" + - "{{ aci_hostname }}_cm_add_tenant_descr.json" + - "{{ aci_hostname }}_nm_add_tenant_descr.json" + - "{{ aci_hostname }}_cm_add_tenant_descr_again.json" + - "{{ aci_hostname }}_nm_add_tenant_descr_again.json" + - "{{ aci_hostname }}_cm_remove_tenant.json" + - "{{ aci_hostname }}_nm_remove_tenant.json" + - "{{ aci_hostname }}_cm_remove_tenant_again.json" + - "{{ aci_hostname }}_nm_remove_tenant_again.json" # CLEAN ENVIRONMENT - name: Remove tenant @@ -60,44 +60,54 @@ annotation: ansible_test owner_key: ansible_key owner_tag: ansible_tag +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_add_tenant.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant - name: Dump content of files debug: - msg: "{{ lookup('file', ansible_host +'_cm_add_tenant.json')}}" + msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" - name: Add tenant (normal mode) cisco.aci.aci_tenant: <<: *tenant_present - output_path: "{{ ansible_host }}_nm_add_tenant.json" + output_path: "{{ aci_hostname }}_nm_add_tenant.json" register: nm_add_tenant - name: Add tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_present +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_again.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_add_tenant_again.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_again - name: Add tenant again (normal mode) cisco.aci.aci_tenant: <<: *tenant_present - output_path: "{{ ansible_host }}_nm_add_tenant_again.json" + output_path: "{{ aci_hostname }}_nm_add_tenant_again.json" register: nm_add_tenant_again - name: Dump content of files debug: - msg: "{{ lookup('file', ansible_host + '_cm_add_tenant.json')}}" + msg: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json')}}" - name: Store file content on variables for create object set_fact: - fc_cm_add_tenant: "{{ lookup('file', ansible_host + '_cm_add_tenant.json') | from_json }}" - fc_nm_add_tenant: "{{ lookup('file', ansible_host + '_nm_add_tenant.json') | from_json }}" - fc_cm_add_tenant_again: "{{ lookup('file', ansible_host + '_cm_add_tenant_again.json') }}" - fc_nm_add_tenant_again: "{{ lookup('file', ansible_host + '_nm_add_tenant_again.json') }}" + fc_cm_add_tenant: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json') | from_json }}" + fc_nm_add_tenant: "{{ lookup('file', aci_hostname + '_nm_add_tenant.json') | from_json }}" + fc_cm_add_tenant_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_again.json') }}" + fc_nm_add_tenant_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_again.json') }}" - name: Log file content verification for create object assert: @@ -132,8 +142,13 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_descr.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_add_tenant_descr.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_descr - name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) @@ -143,7 +158,7 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed - output_path: "{{ ansible_host }}_nm_add_tenant_descr.json" + output_path: "{{ aci_hostname }}_nm_add_tenant_descr.json" register: nm_add_tenant_descr - name: Change description and annotation/owner_tag/owner_key of tenant again (check_mode) @@ -153,8 +168,13 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_descr_again.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_add_tenant_descr_again.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_descr_again - name: Change description and annotation of tenant again (normal mode) @@ -164,15 +184,15 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed - output_path: "{{ ansible_host }}_nm_add_tenant_descr_again.json" + output_path: "{{ aci_hostname }}_nm_add_tenant_descr_again.json" register: nm_add_tenant_descr_again - name: Store file content on variables for update object set_fact: - fc_cm_add_tenant_descr: "{{ lookup('file', ansible_host + '_cm_add_tenant_descr.json') | from_json }}" - fc_nm_add_tenant_descr: "{{ lookup('file', ansible_host + '_nm_add_tenant_descr.json') | from_json }}" - fc_cm_add_tenant_descr_again: "{{ lookup('file', ansible_host + '_cm_add_tenant_descr_again.json') }}" - fc_nm_add_tenant_descr_again: "{{ lookup('file', ansible_host + '_nm_add_tenant_descr_again.json') }}" + fc_cm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr.json') | from_json }}" + fc_nm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr.json') | from_json }}" + fc_cm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr_again.json') }}" + fc_nm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr_again.json') }}" - name: Log file content verification for update object assert: @@ -299,35 +319,45 @@ - name: Remove tenant (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_remove_tenant.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_remove_tenant.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_remove_tenant - name: Remove tenant (normal mode) cisco.aci.aci_tenant: <<: *tenant_absent - output_path: "{{ ansible_host }}_nm_remove_tenant.json" + output_path: "{{ aci_hostname }}_nm_remove_tenant.json" register: nm_remove_tenant - name: Remove tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent +<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_remove_tenant_again.json" check_mode: true +======= + output_path: "{{ aci_hostname }}_cm_remove_tenant_again.json" + check_mode: yes +>>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_remove_tenant_again - name: Remove tenant again (normal mode) cisco.aci.aci_tenant: <<: *tenant_absent - output_path: "{{ ansible_host }}_nm_remove_tenant_again.json" + output_path: "{{ aci_hostname }}_nm_remove_tenant_again.json" register: nm_remove_tenant_again - name: Store file content on variables for delete object set_fact: - fc_cm_remove_tenant: "{{ lookup('file', ansible_host + '_cm_remove_tenant.json') | from_json }}" - fc_nm_remove_tenant: "{{ lookup('file', ansible_host + '_nm_remove_tenant.json') | from_json }}" - fc_cm_remove_tenant_again: "{{ lookup('file', ansible_host + '_cm_remove_tenant_again.json') }}" - fc_nm_remove_tenant_again: "{{ lookup('file', ansible_host + '_nm_remove_tenant_again.json') }}" + fc_cm_remove_tenant: "{{ lookup('file', aci_hostname + '_cm_remove_tenant.json') | from_json }}" + fc_nm_remove_tenant: "{{ lookup('file', aci_hostname + '_nm_remove_tenant.json') | from_json }}" + fc_cm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_cm_remove_tenant_again.json') }}" + fc_nm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_nm_remove_tenant_again.json') }}" - name: Log file content verification for delete object assert: From b801d5cacc5affbff103359d6975979ae1f1d08f Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 12 Apr 2023 13:26:31 -0400 Subject: [PATCH 32/49] [ignore_changes] Added set_hosts function --- plugins/httpapi/aci.py | 233 +++----- plugins/module_utils/aci.py | 19 +- tests/integration/inventory.networking | 10 +- .../tasks/httpapi_inventory_password.yml | 306 ++++------- .../targets/aci_tenant/tasks/main.yml | 515 +++++++++--------- 5 files changed, 458 insertions(+), 625 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 811175a41..2315dd8d5 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -16,8 +16,6 @@ from __future__ import absolute_import, division, print_function -from importlib.resources import path -from operator import methodcaller __metaclass__ = type @@ -42,6 +40,7 @@ from ansible.module_utils._text import to_text, to_native from ansible.module_utils.connection import ConnectionError from ansible.plugins.httpapi import HttpApiBase +from copy import copy # Optional, only used for APIC signature-based authentication try: @@ -61,38 +60,37 @@ except ImportError: HAS_CRYPTOGRAPHY = False +CONNECTION_MAP = {"username": "remote_user", "timeout": "persistent_command_timeout"} +RESET_KEYS = ["username", "password", "port"] +CONNECTION_KEYS = RESET_KEYS + ["timeout", "use_proxy", "use_ssl", "validate_certs"] class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) self.auth = None self.params = None - self.check_auth_from_private_key_task = False - self.check_auth_from_credentials_task = False - self.check_auth_from_private_key_inventory = False - self.check_auth_from_credentials_inventory = False + self.result = {} + self.check_authentication = "" self.backup_hosts = None self.inventory_hosts = [] self.host_counter = 0 - self.entered_exception = False self.connection_error_check = False - - def get_params(self, auth, params): + self.connection_parameters = {} + self.begin_task = False + self.executed_exit_function = False + self.current_host = None + self.provided_hosts = None + self.get_first_item = [] + + def set_params(self, auth, params): self.params = params self.auth = auth - def get_backup_hosts_from_inventory(self): - # append is used here to store the first list of hosts available in self.connection.get_option("host") in the 0th position of the list inventory_host. - # This is done because we keep changing the value of 'host' constantly using self.connection.set_option("host") when a list of hosts is provided. - # We always want access to the original list of hosts from the inventory when the tasks are running on them. - # Case: Host is provided in the inventory - self.inventory_hosts.append(re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",")) - # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted def login(self, username, password): """Log in to APIC""" # Perform login request - self.connection.queue_message("step:", "Establishing connection to {0}".format(self.connection.get_option("host"))) + self.connection.queue_message("step:", "Establishing login to {0}".format(self.connection.get_option("host"))) method = "POST" path = "/api/aaaLogin.json" payload = {"aaaUser": {"attributes": {"name": username, "pwd": password}}} @@ -104,10 +102,9 @@ def login(self, username, password): "Cookie": "APIC-Cookie={0}".format(self._response_to_json(response_value).get("imdata")[0]["aaaLogin"]["attributes"]["token"]) } self.connection.queue_message("step:", "Connection to {0} was successful".format(self.connection.get_option("host"))) - except Exception as exc: - # Catch exception from HTTPError() - if "401" in str(exc): - raise + except Exception as exc_response: + self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) + self.handle_connection_error(exc_response) def logout(self): method = "POST" @@ -122,141 +119,66 @@ def logout(self): self.connection._auth = None self._verify_response(response, method, path, response_data) - def set_parameters(self, method, path, data): - if self.params.get("port") is not None: - self.connection.set_option("port", self.params.get("port")) - - if self.params.get("username") is not None: - self.connection.set_option("remote_user", self.params.get("username")) - - if self.params.get("use_proxy") is not None: - self.connection.set_option("use_proxy", self.params.get("use_proxy")) - - if self.params.get("use_ssl") is not None: - self.connection.set_option("use_ssl", self.params.get("use_ssl")) - - if self.params.get("validate_certs") is not None: - self.connection.set_option("validate_certs", self.params.get("validate_certs")) - - # The command timeout which is the response timeout from APIC - # If the persistent_connect_timeout is less than the response timeout from APIC the persistent socket connection will fail - if self.params.get("timeout") is not None: - self.connection.set_option("persistent_command_timeout", self.params.get("timeout")) - self.connection.set_option("persistent_connect_timeout", self.params.get("timeout") + 30) - - # Start with certificate authentication - if self.auth is not None: - # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate at the task level. - if self.connection.get_option("session_key") is not None: - self.connection.set_option("session_key", None) - # Check if the previous task was running with the credentials at the task level/inventory or the certificate in the inventory - if self.check_auth_from_credentials_task or self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_inventory: - self.host_counter = 0 - self.check_auth_from_credentials_task = False - self.check_auth_from_credentials_inventory = False - self.check_auth_from_private_key_inventory = False - self.check_auth_from_private_key_task = True - self.connection._auth = {"Cookie": "{0}".format(self.auth)} - self.connection.queue_message("step:", "Setting certificate authentication at task level") - # Override parameter in @ensure_connect - self.connection._connected = True - # Start with credential authentication - elif self.params.get("password") is not None: - # If session_key is present in the inventory, certificate in the task is ignored. In order to avoid this, we explicitly set session_key to None - # to give preference to credentials/certificate at the task level. - if self.connection.get_option("session_key") is not None: - self.connection.set_option("session_key", None) - self.connection.set_option("password", self.params.get("password")) - # Check if the previous task was running with the certificate in at the task level/inventory or the credentials in the inventory - if self.check_auth_from_private_key_task or self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_inventory: - self.host_counter = 0 - # Enforce @ensure_connect - self.connection._connected = False - self.check_auth_from_private_key_task = False - self.check_auth_from_credentials_inventory = False - self.check_auth_from_private_key_inventory = False - self.check_auth_from_credentials_task = True - self.connection.queue_message("step:", "Setting credential authentication at task level") - # Note: session_key takes precedence over password if both of them are present in the inventory - elif self.connection.get_option("session_key") is not None: - # Check if the previous task was running with the certificate at the task level or the credentials in the inventory/task - if self.check_auth_from_credentials_inventory or self.check_auth_from_private_key_task or self.check_auth_from_credentials_task: - self.host_counter = 0 - self.check_auth_from_credentials_inventory = False - self.check_auth_from_private_key_task = False - self.check_auth_from_credentials_task = False - self.check_auth_from_private_key_inventory = True - self.connection.queue_message("step:", "Setting certificate authentication from inventory") - self.connection._auth = {"Cookie": "{0}".format(self.cert_auth(path, method, data).get("Cookie"))} - # Override parameter in @ensure_connect - self.connection._connected = True - # No session_key provided. Use password in the inventory instead - else: - # Check if the previous task was running with the certificate at the task/inventory level or the credentials in the task - if self.check_auth_from_private_key_inventory or self.check_auth_from_private_key_task or self.check_auth_from_credentials_task: - self.host_counter = 0 - # Enforce @ensure_connect + def set_parameters(self): + connection_parameters = {} + for key in CONNECTION_KEYS: + value = self.params.get(key) if self.params.get(key) is not None else self.connection.get_option(CONNECTION_MAP.get(key, key)) + self.connection.set_option(CONNECTION_MAP.get(key, key), value) + if key == "timeout" and self.params.get(key) is not None: + self.connection.set_option("persistent_connect_timeout", value + 30) + + connection_parameters[key] = value + if self.connection_parameters and value != self.connection_parameters.get(key) and key in RESET_KEYS: self.connection._connected = False - self.check_auth_from_private_key_inventory = False - self.check_auth_from_private_key_task = False - self.check_auth_from_credentials_task = False - self.check_auth_from_credentials_inventory = True - self.connection.queue_message("step:", "Setting credential authentication from inventory") + self.connection.queue_message("step", "Re-setting connection due to change in {0}".format(key)) - # One API call is made via each call to send_request from aci.py in module_utils - # As long as a host is active in the list we make sure that the API call goes through - def send_request(self, method, path, data): - """This method handles all APIC REST API requests other than login""" + # if self.auth is not None or self.connection.get_option("session_key") is not None: + # self.connection._connected = True - # Note: Preference is given to Hosts mentioned at task level in the playbook - if self.params.get("host") is not None: - # Case: Host is provided in the task of a playbook - task_hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") - - # We check whether: - # 1.The list of hosts provided in two consecutive tasks are not the same - # 2.connection_error_check was set in the previous task - # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). - # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if (self.backup_hosts is not None and self.backup_hosts != task_hosts) or self.connection_error_check: - self.host_counter = 0 - self.connection_error_check = False - self.connection._connected = False + if self.connection_parameters != connection_parameters: + self.connection_parameters = copy(connection_parameters) - self.backup_hosts = task_hosts - else: - # Case: Hosts from the inventory are used - self.get_backup_hosts_from_inventory() - - # We check whether: - # 1.The list of hosts provided in two consecutive tasks are not the same - # 2.connection_error_check was set in the previous task - # If yes, we begin the operation on the first host of the list. (Memory of the host in the list-reset). - # If no, we continue operation on the same host on which previous task was running (Memory of the host in the list-preserved). - if (self.backup_hosts is not None and self.backup_hosts != self.inventory_hosts[0]) or self.connection_error_check: - self.host_counter = 0 - self.connection_error_check = False - self.connection._connected = False + def set_hosts(self): + if self.params.get("host") is not None: + get_hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") + else: + self.get_first_item.append(re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",")) + get_hosts = self.get_first_item[0] - # Set backup host/hosts from the inventory. Host is not provided in the task. - self.backup_hosts = self.inventory_hosts[0] + if self.provided_hosts is None: + self.provided_hosts = get_hosts + self.connection.queue_message( + "step:", "Provided Hosts: {0}".format(self.provided_hosts) + ) + self.backup_hosts = self.provided_hosts + self.current_host = self.backup_hosts.pop(0) + elif (len(self.backup_hosts) != 0 and self.current_host not in get_hosts) or self.connection_error_check == True: + self.connection_error_check = False + self.connection._connected = False + self.connection.queue_message( + "step:", "Provided hosts have changed: {0}".format(get_hosts) + ) + self.backup_hosts = get_hosts + self.current_host = self.backup_hosts.pop(0) + self.connection.set_option("host", self.current_host) - # Set parameters from the task - self.set_parameters(method, path, data) + # One API call is made via each call to send_request from aci.py in module_utils + # As long as a host is active in the list we make sure that the API call goes through + def send_request(self, method, path, data): + """This method handles all APIC REST API requests other than login""" - # Start operation on the host - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - self.connection.queue_message("step:", "Initializing operation on host {0}".format(self.connection.get_option("host"))) + self.set_parameters() + self.set_hosts() - # If the credentials are mentioned, the first attempt at login is performed automatically via @ensure_connect before making a request. try: response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message( "step:", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) ) except Exception as exc_response: - self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) + if len(self.backup_hosts) == 0: + return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), str(exc_response)) + self.connection.queue_message("step:", "Connection to {0} has failed in between operations with: {1}".format(self.connection.get_option("host"), exc_response)) self.handle_connection_error(exc_response) self.connection.queue_message("step:", "Retrying request on {0}".format(self.connection.get_option("host"))) # recurse through function for retrying the request @@ -266,28 +188,28 @@ def send_request(self, method, path, data): # Custom error handler def handle_connection_error(self, exception): - self.host_counter += 1 - if self.host_counter >= len(self.backup_hosts): - # Base Case for send_request. All hosts are exhausted in the list. + if len(self.backup_hosts) == 0: self.connection_error_check = True raise ConnectionError( "No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option("host"), exception) ) + self.current_host = self.backup_hosts.pop(0) self.connection.queue_message( - "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.backup_hosts[self.host_counter]) + "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) ) - self.connection.set_option("host", self.backup_hosts[self.host_counter]) - if self.auth is not None or (self.connection.get_option("session_key") is not None and self.auth is None): - return - else: - # Login function is called until connection to a host is established or until all the hosts in the list are exhausted - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + self.connection.set_option("host", self.current_host) + # Login function is called until connection to a host is established or until all the hosts in the list are exhausted + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) # Built-in-function def handle_httperror(self, exc): self.connection.queue_message("step:", "Failed to receive response from {0}: {1}".format(self.connection.get_option("host"), exc)) - if str(exc) == "HTTP Error 401: Unauthorized": - raise ConnectionError("Connection to {0} has failed with {1}".format(self.connection.get_option("host"), exc)) + if exc.code == 401: + return False + elif exc.code == 403: + self.connection._auth = None + self.login(self.connection.get_option('remote_user'), self.connection.get_option('password')) + return True return exc def _verify_response(self, response, method, path, response_data): @@ -325,7 +247,8 @@ def _return_info(self, response_code, method, path, msg, respond_data=None): info["method"] = method info["url"] = path info["msg"] = msg - info["body"] = respond_data + if respond_data is not None: + info["body"] = respond_data return info def cert_auth(self, path, method, payload=""): diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 90dc2e17a..9c46399c9 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -128,10 +128,10 @@ def aci_argument_spec(): choices=["debug", "info", "normal"], fallback=(env_fallback, ["ACI_OUTPUT_LEVEL"]), ), - timeout=dict(type="int", default=30, fallback=(env_fallback, ["ACI_TIMEOUT"])), - use_proxy=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_USE_PROXY"])), - use_ssl=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_USE_SSL"])), - validate_certs=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_VALIDATE_CERTS"])), + timeout=dict(type="int", fallback=(env_fallback, ["ACI_TIMEOUT"])), + use_proxy=dict(type="bool", fallback=(env_fallback, ["ACI_USE_PROXY"])), + use_ssl=dict(type="bool", fallback=(env_fallback, ["ACI_USE_SSL"])), + validate_certs=dict(type="bool", fallback=(env_fallback, ["ACI_VALIDATE_CERTS"])), output_path=dict(type="str", fallback=(env_fallback, ["ACI_OUTPUT_PATH"])), ) @@ -1602,15 +1602,15 @@ def api_call(self, method): def api_call(self, method, path, url, data=None, output=False): >>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) resp = None + info = {} if self.params.get("private_key"): self.cert_auth(path=path, payload=data, method=method) if self.module._socket_path: - self.params["validate_certs"] = False connect = Connection(self.module._socket_path) - connect.get_params(self.headers.get("Cookie"), self.params) + connect.set_params(self.headers.get("Cookie"), self.params) info = connect.send_request(method, "/{0}".format(path), data) - self.httpapi_logs.extend(connect.pop_messages()) self.url = info.get("url") + self.httpapi_logs.extend(connect.pop_messages()) else: resp, info = fetch_url( self.module, @@ -1618,10 +1618,11 @@ def api_call(self, method, path, url, data=None, output=False): data=data, headers=self.headers, method=method, - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), + timeout=self.params.get("timeout", 30), + use_proxy=self.params.get("use_proxy", True), ) + self.response = info.get("msg") self.status = info.get("status") self.method = method diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 424226081..ec6249bee 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -4,16 +4,8 @@ cn-dmz-apic-m1-03-v52 ansible_host=173.36.219.69 aci_hostname=173.36.219.69 cn-dmz-apic-m1-04-v60 ansible_host=173.36.219.70 aci_hostname=173.36.219.70 cn-dmz-apic-m1-07-v32 ansible_host=173.36.219.73 aci_hostname=173.36.219.73 aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a -<<<<<<< HEAD azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true -======= -azure_cloud ansible_host=104.42.26.226 aci_hostname=104.42.26.226 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true -<<<<<<< HEAD -inventory_hosts aci_hostname=173.36.219.68 ansible_connection=ansible.netcommon.httpapi ->>>>>>> 1293725 ([ignore_changes] Add inventory_hosts=6 to the test file) -======= -#inventory_hosts aci_hostname=173.36.219.69 ansible_user=ansible_github_ci ansible_password="sJ94G92#8dq2hx*K4qh" ansible_command_timeout=3 ansible_connection=ansible.netcommon.httpapi ansible_network_os=cisco.aci.aci ansible_httpapi_validate_certs=False ansible_httpapi_use_ssl=True ansible_httpapi_use_proxy=True ansible_httpapi_port=443 ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) +#inventory_hosts aci_hostname=173.36.219.72 ansible_user=ansible_github_ci ansible_password="sJ94G92#8dq2hx*K4qh" ansible_command_timeout=5 ansible_connection=ansible.netcommon.httpapi ansible_network_os=cisco.aci.aci ansible_httpapi_validate_certs=False ansible_httpapi_use_ssl=True ansible_httpapi_use_proxy=True ansible_httpapi_port=443 [aci:vars] aci_username=ansible_github_ci diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml index 2b1c52ceb..42b9b04b0 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml @@ -69,46 +69,22 @@ - name: Set vars set_fact: aci_info_single_host_active: &aci_info_single_host_active - host: 173.36.219.68 + host: 173.36.219.72 username: "{{ aci_username }}" password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars set_fact: aci_info_single_host_active_certificate: &aci_info_single_host_active_certificate - host: 173.36.219.68 + host: 173.36.219.72 username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" - output_level: debug - - - name: Set vars - set_fact: - aci_info_second_single_host_active: &aci_info_second_single_host_active - host: 173.36.219.69 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - use_proxy: '{{ use_proxy | default(true) }}' - port: '{{ ansible_httpapi_port }}' - timeout: "{{ ansible_command_timeout }}" - output_level: debug - - - name: Set vars - set_fact: - aci_info_third_single_host_active: &aci_info_third_single_host_active - host: 173.36.219.73 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Add a tenant using an XML string @@ -127,31 +103,15 @@ certificate: "{{ lookup('file', 'certs/admin.crt') }}" state: present - - name: Add user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_second_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: present - - - name: Add user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_third_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: present - ## Plugin Tests using password from inventory (Part1) ## - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -437,7 +397,7 @@ - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" -# ###################################################################### +###################################################################### - name: Set vars set_fact: @@ -447,7 +407,7 @@ password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars @@ -457,7 +417,7 @@ username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set Single inactive host in inventory @@ -466,7 +426,7 @@ ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -679,31 +639,31 @@ - name: Set vars set_fact: aci_info_first_host_active: &aci_info_first_host_active - host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 username: "{{ aci_username }}" password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars set_fact: aci_info_first_host_active_certificate: &aci_info_first_host_active_certificate - host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -992,31 +952,31 @@ - name: Set vars set_fact: aci_info_second_active_host: &aci_info_second_active_host - host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 username: "{{ aci_username }}" password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars set_fact: aci_info_second_active_host_certificate: &aci_info_second_active_host_certificate - host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set the inventory with the second host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -1230,7 +1190,7 @@ that: - task_pwd_delete_tenant_second_active_host_1.current == [] - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_1 is changed - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1240,7 +1200,7 @@ - task_cert_delete_tenant_second_active_host_2 is changed - task_cert_delete_tenant_second_active_host_2.current == [] - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_2 is changed - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1250,7 +1210,7 @@ - task_pwd_delete_tenant_second_active_host_3 is changed - task_pwd_delete_tenant_second_active_host_3.current == [] - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_3 is changed - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1260,7 +1220,7 @@ - inventory_pwd_delete_tenant_second_active_host_4 is changed - inventory_pwd_delete_tenant_second_active_host_4.current == [] - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_second_active_host_4 is changed - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1270,7 +1230,7 @@ - task_pwd_delete_tenant_second_active_host_5 is changed - task_pwd_delete_tenant_second_active_host_5.current == [] - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_5 is changed - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1280,7 +1240,7 @@ - task_cert_delete_tenant_second_active_host_6 is changed - task_cert_delete_tenant_second_active_host_6.current == [] - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_6 is changed - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1289,7 +1249,7 @@ - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - inventory_pwd_delete_tenant_second_active_host_7 is changed - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_delete_tenant_second_active_host_7.current == [] - inventory_pwd_add_tenant_second_active_host_7 is changed - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" @@ -1300,7 +1260,7 @@ - task_cert_delete_tenant_second_active_host_8 is changed - task_cert_delete_tenant_second_active_host_8.current == [] - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_8 is changed - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1313,31 +1273,31 @@ - name: Set vars set_fact: aci_info_last_active_host: &aci_info_last_active_host - host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 username: "{{ aci_username }}" password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars set_fact: aci_info_last_active_host_certificate: &aci_info_last_active_host_certificate - host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set the inventory with the last host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -1555,7 +1515,7 @@ - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_1 is changed - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1569,7 +1529,7 @@ - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_2 is changed - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1583,7 +1543,7 @@ - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_3 is changed - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1597,7 +1557,7 @@ - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_last_active_host_4 is changed - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1611,7 +1571,7 @@ - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_5 is changed - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1625,7 +1585,7 @@ - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_6 is changed - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1638,7 +1598,7 @@ - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_delete_tenant_last_active_host_7.current == [] - inventory_pwd_add_tenant_last_active_host_7 is changed - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" @@ -1653,7 +1613,7 @@ - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_8 is changed - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -1671,7 +1631,7 @@ password: "{{ aci_password }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set vars @@ -1681,7 +1641,7 @@ username: "{{ aci_username }}" validate_certs: '{{ validate_certs | default(false) }}' use_ssl: '{{ use_ssl | default(true) }}' - timeout: "{{ ansible_command_timeout }}" + timeout: 5 output_level: debug - name: Set the inventory with all the hosts in the inventory as inactive @@ -1690,7 +1650,7 @@ ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -1911,7 +1871,7 @@ - name: Delete Tenant on single active host present in the task with password cisco.aci.aci_tenant: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test state: absent register: task_pwd_delete_tenant_second_single_active_host_1 @@ -1925,7 +1885,7 @@ - name: Add Tenant on single active host present in the task with password cisco.aci.aci_tenant: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test state: present register: task_pwd_add_tenant_second_single_active_host_1 @@ -1941,7 +1901,7 @@ - name: Add a new AP on single active host present in the task password cisco.aci.aci_ap: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test ap: ap description: default ap @@ -2240,10 +2200,10 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2296,7 +2256,7 @@ ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2329,10 +2289,10 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2365,10 +2325,10 @@ - name: Set hosts 4 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2401,10 +2361,10 @@ - name: Set hosts 5 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2440,7 +2400,7 @@ ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2499,7 +2459,7 @@ - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - task_pwd_delete_tenant_second_active_host_1.current == [] - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_1 is changed - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2512,7 +2472,7 @@ - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_1 is changed - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2540,7 +2500,7 @@ - task_cert_delete_tenant_second_active_host_2 is changed - task_cert_delete_tenant_second_active_host_2.current == [] - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_2 is changed - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2554,7 +2514,7 @@ - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_2 is changed - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2582,7 +2542,7 @@ - inventory_pwd_delete_tenant_second_active_host_4_random3 is changed - inventory_pwd_delete_tenant_second_active_host_4_random3.current == [] - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_second_active_host_4_random3 is changed - inventory_pwd_add_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" - inventory_pwd_add_tenant_second_active_host_4_random3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2596,7 +2556,7 @@ - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_last_active_host_4_random4 is changed - inventory_pwd_add_tenant_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - inventory_pwd_add_tenant_last_active_host_4_random4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -2608,11 +2568,11 @@ - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -2907,7 +2867,7 @@ ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -3120,11 +3080,11 @@ - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -3413,11 +3373,11 @@ - name: Set the inventory with the second host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -3631,7 +3591,7 @@ that: - task_pwd_delete_tenant_second_active_host_1.current == [] - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_1 is changed - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3641,7 +3601,7 @@ - task_cert_delete_tenant_second_active_host_2 is changed - task_cert_delete_tenant_second_active_host_2.current == [] - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_2 is changed - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3651,7 +3611,7 @@ - task_pwd_delete_tenant_second_active_host_3 is changed - task_pwd_delete_tenant_second_active_host_3.current == [] - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_3 is changed - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3661,7 +3621,7 @@ - inventory_pwd_delete_tenant_second_active_host_4 is changed - inventory_pwd_delete_tenant_second_active_host_4.current == [] - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_second_active_host_4 is changed - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3671,7 +3631,7 @@ - task_pwd_delete_tenant_second_active_host_5 is changed - task_pwd_delete_tenant_second_active_host_5.current == [] - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_5 is changed - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3681,7 +3641,7 @@ - task_cert_delete_tenant_second_active_host_6 is changed - task_cert_delete_tenant_second_active_host_6.current == [] - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_6 is changed - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3690,7 +3650,7 @@ - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - inventory_pwd_delete_tenant_second_active_host_7 is changed - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_delete_tenant_second_active_host_7.current == [] - inventory_pwd_add_tenant_second_active_host_7 is changed - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" @@ -3701,7 +3661,7 @@ - task_cert_delete_tenant_second_active_host_8 is changed - task_cert_delete_tenant_second_active_host_8.current == [] - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_8 is changed - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3714,11 +3674,11 @@ - name: Set the inventory with the last host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -3936,7 +3896,7 @@ - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_1 is changed - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3950,7 +3910,7 @@ - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_2 is changed - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3964,7 +3924,7 @@ - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_3 is changed - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3978,7 +3938,7 @@ - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_last_active_host_4 is changed - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -3992,7 +3952,7 @@ - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_5 is changed - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4006,7 +3966,7 @@ - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_6 is changed - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4019,7 +3979,7 @@ - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_delete_tenant_last_active_host_7.current == [] - inventory_pwd_add_tenant_last_active_host_7 is changed - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" @@ -4034,7 +3994,7 @@ - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_8 is changed - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4051,7 +4011,7 @@ ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4272,7 +4232,7 @@ - name: Delete Tenant on single active host present in the task with password cisco.aci.aci_tenant: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test state: absent register: task_pwd_delete_tenant_second_single_active_host_1 @@ -4286,7 +4246,7 @@ - name: Add Tenant on single active host present in the task with password cisco.aci.aci_tenant: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test state: present register: task_pwd_add_tenant_second_single_active_host_1 @@ -4302,7 +4262,7 @@ - name: Add a new AP on single active host present in the task password cisco.aci.aci_ap: - <<: *aci_info_second_single_host_active + <<: *aci_info_single_host_active tenant: ansible_test ap: ap description: default ap @@ -4601,11 +4561,11 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4641,7 +4601,7 @@ ansible_host: 124.145.225.187 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4674,11 +4634,11 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4711,11 +4671,11 @@ - name: Set hosts 4 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.68,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4748,11 +4708,11 @@ - name: Set hosts 5 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.68 + ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4789,7 +4749,7 @@ ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4865,7 +4825,7 @@ - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - task_pwd_delete_tenant_second_active_host_1.current == [] - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_second_active_host_1 is changed - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4878,7 +4838,7 @@ - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_pwd_add_tenant_last_active_host_1 is changed - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4906,7 +4866,7 @@ - task_cert_delete_tenant_second_active_host_2 is changed - task_cert_delete_tenant_second_active_host_2.current == [] - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_second_active_host_2 is changed - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4920,7 +4880,7 @@ - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - task_cert_add_tenant_last_active_host_2 is changed - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4948,7 +4908,7 @@ - inventory_pwd_delete_tenant_second_active_host_4 is changed - inventory_pwd_delete_tenant_second_active_host_4.current == [] - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_second_active_host_4 is changed - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4962,7 +4922,7 @@ - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.68" + - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - inventory_pwd_add_tenant_last_active_host_4 is changed - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" @@ -4974,11 +4934,11 @@ - name: Set host 7 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -4996,11 +4956,11 @@ - name: Set host 8 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -5018,10 +4978,10 @@ - name: Set host 9 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.68 + ansible_host: 173.36.219.72 ansible_user: "{{ ansible_user }}" ansible_password: "incorrect_password" - ansible_command_timeout: "{{ ansible_command_timeout }}" + ansible_command_timeout: 5 ansible_connection: "{{ ansible_connection }}" ansible_network_os: "{{ ansible_network_os }}" ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" @@ -5053,41 +5013,9 @@ state: absent ignore_errors: yes - - name: Remove user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_second_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: absent - ignore_errors: yes - - - name: Remove user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_third_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: absent - ignore_errors: yes - - name: Delete Tenant cisco.aci.aci_tenant: <<: *aci_info_single_host_active tenant: ansible_test state: absent ignore_errors: yes - - - name: Delete Tenant - cisco.aci.aci_tenant: - <<: *aci_info_second_single_host_active - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Delete Tenant (Final Task in the Test) - cisco.aci.aci_tenant: - <<: *aci_info_third_single_host_active - tenant: ansible_test - state: absent - ignore_errors: yes diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index 7d2c9ed13..975c49a98 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -13,36 +13,36 @@ tags: httpapi_inventory_password when: inventory_hostname == play_hosts[6] -- name: Delete old log files to clean test directory - file: - path: "{{ item }}" - state: absent - with_items: - - "{{ aci_hostname }}_cm_add_tenant.json" - - "{{ aci_hostname }}_nm_add_tenant.json" - - "{{ aci_hostname }}_cm_add_tenant_again.json" - - "{{ aci_hostname }}_nm_add_tenant_again.json" - - "{{ aci_hostname }}_cm_add_tenant_descr.json" - - "{{ aci_hostname }}_nm_add_tenant_descr.json" - - "{{ aci_hostname }}_cm_add_tenant_descr_again.json" - - "{{ aci_hostname }}_nm_add_tenant_descr_again.json" - - "{{ aci_hostname }}_cm_remove_tenant.json" - - "{{ aci_hostname }}_nm_remove_tenant.json" - - "{{ aci_hostname }}_cm_remove_tenant_again.json" - - "{{ aci_hostname }}_nm_remove_tenant_again.json" - -# CLEAN ENVIRONMENT -- name: Remove tenant - cisco.aci.aci_tenant: &tenant_absent - host: '{{ aci_hostname }}' - username: '{{ aci_username }}' - password: '{{ aci_password }}' - validate_certs: '{{ aci_validate_certs | default(false) }}' - use_ssl: '{{ aci_use_ssl | default(true) }}' - use_proxy: '{{ aci_use_proxy | default(true) }}' - output_level: '{{ aci_output_level | default("info") }}' - tenant: ansible_test - state: absent +# - name: Delete old log files to clean test directory +# file: +# path: "{{ item }}" +# state: absent +# with_items: +# - "{{ aci_hostname }}_cm_add_tenant.json" +# - "{{ aci_hostname }}_nm_add_tenant.json" +# - "{{ aci_hostname }}_cm_add_tenant_again.json" +# - "{{ aci_hostname }}_nm_add_tenant_again.json" +# - "{{ aci_hostname }}_cm_add_tenant_descr.json" +# - "{{ aci_hostname }}_nm_add_tenant_descr.json" +# - "{{ aci_hostname }}_cm_add_tenant_descr_again.json" +# - "{{ aci_hostname }}_nm_add_tenant_descr_again.json" +# - "{{ aci_hostname }}_cm_remove_tenant.json" +# - "{{ aci_hostname }}_nm_remove_tenant.json" +# - "{{ aci_hostname }}_cm_remove_tenant_again.json" +# - "{{ aci_hostname }}_nm_remove_tenant_again.json" + +# # CLEAN ENVIRONMENT +# - name: Remove tenant +# cisco.aci.aci_tenant: &tenant_absent +# host: '{{ aci_hostname }}' +# username: '{{ aci_username }}' +# password: '{{ aci_password }}' +# validate_certs: '{{ aci_validate_certs | default(false) }}' +# use_ssl: '{{ aci_use_ssl | default(true) }}' +# use_proxy: '{{ aci_use_proxy | default(true) }}' +# output_level: '{{ aci_output_level | default("info") }}' +# tenant: ansible_test +# state: absent # ADD TENANT @@ -60,78 +60,87 @@ annotation: ansible_test owner_key: ansible_key owner_tag: ansible_tag -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_add_tenant.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant -- name: Dump content of files - debug: - msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" - -- name: Add tenant (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_present - output_path: "{{ aci_hostname }}_nm_add_tenant.json" - register: nm_add_tenant +# # ADD TENANT +# - name: Add tenant (check_mode) +# cisco.aci.aci_tenant: &tenant_present +# host: '{{ aci_hostname }}' +# username: '{{ aci_username }}' +# password: '{{ aci_password }}' +# validate_certs: '{{ aci_validate_certs | default(false) }}' +# use_ssl: '{{ aci_use_ssl | default(true) }}' +# use_proxy: '{{ aci_use_proxy | default(true) }}' +# output_level: '{{ aci_output_level | default("info") }}' +# tenant: ansible_test +# state: present +# annotation: ansible_test +# owner_key: ansible_key +# owner_tag: ansible_tag +# output_path: "{{ aci_hostname }}_cm_add_tenant.json" +# check_mode: yes +# register: cm_add_tenant + +# - name: Dump content of files +# debug: +# msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" + +# - name: Add tenant (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_present +# output_path: "{{ aci_hostname }}_nm_add_tenant.json" +# register: nm_add_tenant - name: Add tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_present -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_again.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_add_tenant_again.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_again -- name: Add tenant again (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_present - output_path: "{{ aci_hostname }}_nm_add_tenant_again.json" - register: nm_add_tenant_again - -- name: Dump content of files - debug: - msg: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json')}}" - - -- name: Store file content on variables for create object - set_fact: - fc_cm_add_tenant: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json') | from_json }}" - fc_nm_add_tenant: "{{ lookup('file', aci_hostname + '_nm_add_tenant.json') | from_json }}" - fc_cm_add_tenant_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_again.json') }}" - fc_nm_add_tenant_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_again.json') }}" - -- name: Log file content verification for create object - assert: - that: - - fc_cm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' - - fc_cm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' - - fc_nm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' - - fc_nm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' - - fc_cm_add_tenant_again == '' - - fc_nm_add_tenant_again == '' - -- name: Verify add_tenant - assert: - that: - - cm_add_tenant is changed - - nm_add_tenant is changed - - cm_add_tenant_again is not changed - - nm_add_tenant_again is not changed - - nm_add_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test' - - cm_add_tenant.proposed.fvTenant.attributes.annotation == 'ansible_test' - - nm_add_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key' - - cm_add_tenant.proposed.fvTenant.attributes.ownerKey == 'ansible_key' - - nm_add_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag' - - cm_add_tenant.proposed.fvTenant.attributes.ownerTag == 'ansible_tag' +# - name: Add tenant again (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_present +# output_path: "{{ aci_hostname }}_nm_add_tenant_again.json" +# register: nm_add_tenant_again + +# - name: Dump content of files +# debug: +# msg: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json')}}" + + +# - name: Store file content on variables for create object +# set_fact: +# fc_cm_add_tenant: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json') | from_json }}" +# fc_nm_add_tenant: "{{ lookup('file', aci_hostname + '_nm_add_tenant.json') | from_json }}" +# fc_cm_add_tenant_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_again.json') }}" +# fc_nm_add_tenant_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_again.json') }}" + +# - name: Log file content verification for create object +# assert: +# that: +# - fc_cm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' +# - fc_cm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' +# - fc_nm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' +# - fc_nm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' +# - fc_cm_add_tenant_again == '' +# - fc_nm_add_tenant_again == '' + +# - name: Verify add_tenant +# assert: +# that: +# - cm_add_tenant is changed +# - nm_add_tenant is changed +# - cm_add_tenant_again is not changed +# - nm_add_tenant_again is not changed +# - nm_add_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test' +# - cm_add_tenant.proposed.fvTenant.attributes.annotation == 'ansible_test' +# - nm_add_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key' +# - cm_add_tenant.proposed.fvTenant.attributes.ownerKey == 'ansible_key' +# - nm_add_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag' +# - cm_add_tenant.proposed.fvTenant.attributes.ownerTag == 'ansible_tag' # CHANGE TENANT @@ -142,24 +151,19 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_descr.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_add_tenant_descr.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_descr -- name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_present - description: Ansible test tenant - annotation: ansible_test_changed - owner_key: ansible_key_changed - owner_tag: ansible_tag_changed - output_path: "{{ aci_hostname }}_nm_add_tenant_descr.json" - register: nm_add_tenant_descr +# - name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_present +# description: Ansible test tenant +# annotation: ansible_test_changed +# owner_key: ansible_key_changed +# owner_tag: ansible_tag_changed +# output_path: "{{ aci_hostname }}_nm_add_tenant_descr.json" +# register: nm_add_tenant_descr - name: Change description and annotation/owner_tag/owner_key of tenant again (check_mode) cisco.aci.aci_tenant: @@ -168,59 +172,54 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_add_tenant_descr_again.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_add_tenant_descr_again.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_add_tenant_descr_again -- name: Change description and annotation of tenant again (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_present - description: Ansible test tenant - annotation: ansible_test_changed - owner_key: ansible_key_changed - owner_tag: ansible_tag_changed - output_path: "{{ aci_hostname }}_nm_add_tenant_descr_again.json" - register: nm_add_tenant_descr_again - -- name: Store file content on variables for update object - set_fact: - fc_cm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr.json') | from_json }}" - fc_nm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr.json') | from_json }}" - fc_cm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr_again.json') }}" - fc_nm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr_again.json') }}" - -- name: Log file content verification for update object - assert: - that: - - fc_cm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' - - fc_nm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' - - fc_cm_add_tenant_descr_again == '' - - fc_nm_add_tenant_descr_again == '' - -- name: Verify add_tenant_descr - assert: - that: - - cm_add_tenant_descr is changed - - nm_add_tenant_descr is changed - - cm_add_tenant_descr_again is not changed - - nm_add_tenant_descr_again is not changed - - cm_add_tenant_descr.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' - - nm_add_tenant_descr.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' - - cm_add_tenant_descr_again.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' - - nm_add_tenant_descr_again.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' - - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' - - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' - - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' - - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' - - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' +# - name: Change description and annotation of tenant again (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_present +# description: Ansible test tenant +# annotation: ansible_test_changed +# owner_key: ansible_key_changed +# owner_tag: ansible_tag_changed +# output_path: "{{ aci_hostname }}_nm_add_tenant_descr_again.json" +# register: nm_add_tenant_descr_again + +# - name: Store file content on variables for update object +# set_fact: +# fc_cm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr.json') | from_json }}" +# fc_nm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr.json') | from_json }}" +# fc_cm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr_again.json') }}" +# fc_nm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr_again.json') }}" + +# - name: Log file content verification for update object +# assert: +# that: +# - fc_cm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' +# - fc_nm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' +# - fc_cm_add_tenant_descr_again == '' +# - fc_nm_add_tenant_descr_again == '' + +# - name: Verify add_tenant_descr +# assert: +# that: +# - cm_add_tenant_descr is changed +# - nm_add_tenant_descr is changed +# - cm_add_tenant_descr_again is not changed +# - nm_add_tenant_descr_again is not changed +# - cm_add_tenant_descr.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' +# - nm_add_tenant_descr.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' +# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' +# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' +# - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' +# - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' +# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' +# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' # ADD TENANT AGAIN @@ -233,19 +232,19 @@ check_mode: true register: cm_add_tenant_again_no_descr -- name: Add tenant again with no description (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_present - annotation: ansible_test_changed - owner_key: ansible_key_changed - owner_tag: ansible_tag_changed - register: nm_add_tenant_again_no_descr +# - name: Add tenant again with no description (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_present +# annotation: ansible_test_changed +# owner_key: ansible_key_changed +# owner_tag: ansible_tag_changed +# register: nm_add_tenant_again_no_descr -- name: Verify add_tenant_again_no_descr - assert: - that: - - cm_add_tenant_again_no_descr is not changed - - nm_add_tenant_again_no_descr is not changed +# - name: Verify add_tenant_again_no_descr +# assert: +# that: +# - cm_add_tenant_again_no_descr is not changed +# - nm_add_tenant_again_no_descr is not changed # QUERY ALL TENANTS @@ -262,17 +261,17 @@ check_mode: true register: cm_query_all_tenants -- name: Query all tenants (normal mode) - cisco.aci.aci_tenant: *tenant_query - register: nm_query_all_tenants +# - name: Query all tenants (normal mode) +# cisco.aci.aci_tenant: *tenant_query +# register: nm_query_all_tenants -- name: Verify query_all_tenants - assert: - that: - - cm_query_all_tenants is not changed - - nm_query_all_tenants is not changed - # NOTE: Order of tenants is not stable between calls - #- cm_query_all_tenants == nm_query_all_tenants +# - name: Verify query_all_tenants +# assert: +# that: +# - cm_query_all_tenants is not changed +# - nm_query_all_tenants is not changed +# # NOTE: Order of tenants is not stable between calls +# #- cm_query_all_tenants == nm_query_all_tenants # QUERY A TENANT @@ -283,98 +282,88 @@ check_mode: true register: cm_query_tenant -- name: Query our tenant - cisco.aci.aci_tenant: - <<: *tenant_query - tenant: ansible_test - register: nm_query_tenant - -- name: Verify query_tenant - assert: - that: - - cm_query_tenant is not changed - - nm_query_tenant is not changed - - cm_query_tenant == nm_query_tenant - - cm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' - - nm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' - - nm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' - - cm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' - - nm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - cm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - -- name: Update tenant with default annotation - cisco.aci.aci_tenant: - <<: *tenant_present - annotation: "{{ fake_var | default(omit) }}" - register: default_annotation - -- name: Assertion check for update tenant with default annotation - assert: - that: - - default_annotation is changed - - default_annotation.current.0.fvTenant.attributes.annotation == 'orchestrator:ansible' +# - name: Query our tenant +# cisco.aci.aci_tenant: +# <<: *tenant_query +# tenant: ansible_test +# register: nm_query_tenant + +# - name: Verify query_tenant +# assert: +# that: +# - cm_query_tenant is not changed +# - nm_query_tenant is not changed +# - cm_query_tenant == nm_query_tenant +# - cm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' +# - nm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' +# - nm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - cm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' +# - nm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' +# - cm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + + +# - name: Update tenant with default annotation +# cisco.aci.aci_tenant: +# <<: *tenant_present +# annotation: "{{ fake_var | default(omit) }}" +# register: default_annotation + +# - name: Assertion check for update tenant with default annotation +# assert: +# that: +# - default_annotation is changed +# - default_annotation.current.0.fvTenant.attributes.annotation == 'orchestrator:ansible' # REMOVE TENANT - name: Remove tenant (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_remove_tenant.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_remove_tenant.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_remove_tenant -- name: Remove tenant (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_absent - output_path: "{{ aci_hostname }}_nm_remove_tenant.json" - register: nm_remove_tenant +# - name: Remove tenant (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_absent +# output_path: "{{ aci_hostname }}_nm_remove_tenant.json" +# register: nm_remove_tenant - name: Remove tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent -<<<<<<< HEAD output_path: "{{ ansible_host }}_cm_remove_tenant_again.json" check_mode: true -======= - output_path: "{{ aci_hostname }}_cm_remove_tenant_again.json" - check_mode: yes ->>>>>>> 29a664c ([minor_change] Removed different functions to make requests which can now be done by using just one function) register: cm_remove_tenant_again -- name: Remove tenant again (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_absent - output_path: "{{ aci_hostname }}_nm_remove_tenant_again.json" - register: nm_remove_tenant_again - -- name: Store file content on variables for delete object - set_fact: - fc_cm_remove_tenant: "{{ lookup('file', aci_hostname + '_cm_remove_tenant.json') | from_json }}" - fc_nm_remove_tenant: "{{ lookup('file', aci_hostname + '_nm_remove_tenant.json') | from_json }}" - fc_cm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_cm_remove_tenant_again.json') }}" - fc_nm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_nm_remove_tenant_again.json') }}" - -- name: Log file content verification for delete object - assert: - that: - - fc_cm_remove_tenant.0.fvTenant.attributes.status == 'deleted' - - fc_cm_remove_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' - - fc_nm_remove_tenant == [{}] - - fc_cm_remove_tenant_again == '' - - fc_nm_remove_tenant_again == '' - -- name: Verify remove_tenant - assert: - that: - - cm_remove_tenant is changed - - nm_remove_tenant is changed - - cm_remove_tenant_again is not changed - - nm_remove_tenant_again is not changed +# - name: Remove tenant again (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_absent +# output_path: "{{ aci_hostname }}_nm_remove_tenant_again.json" +# register: nm_remove_tenant_again + +# - name: Store file content on variables for delete object +# set_fact: +# fc_cm_remove_tenant: "{{ lookup('file', aci_hostname + '_cm_remove_tenant.json') | from_json }}" +# fc_nm_remove_tenant: "{{ lookup('file', aci_hostname + '_nm_remove_tenant.json') | from_json }}" +# fc_cm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_cm_remove_tenant_again.json') }}" +# fc_nm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_nm_remove_tenant_again.json') }}" + +# - name: Log file content verification for delete object +# assert: +# that: +# - fc_cm_remove_tenant.0.fvTenant.attributes.status == 'deleted' +# - fc_cm_remove_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' +# - fc_nm_remove_tenant == [{}] +# - fc_cm_remove_tenant_again == '' +# - fc_nm_remove_tenant_again == '' + +# - name: Verify remove_tenant +# assert: +# that: +# - cm_remove_tenant is changed +# - nm_remove_tenant is changed +# - cm_remove_tenant_again is not changed +# - nm_remove_tenant_again is not changed # QUERY NON-EXISTING TENANT @@ -385,16 +374,16 @@ check_mode: true register: cm_query_non_tenant -- name: Query non-existing tenant (normal mode) - cisco.aci.aci_tenant: - <<: *tenant_query - tenant: ansible_test - register: nm_query_non_tenant - -# TODO: Implement more tests -- name: Verify query_non_tenant - assert: - that: - - cm_query_non_tenant is not changed - - nm_query_non_tenant is not changed - - cm_query_non_tenant == nm_query_non_tenant +# - name: Query non-existing tenant (normal mode) +# cisco.aci.aci_tenant: +# <<: *tenant_query +# tenant: ansible_test +# register: nm_query_non_tenant + +# # TODO: Implement more tests +# - name: Verify query_non_tenant +# assert: +# that: +# - cm_query_non_tenant is not changed +# - nm_query_non_tenant is not changed +# - cm_query_non_tenant == nm_query_non_tenant From 04c0cda99ffd500b655685fb17a163f64327e43a Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 24 Apr 2023 09:51:13 -0400 Subject: [PATCH 33/49] [ignore_changes] Merged handle_http_error with send_request --- plugins/httpapi/aci.py | 134 ++++++++++-------- plugins/module_utils/aci.py | 22 ++- .../aci_tenant/filter_plugins/generate_ips.py | 21 +++ .../tasks/httpapi_inventory_password.yml | 22 +-- 4 files changed, 123 insertions(+), 76 deletions(-) create mode 100644 tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 2315dd8d5..5527f62b0 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -40,7 +40,7 @@ from ansible.module_utils._text import to_text, to_native from ansible.module_utils.connection import ConnectionError from ansible.plugins.httpapi import HttpApiBase -from copy import copy +from copy import copy, deepcopy # Optional, only used for APIC signature-based authentication try: @@ -67,24 +67,17 @@ class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) - self.auth = None self.params = None self.result = {} - self.check_authentication = "" self.backup_hosts = None - self.inventory_hosts = [] - self.host_counter = 0 self.connection_error_check = False self.connection_parameters = {} - self.begin_task = False - self.executed_exit_function = False self.current_host = None self.provided_hosts = None - self.get_first_item = [] + self.inventory_hosts = None - def set_params(self, auth, params): + def set_params(self, params): self.params = params - self.auth = auth # Login function is executed until connection to a host is established or until all the hosts in the list are exhausted def login(self, username, password): @@ -95,6 +88,7 @@ def login(self, username, password): path = "/api/aaaLogin.json" payload = {"aaaUser": {"attributes": {"name": username, "pwd": password}}} data = json.dumps(payload) + self.connection._connected = True try: response, response_data = self.connection.send(path, data, method=method) response_value = self._get_response_value(response_data) @@ -102,9 +96,9 @@ def login(self, username, password): "Cookie": "APIC-Cookie={0}".format(self._response_to_json(response_value).get("imdata")[0]["aaaLogin"]["attributes"]["token"]) } self.connection.queue_message("step:", "Connection to {0} was successful".format(self.connection.get_option("host"))) - except Exception as exc_response: - self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) - self.handle_connection_error(exc_response) + except Exception: + self.connection._connected = False + raise def logout(self): method = "POST" @@ -132,81 +126,101 @@ def set_parameters(self): self.connection._connected = False self.connection.queue_message("step", "Re-setting connection due to change in {0}".format(key)) - # if self.auth is not None or self.connection.get_option("session_key") is not None: - # self.connection._connected = True + if self.params.get("private_key") is not None: + self.connection.set_option("session_key", None) + connection_parameters["certificate_name"] = self.params.get("certificate_name") + connection_parameters["private_key"] = self.params.get("private_key") + elif self.connection.get_option("session_key") is not None and self.params.get("password") is None: + connection_parameters["certificate_name"] = list(self.connection.get_option("session_key").keys())[0] + connection_parameters["private_key"] = list(self.connection.get_option("session_key").values())[0] + else: + if self.connection_parameters.get("private_key") is not None: + self.connection._connected = False + self.connection.set_option("session_key", None) + connection_parameters["private_key"] = None + connection_parameters["certificate_name"] = None if self.connection_parameters != connection_parameters: self.connection_parameters = copy(connection_parameters) + self.set_hosts() + def set_hosts(self): if self.params.get("host") is not None: get_hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") else: - self.get_first_item.append(re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",")) - get_hosts = self.get_first_item[0] + if self.inventory_hosts is None: + self.inventory_hosts = re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",") + get_hosts = self.inventory_hosts if self.provided_hosts is None: - self.provided_hosts = get_hosts + self.provided_hosts = deepcopy(get_hosts) self.connection.queue_message( "step:", "Provided Hosts: {0}".format(self.provided_hosts) ) - self.backup_hosts = self.provided_hosts + self.backup_hosts = deepcopy(get_hosts) self.current_host = self.backup_hosts.pop(0) - elif (len(self.backup_hosts) != 0 and self.current_host not in get_hosts) or self.connection_error_check == True: - self.connection_error_check = False - self.connection._connected = False + elif self.provided_hosts != get_hosts: + self.provided_hosts = deepcopy(get_hosts) self.connection.queue_message( - "step:", "Provided hosts have changed: {0}".format(get_hosts) + "step:", "Provided Hosts have changed: {0}".format(self.provided_hosts) ) - self.backup_hosts = get_hosts - self.current_host = self.backup_hosts.pop(0) + self.backup_hosts = deepcopy(get_hosts) + try: + index = self.backup_hosts.index(self.current_host) + self.backup_hosts.pop(index) + self.connection.queue_message( + "step:", "Continuing the operations on the connected host: {0}".format(self.current_host) + ) + except: + self.current_host = self.backup_hosts.pop(0) self.connection.set_option("host", self.current_host) # One API call is made via each call to send_request from aci.py in module_utils - # As long as a host is active in the list we make sure that the API call goes through + # As long as a host is active in the list the API call will go through def send_request(self, method, path, data): """This method handles all APIC REST API requests other than login""" self.set_parameters() - self.set_hosts() + + if self.connection_parameters.get("private_key") is not None: + try: + self.connection._auth = {"Cookie": "{0}".format(self.cert_auth(method, path, data).get("Cookie"))} + self.connection._connected = True + except Exception as exc_response: + self.connection._connected = False + return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), str(exc_response)) try: + if self.connection._connected is False: + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message( "step:", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) ) except Exception as exc_response: + self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) if len(self.backup_hosts) == 0: - return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), str(exc_response)) - self.connection.queue_message("step:", "Connection to {0} has failed in between operations with: {1}".format(self.connection.get_option("host"), exc_response)) - self.handle_connection_error(exc_response) - self.connection.queue_message("step:", "Retrying request on {0}".format(self.connection.get_option("host"))) + self.connection._connected = False + msg = str("No hosts left in the cluster to continue operation! Error on final host {0}: {1}".format(self.connection.get_option("host"), exc_response)) + return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), msg) + else: + self.current_host = self.backup_hosts.pop(0) + self.connection.queue_message( + "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) + ) + self.connection.set_option("host", self.current_host) # recurse through function for retrying the request return self.send_request(method, path, data) # return statement executed upon each successful response from the request function return self._verify_response(response, method, path, response_data) - # Custom error handler - def handle_connection_error(self, exception): - if len(self.backup_hosts) == 0: - self.connection_error_check = True - raise ConnectionError( - "No hosts left in cluster to continue operation!!! Error on final host {0}: {1}".format(self.connection.get_option("host"), exception) - ) - self.current_host = self.backup_hosts.pop(0) - self.connection.queue_message( - "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) - ) - self.connection.set_option("host", self.current_host) - # Login function is called until connection to a host is established or until all the hosts in the list are exhausted - self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - # Built-in-function def handle_httperror(self, exc): self.connection.queue_message("step:", "Failed to receive response from {0}: {1}".format(self.connection.get_option("host"), exc)) if exc.code == 401: return False - elif exc.code == 403: + elif exc.code == 403 and self.connection_parameters.get("private_key") is None: self.connection._auth = None self.login(self.connection.get_option('remote_user'), self.connection.get_option('password')) return True @@ -251,52 +265,56 @@ def _return_info(self, response_code, method, path, msg, respond_data=None): info["body"] = respond_data return info - def cert_auth(self, path, method, payload=""): + def cert_auth(self, method, path, payload=""): """Perform APIC signature-based authentication, not the expected SSL client certificate authentication.""" - headers = dict() - if payload is None: payload = "" + headers = dict() + try: if HAS_CRYPTOGRAPHY: - key = list(self.connection.get_option("session_key").values())[0].encode() + key = self.connection_parameters.get("private_key").encode() sig_key = serialization.load_pem_private_key( key, password=None, backend=default_backend(), ) else: - sig_key = load_privatekey(FILETYPE_PEM, list(self.connection.get_option("session_key").values())[0]) + sig_key = load_privatekey(FILETYPE_PEM, self.connection_parameters.get("private_key")) except Exception: - if os.path.exists(list(self.connection.get_option("session_key").values())[0]): + if os.path.exists(self.connection_parameters.get("private_key")): try: permission = "r" if HAS_CRYPTOGRAPHY: permission = "rb" - with open(list(self.connection.get_option("session_key").values())[0], permission) as fh: + with open(self.connection_parameters.get("private_key"), permission) as fh: private_key_content = fh.read() except Exception: - raise ConnectionError("Cannot open private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + raise ConnectionError("Cannot open private key file {0}".format(self.connection_parameters.get("private_key"))) try: if HAS_CRYPTOGRAPHY: sig_key = serialization.load_pem_private_key(private_key_content, password=None, backend=default_backend()) else: sig_key = load_privatekey(FILETYPE_PEM, private_key_content) except Exception: - raise ConnectionError("Cannot load private key file {0}".format(list(self.connection.get_option("session_key").values())[0])) + raise ConnectionError("Cannot load private key file {0}".format(self.connection_parameters.get("private_key"))) + if self.connection_parameters.get("certificate_name") is None: + self.connection_parameters["certificate_name"] = os.path.basename(os.path.splitext(self.connection_parameters.get("private_key"))[0]) else: raise ConnectionError( - "Provided private key {0} does not appear to be a private key.".format(list(self.connection.get_option("session_key").values())[0]) + "Provided private key {0} does not appear to be a private key.".format(self.connection_parameters.get("private_key")) ) + if self.connection_parameters.get("certificate_name") is None: + self.connection_parameters["certificate_name"] = self.connection.get_option("remote_user") sig_request = method + path + payload if HAS_CRYPTOGRAPHY: sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) else: sig_signature = sign(sig_key, sig_request, "sha256") sig_dn = "uni/userext/user-{0}/usercert-{1}".format( - self.connection.get_option("remote_user"), list(self.connection.get_option("session_key").keys())[0] + self.connection.get_option("remote_user"), self.connection_parameters.get("certificate_name") ) headers["Cookie"] = ( "APIC-Certificate-Algorithm=v1.0; " diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 9c46399c9..aace7ce9f 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -331,6 +331,7 @@ def __init__(self, module): self.response = None self.status = None self.url = None + self.connection = None self.httpapi_logs = list() # aci_rest output @@ -351,7 +352,7 @@ def __init__(self, module): msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") elif self.params.get("password") is not None: self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") - elif self.module._socket_path is None: + elif self.get_connection() is None: if self.params.get("password"): # Perform password-based authentication, log on using password self.login() @@ -398,6 +399,15 @@ def define_method(self): state_map = dict(absent="delete", present="post", query="get") self.params["method"] = state_map.get(self.params.get("state")) + def get_connection(self): + if self.connection: + return self.connection + + if self.module._socket_path: + self.connection = Connection(self.module._socket_path) + + return self.connection + def login(self): """Log in to APIC""" @@ -1602,15 +1612,13 @@ def api_call(self, method): def api_call(self, method, path, url, data=None, output=False): >>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) resp = None - info = {} if self.params.get("private_key"): self.cert_auth(path=path, payload=data, method=method) - if self.module._socket_path: - connect = Connection(self.module._socket_path) - connect.set_params(self.headers.get("Cookie"), self.params) - info = connect.send_request(method, "/{0}".format(path), data) + if self.get_connection() is not None: + self.get_connection().set_params(self.params) + info = self.get_connection().send_request(method, "/{0}".format(path), data) self.url = info.get("url") - self.httpapi_logs.extend(connect.pop_messages()) + self.httpapi_logs.extend(self.get_connection().pop_messages()) else: resp, info = fetch_url( self.module, diff --git a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py new file mode 100644 index 000000000..a85f3c696 --- /dev/null +++ b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py @@ -0,0 +1,21 @@ +#!/usr/bin/python +from ipaddress import ip_network +import random + +RANGE_IPV4 = list(ip_network("192.0.2.0/24").hosts()) + list(ip_network("198.51.100.0/24").hosts()) + list(ip_network("203.0.113.0/24").hosts()) + +class FilterModule(object): + def filters(self): + return { + 'generate_random_ips': self.generate_random_ips, + } + + def generate_random_ips(self, given_ip, insert_given_ip_at, number_of_ips): + ips = "" + for i in range(number_of_ips): + if i == insert_given_ip_at - 1: + ips += given_ip + else: + ips += str((random.choice(RANGE_IPV4))) + ips += "," + return ips.rstrip(',') \ No newline at end of file diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml index 42b9b04b0..33fa9e1ab 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml @@ -108,7 +108,7 @@ - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{aci_hostname|generate_random_ips(5,5)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: 5 @@ -660,7 +660,7 @@ - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: 5 @@ -2200,7 +2200,7 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: 5 @@ -2289,7 +2289,7 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_command_timeout: 5 @@ -2568,7 +2568,7 @@ - name: Set Single active host in inventory ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -3080,7 +3080,7 @@ - name: Set the inventory with the first host in the list as active ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4561,7 +4561,7 @@ - name: Set hosts 1 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4634,7 +4634,7 @@ - name: Set hosts 3 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 + ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} @@ -4934,7 +4934,7 @@ - name: Set host 7 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} @@ -4956,7 +4956,7 @@ - name: Set host 8 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "{{ ansible_password }}" ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} @@ -4978,7 +4978,7 @@ - name: Set host 9 ansible.builtin.add_host: name: inventory_hosts - ansible_host: 173.36.219.72 + ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" ansible_user: "{{ ansible_user }}" ansible_password: "incorrect_password" ansible_command_timeout: 5 From 34846e57dfb8931d4a73bceaad56ac3960e0f31c Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 24 Apr 2023 10:24:14 -0400 Subject: [PATCH 34/49] [ignore_changes] Removed rebase comments --- plugins/doc_fragments/aci.py | 3 - plugins/modules/aci_rest.py | 8 - .../targets/aci_rest/tasks/error_handling.yml | 5 - .../targets/aci_tenant/tasks/main.yml | 506 +++++++++--------- 4 files changed, 252 insertions(+), 270 deletions(-) diff --git a/plugins/doc_fragments/aci.py b/plugins/doc_fragments/aci.py index 33d37904b..70eaa24dc 100644 --- a/plugins/doc_fragments/aci.py +++ b/plugins/doc_fragments/aci.py @@ -18,10 +18,7 @@ class ModuleDocFragment(object): - IP Address or hostname of APIC resolvable by Ansible control host. - If the value is not specified in the task, the value of environment variable C(ACI_HOST) will be used instead. type: str -<<<<<<< HEAD required: true -======= ->>>>>>> 82a0f1c ([ignore] Addition of code to include High Availability at task level) aliases: [ hostname ] port: description: diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 2722e63ec..fd8b10943 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -389,19 +389,11 @@ def main(): elif rest_type == "xml" and HAS_LXML_ETREE: if content and isinstance(content, dict) and HAS_XMLJSON_COBRA: # Validate inline YAML/JSON -<<<<<<< HEAD payload = etree.tostring(cobra.etree(payload)[0], encoding="unicode") elif payload and isinstance(payload, str): try: # Validate XML string payload = etree.tostring(etree.fromstring(payload), encoding="unicode") -======= - payload = etree.tostring(cobra.etree(payload)[0], encoding='unicode') - elif payload and isinstance(payload, str): - try: - # Validate XML string - payload = etree.tostring(etree.fromstring(payload), encoding='unicode') ->>>>>>> 1620e2b (Added plugin option to aci_rest) except Exception as e: module.fail_json(msg="Failed to parse provided XML payload: %s" % to_text(e), payload=payload) diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index f748f713f..1c503c5f2 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -20,12 +20,7 @@ fvTenant: attributes: name: ansible_test -<<<<<<< HEAD ignore_errors: true -======= - ignore_errors: yes - delegate_to: localhost ->>>>>>> 1620e2b (Added plugin option to aci_rest) register: error_on_name_resolution - name: Verify error_on_name_resolution diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index 975c49a98..2c8fc0bcb 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -13,37 +13,36 @@ tags: httpapi_inventory_password when: inventory_hostname == play_hosts[6] -# - name: Delete old log files to clean test directory -# file: -# path: "{{ item }}" -# state: absent -# with_items: -# - "{{ aci_hostname }}_cm_add_tenant.json" -# - "{{ aci_hostname }}_nm_add_tenant.json" -# - "{{ aci_hostname }}_cm_add_tenant_again.json" -# - "{{ aci_hostname }}_nm_add_tenant_again.json" -# - "{{ aci_hostname }}_cm_add_tenant_descr.json" -# - "{{ aci_hostname }}_nm_add_tenant_descr.json" -# - "{{ aci_hostname }}_cm_add_tenant_descr_again.json" -# - "{{ aci_hostname }}_nm_add_tenant_descr_again.json" -# - "{{ aci_hostname }}_cm_remove_tenant.json" -# - "{{ aci_hostname }}_nm_remove_tenant.json" -# - "{{ aci_hostname }}_cm_remove_tenant_again.json" -# - "{{ aci_hostname }}_nm_remove_tenant_again.json" - -# # CLEAN ENVIRONMENT -# - name: Remove tenant -# cisco.aci.aci_tenant: &tenant_absent -# host: '{{ aci_hostname }}' -# username: '{{ aci_username }}' -# password: '{{ aci_password }}' -# validate_certs: '{{ aci_validate_certs | default(false) }}' -# use_ssl: '{{ aci_use_ssl | default(true) }}' -# use_proxy: '{{ aci_use_proxy | default(true) }}' -# output_level: '{{ aci_output_level | default("info") }}' -# tenant: ansible_test -# state: absent - +- name: Delete old log files to clean test directory + file: + path: "{{ item }}" + state: absent + with_items: + - "{{ aci_hostname }}_cm_add_tenant.json" + - "{{ aci_hostname }}_nm_add_tenant.json" + - "{{ aci_hostname }}_cm_add_tenant_again.json" + - "{{ aci_hostname }}_nm_add_tenant_again.json" + - "{{ aci_hostname }}_cm_add_tenant_descr.json" + - "{{ aci_hostname }}_nm_add_tenant_descr.json" + - "{{ aci_hostname }}_cm_add_tenant_descr_again.json" + - "{{ aci_hostname }}_nm_add_tenant_descr_again.json" + - "{{ aci_hostname }}_cm_remove_tenant.json" + - "{{ aci_hostname }}_nm_remove_tenant.json" + - "{{ aci_hostname }}_cm_remove_tenant_again.json" + - "{{ aci_hostname }}_nm_remove_tenant_again.json" + +# CLEAN ENVIRONMENT +- name: Remove tenant + cisco.aci.aci_tenant: &tenant_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: absent # ADD TENANT - name: Add tenant (check_mode) @@ -64,34 +63,34 @@ check_mode: true register: cm_add_tenant -# # ADD TENANT -# - name: Add tenant (check_mode) -# cisco.aci.aci_tenant: &tenant_present -# host: '{{ aci_hostname }}' -# username: '{{ aci_username }}' -# password: '{{ aci_password }}' -# validate_certs: '{{ aci_validate_certs | default(false) }}' -# use_ssl: '{{ aci_use_ssl | default(true) }}' -# use_proxy: '{{ aci_use_proxy | default(true) }}' -# output_level: '{{ aci_output_level | default("info") }}' -# tenant: ansible_test -# state: present -# annotation: ansible_test -# owner_key: ansible_key -# owner_tag: ansible_tag -# output_path: "{{ aci_hostname }}_cm_add_tenant.json" -# check_mode: yes -# register: cm_add_tenant - -# - name: Dump content of files -# debug: -# msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" - -# - name: Add tenant (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_present -# output_path: "{{ aci_hostname }}_nm_add_tenant.json" -# register: nm_add_tenant +# ADD TENANT +- name: Add tenant (check_mode) + cisco.aci.aci_tenant: &tenant_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + tenant: ansible_test + state: present + annotation: ansible_test + owner_key: ansible_key + owner_tag: ansible_tag + output_path: "{{ aci_hostname }}_cm_add_tenant.json" + check_mode: yes + register: cm_add_tenant + +- name: Dump content of files + debug: + msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" + +- name: Add tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + output_path: "{{ aci_hostname }}_nm_add_tenant.json" + register: nm_add_tenant - name: Add tenant again (check_mode) cisco.aci.aci_tenant: @@ -100,48 +99,47 @@ check_mode: true register: cm_add_tenant_again -# - name: Add tenant again (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_present -# output_path: "{{ aci_hostname }}_nm_add_tenant_again.json" -# register: nm_add_tenant_again - -# - name: Dump content of files -# debug: -# msg: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json')}}" - - -# - name: Store file content on variables for create object -# set_fact: -# fc_cm_add_tenant: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json') | from_json }}" -# fc_nm_add_tenant: "{{ lookup('file', aci_hostname + '_nm_add_tenant.json') | from_json }}" -# fc_cm_add_tenant_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_again.json') }}" -# fc_nm_add_tenant_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_again.json') }}" - -# - name: Log file content verification for create object -# assert: -# that: -# - fc_cm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' -# - fc_cm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' -# - fc_nm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' -# - fc_nm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' -# - fc_cm_add_tenant_again == '' -# - fc_nm_add_tenant_again == '' - -# - name: Verify add_tenant -# assert: -# that: -# - cm_add_tenant is changed -# - nm_add_tenant is changed -# - cm_add_tenant_again is not changed -# - nm_add_tenant_again is not changed -# - nm_add_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test' -# - cm_add_tenant.proposed.fvTenant.attributes.annotation == 'ansible_test' -# - nm_add_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key' -# - cm_add_tenant.proposed.fvTenant.attributes.ownerKey == 'ansible_key' -# - nm_add_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag' -# - cm_add_tenant.proposed.fvTenant.attributes.ownerTag == 'ansible_tag' - +- name: Add tenant again (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + output_path: "{{ aci_hostname }}_nm_add_tenant_again.json" + register: nm_add_tenant_again + +- name: Dump content of files + debug: + msg: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json')}}" + + +- name: Store file content on variables for create object + set_fact: + fc_cm_add_tenant: "{{ lookup('file', aci_hostname + '_cm_add_tenant.json') | from_json }}" + fc_nm_add_tenant: "{{ lookup('file', aci_hostname + '_nm_add_tenant.json') | from_json }}" + fc_cm_add_tenant_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_again.json') }}" + fc_nm_add_tenant_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_again.json') }}" + +- name: Log file content verification for create object + assert: + that: + - fc_cm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' + - fc_cm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' + - fc_nm_add_tenant.0.fvTenant.attributes.name == 'ansible_test' + - fc_nm_add_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' + - fc_cm_add_tenant_again == '' + - fc_nm_add_tenant_again == '' + +- name: Verify add_tenant + assert: + that: + - cm_add_tenant is changed + - nm_add_tenant is changed + - cm_add_tenant_again is not changed + - nm_add_tenant_again is not changed + - nm_add_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test' + - cm_add_tenant.proposed.fvTenant.attributes.annotation == 'ansible_test' + - nm_add_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key' + - cm_add_tenant.proposed.fvTenant.attributes.ownerKey == 'ansible_key' + - nm_add_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag' + - cm_add_tenant.proposed.fvTenant.attributes.ownerTag == 'ansible_tag' # CHANGE TENANT - name: Change description and annotation/owner_tag/owner_key of tenant (check_mode) @@ -155,15 +153,15 @@ check_mode: true register: cm_add_tenant_descr -# - name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_present -# description: Ansible test tenant -# annotation: ansible_test_changed -# owner_key: ansible_key_changed -# owner_tag: ansible_tag_changed -# output_path: "{{ aci_hostname }}_nm_add_tenant_descr.json" -# register: nm_add_tenant_descr +- name: Change description and annotation/owner_tag/owner_key of tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + output_path: "{{ aci_hostname }}_nm_add_tenant_descr.json" + register: nm_add_tenant_descr - name: Change description and annotation/owner_tag/owner_key of tenant again (check_mode) cisco.aci.aci_tenant: @@ -176,50 +174,50 @@ check_mode: true register: cm_add_tenant_descr_again -# - name: Change description and annotation of tenant again (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_present -# description: Ansible test tenant -# annotation: ansible_test_changed -# owner_key: ansible_key_changed -# owner_tag: ansible_tag_changed -# output_path: "{{ aci_hostname }}_nm_add_tenant_descr_again.json" -# register: nm_add_tenant_descr_again - -# - name: Store file content on variables for update object -# set_fact: -# fc_cm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr.json') | from_json }}" -# fc_nm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr.json') | from_json }}" -# fc_cm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr_again.json') }}" -# fc_nm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr_again.json') }}" - -# - name: Log file content verification for update object -# assert: -# that: -# - fc_cm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' -# - fc_nm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' -# - fc_cm_add_tenant_descr_again == '' -# - fc_nm_add_tenant_descr_again == '' - -# - name: Verify add_tenant_descr -# assert: -# that: -# - cm_add_tenant_descr is changed -# - nm_add_tenant_descr is changed -# - cm_add_tenant_descr_again is not changed -# - nm_add_tenant_descr_again is not changed -# - cm_add_tenant_descr.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' -# - nm_add_tenant_descr.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' -# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' -# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' -# - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' -# - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' -# - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' -# - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' +- name: Change description and annotation of tenant again (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + description: Ansible test tenant + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + output_path: "{{ aci_hostname }}_nm_add_tenant_descr_again.json" + register: nm_add_tenant_descr_again + +- name: Store file content on variables for update object + set_fact: + fc_cm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr.json') | from_json }}" + fc_nm_add_tenant_descr: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr.json') | from_json }}" + fc_cm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_cm_add_tenant_descr_again.json') }}" + fc_nm_add_tenant_descr_again: "{{ lookup('file', aci_hostname + '_nm_add_tenant_descr_again.json') }}" + +- name: Log file content verification for update object + assert: + that: + - fc_cm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' + - fc_nm_add_tenant_descr.0.fvTenant.attributes.descr == 'Ansible test tenant' + - fc_cm_add_tenant_descr_again == '' + - fc_nm_add_tenant_descr_again == '' + +- name: Verify add_tenant_descr + assert: + that: + - cm_add_tenant_descr is changed + - nm_add_tenant_descr is changed + - cm_add_tenant_descr_again is not changed + - nm_add_tenant_descr_again is not changed + - cm_add_tenant_descr.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_add_tenant_descr.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_add_tenant_descr.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - nm_add_tenant_descr_again.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_add_tenant_descr_again.proposed.fvTenant.attributes.ownerTag == 'ansible_tag_changed' # ADD TENANT AGAIN @@ -232,19 +230,19 @@ check_mode: true register: cm_add_tenant_again_no_descr -# - name: Add tenant again with no description (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_present -# annotation: ansible_test_changed -# owner_key: ansible_key_changed -# owner_tag: ansible_tag_changed -# register: nm_add_tenant_again_no_descr +- name: Add tenant again with no description (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_present + annotation: ansible_test_changed + owner_key: ansible_key_changed + owner_tag: ansible_tag_changed + register: nm_add_tenant_again_no_descr -# - name: Verify add_tenant_again_no_descr -# assert: -# that: -# - cm_add_tenant_again_no_descr is not changed -# - nm_add_tenant_again_no_descr is not changed +- name: Verify add_tenant_again_no_descr + assert: + that: + - cm_add_tenant_again_no_descr is not changed + - nm_add_tenant_again_no_descr is not changed # QUERY ALL TENANTS @@ -261,17 +259,17 @@ check_mode: true register: cm_query_all_tenants -# - name: Query all tenants (normal mode) -# cisco.aci.aci_tenant: *tenant_query -# register: nm_query_all_tenants +- name: Query all tenants (normal mode) + cisco.aci.aci_tenant: *tenant_query + register: nm_query_all_tenants -# - name: Verify query_all_tenants -# assert: -# that: -# - cm_query_all_tenants is not changed -# - nm_query_all_tenants is not changed -# # NOTE: Order of tenants is not stable between calls -# #- cm_query_all_tenants == nm_query_all_tenants +- name: Verify query_all_tenants + assert: + that: + - cm_query_all_tenants is not changed + - nm_query_all_tenants is not changed + # NOTE: Order of tenants is not stable between calls + #- cm_query_all_tenants == nm_query_all_tenants # QUERY A TENANT @@ -282,37 +280,37 @@ check_mode: true register: cm_query_tenant -# - name: Query our tenant -# cisco.aci.aci_tenant: -# <<: *tenant_query -# tenant: ansible_test -# register: nm_query_tenant - -# - name: Verify query_tenant -# assert: -# that: -# - cm_query_tenant is not changed -# - nm_query_tenant is not changed -# - cm_query_tenant == nm_query_tenant -# - cm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' -# - nm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' -# - nm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - cm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' -# - nm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' -# - cm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' - - -# - name: Update tenant with default annotation -# cisco.aci.aci_tenant: -# <<: *tenant_present -# annotation: "{{ fake_var | default(omit) }}" -# register: default_annotation - -# - name: Assertion check for update tenant with default annotation -# assert: -# that: -# - default_annotation is changed -# - default_annotation.current.0.fvTenant.attributes.annotation == 'orchestrator:ansible' +- name: Query our tenant + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + register: nm_query_tenant + +- name: Verify query_tenant + assert: + that: + - cm_query_tenant is not changed + - nm_query_tenant is not changed + - cm_query_tenant == nm_query_tenant + - cm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_query_tenant.current[0].fvTenant.attributes.annotation == 'ansible_test_changed' + - nm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - cm_query_tenant.current[0].fvTenant.attributes.ownerKey == 'ansible_key_changed' + - nm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + - cm_query_tenant.current[0].fvTenant.attributes.ownerTag == 'ansible_tag_changed' + + +- name: Update tenant with default annotation + cisco.aci.aci_tenant: + <<: *tenant_present + annotation: "{{ fake_var | default(omit) }}" + register: default_annotation + +- name: Assertion check for update tenant with default annotation + assert: + that: + - default_annotation is changed + - default_annotation.current.0.fvTenant.attributes.annotation == 'orchestrator:ansible' # REMOVE TENANT - name: Remove tenant (check_mode) @@ -322,11 +320,11 @@ check_mode: true register: cm_remove_tenant -# - name: Remove tenant (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_absent -# output_path: "{{ aci_hostname }}_nm_remove_tenant.json" -# register: nm_remove_tenant +- name: Remove tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_absent + output_path: "{{ aci_hostname }}_nm_remove_tenant.json" + register: nm_remove_tenant - name: Remove tenant again (check_mode) cisco.aci.aci_tenant: @@ -335,35 +333,35 @@ check_mode: true register: cm_remove_tenant_again -# - name: Remove tenant again (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_absent -# output_path: "{{ aci_hostname }}_nm_remove_tenant_again.json" -# register: nm_remove_tenant_again - -# - name: Store file content on variables for delete object -# set_fact: -# fc_cm_remove_tenant: "{{ lookup('file', aci_hostname + '_cm_remove_tenant.json') | from_json }}" -# fc_nm_remove_tenant: "{{ lookup('file', aci_hostname + '_nm_remove_tenant.json') | from_json }}" -# fc_cm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_cm_remove_tenant_again.json') }}" -# fc_nm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_nm_remove_tenant_again.json') }}" - -# - name: Log file content verification for delete object -# assert: -# that: -# - fc_cm_remove_tenant.0.fvTenant.attributes.status == 'deleted' -# - fc_cm_remove_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' -# - fc_nm_remove_tenant == [{}] -# - fc_cm_remove_tenant_again == '' -# - fc_nm_remove_tenant_again == '' - -# - name: Verify remove_tenant -# assert: -# that: -# - cm_remove_tenant is changed -# - nm_remove_tenant is changed -# - cm_remove_tenant_again is not changed -# - nm_remove_tenant_again is not changed +- name: Remove tenant again (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_absent + output_path: "{{ aci_hostname }}_nm_remove_tenant_again.json" + register: nm_remove_tenant_again + +- name: Store file content on variables for delete object + set_fact: + fc_cm_remove_tenant: "{{ lookup('file', aci_hostname + '_cm_remove_tenant.json') | from_json }}" + fc_nm_remove_tenant: "{{ lookup('file', aci_hostname + '_nm_remove_tenant.json') | from_json }}" + fc_cm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_cm_remove_tenant_again.json') }}" + fc_nm_remove_tenant_again: "{{ lookup('file', aci_hostname + '_nm_remove_tenant_again.json') }}" + +- name: Log file content verification for delete object + assert: + that: + - fc_cm_remove_tenant.0.fvTenant.attributes.status == 'deleted' + - fc_cm_remove_tenant.0.fvTenant.attributes.dn == 'uni/tn-ansible_test' + - fc_nm_remove_tenant == [{}] + - fc_cm_remove_tenant_again == '' + - fc_nm_remove_tenant_again == '' + +- name: Verify remove_tenant + assert: + that: + - cm_remove_tenant is changed + - nm_remove_tenant is changed + - cm_remove_tenant_again is not changed + - nm_remove_tenant_again is not changed # QUERY NON-EXISTING TENANT @@ -374,16 +372,16 @@ check_mode: true register: cm_query_non_tenant -# - name: Query non-existing tenant (normal mode) -# cisco.aci.aci_tenant: -# <<: *tenant_query -# tenant: ansible_test -# register: nm_query_non_tenant - -# # TODO: Implement more tests -# - name: Verify query_non_tenant -# assert: -# that: -# - cm_query_non_tenant is not changed -# - nm_query_non_tenant is not changed -# - cm_query_non_tenant == nm_query_non_tenant +- name: Query non-existing tenant (normal mode) + cisco.aci.aci_tenant: + <<: *tenant_query + tenant: ansible_test + register: nm_query_non_tenant + +# TODO: Implement more tests +- name: Verify query_non_tenant + assert: + that: + - cm_query_non_tenant is not changed + - nm_query_non_tenant is not changed + - cm_query_non_tenant == nm_query_non_tenant From 021807c072117cfafb746a19566d52adba1eb348 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Tue, 25 Apr 2023 22:37:49 -0400 Subject: [PATCH 35/49] [ignore_changes] Reset hosts list when all hosts are exhausted in the list --- plugins/httpapi/aci.py | 59 +++++++++++-------- plugins/module_utils/aci.py | 11 ++-- .../aci_tenant/filter_plugins/generate_ips.py | 2 +- .../targets/aci_tenant/tasks/main.yml | 20 +++---- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 5527f62b0..a51a5fec0 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -83,7 +83,7 @@ def set_params(self, params): def login(self, username, password): """Log in to APIC""" # Perform login request - self.connection.queue_message("step:", "Establishing login to {0}".format(self.connection.get_option("host"))) + self.connection.queue_message("debug", "Establishing login to {0}".format(self.connection.get_option("host"))) method = "POST" path = "/api/aaaLogin.json" payload = {"aaaUser": {"attributes": {"name": username, "pwd": password}}} @@ -95,7 +95,7 @@ def login(self, username, password): self.connection._auth = { "Cookie": "APIC-Cookie={0}".format(self._response_to_json(response_value).get("imdata")[0]["aaaLogin"]["attributes"]["token"]) } - self.connection.queue_message("step:", "Connection to {0} was successful".format(self.connection.get_option("host"))) + self.connection.queue_message("debug", "Connection to {0} was successful".format(self.connection.get_option("host"))) except Exception: self.connection._connected = False raise @@ -136,6 +136,7 @@ def set_parameters(self): else: if self.connection_parameters.get("private_key") is not None: self.connection._connected = False + self.connection.queue_message("step", "Re-setting connection due to change from private/session key authentication to password authentication") self.connection.set_option("session_key", None) connection_parameters["private_key"] = None connection_parameters["certificate_name"] = None @@ -147,33 +148,39 @@ def set_parameters(self): def set_hosts(self): if self.params.get("host") is not None: - get_hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") + hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") else: if self.inventory_hosts is None: self.inventory_hosts = re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",") - get_hosts = self.inventory_hosts + hosts = self.inventory_hosts if self.provided_hosts is None: - self.provided_hosts = deepcopy(get_hosts) + self.provided_hosts = deepcopy(hosts) self.connection.queue_message( - "step:", "Provided Hosts: {0}".format(self.provided_hosts) + "debug", "Provided Hosts: {0}".format(self.provided_hosts) ) - self.backup_hosts = deepcopy(get_hosts) + self.backup_hosts = deepcopy(hosts) self.current_host = self.backup_hosts.pop(0) - elif self.provided_hosts != get_hosts: - self.provided_hosts = deepcopy(get_hosts) self.connection.queue_message( - "step:", "Provided Hosts have changed: {0}".format(self.provided_hosts) + "debug", "Initializing operation on {0}".format(self.current_host) + ) + elif self.provided_hosts != hosts: + self.provided_hosts = deepcopy(hosts) + self.connection.queue_message( + "debug", "Provided Hosts have changed: {0}".format(self.provided_hosts) ) - self.backup_hosts = deepcopy(get_hosts) + self.backup_hosts = deepcopy(hosts) try: - index = self.backup_hosts.index(self.current_host) - self.backup_hosts.pop(index) + self.backup_hosts.pop(self.backup_hosts.index(self.current_host)) self.connection.queue_message( - "step:", "Continuing the operations on the connected host: {0}".format(self.current_host) + "debug", "Connected host {0} found in the provided hosts. Continuing with it.".format(self.current_host) ) - except: + except Exception: self.current_host = self.backup_hosts.pop(0) + self.connection._connected = False + self.connection.queue_message( + "debug", "Initializing operation on {0}".format(self.current_host) + ) self.connection.set_option("host", self.current_host) # One API call is made via each call to send_request from aci.py in module_utils @@ -189,25 +196,29 @@ def send_request(self, method, path, data): self.connection._connected = True except Exception as exc_response: self.connection._connected = False - return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), str(exc_response)) + return self._return_info("", method, self.validate_url(self.connection._url+path), str(exc_response)) try: if self.connection._connected is False: self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) + self.connection.queue_message( + "debug", "Sending {0} request to {1}".format(method, self.connection._url+path) + ) response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message( - "step:", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) + "debug", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) ) except Exception as exc_response: - self.connection.queue_message("step:", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) + self.connection.queue_message("debug", "Connection to {0} has failed: {1}".format(self.connection.get_option("host"), exc_response)) if len(self.backup_hosts) == 0: + self.provided_hosts = None self.connection._connected = False msg = str("No hosts left in the cluster to continue operation! Error on final host {0}: {1}".format(self.connection.get_option("host"), exc_response)) - return self._return_info("", method, re.match(r'^.*?\.json',self.connection._url+path).group(0), msg) + return self._return_info("", method, self.validate_url(self.connection._url+path), msg) else: self.current_host = self.backup_hosts.pop(0) self.connection.queue_message( - "step:", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) + "debug", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) ) self.connection.set_option("host", self.current_host) # recurse through function for retrying the request @@ -217,15 +228,17 @@ def send_request(self, method, path, data): # Built-in-function def handle_httperror(self, exc): - self.connection.queue_message("step:", "Failed to receive response from {0}: {1}".format(self.connection.get_option("host"), exc)) if exc.code == 401: - return False + raise ConnectionError(exc) elif exc.code == 403 and self.connection_parameters.get("private_key") is None: self.connection._auth = None self.login(self.connection.get_option('remote_user'), self.connection.get_option('password')) return True return exc + def validate_url(self, url): + return re.match(r'^.*?\.json|^.*?\.xml', url).group(0) + def _verify_response(self, response, method, path, response_data): """Process the return code and response object from APIC""" response_value = self._get_response_value(response_data) @@ -234,7 +247,7 @@ def _verify_response(self, response, method, path, response_data): else: respond_data = response_value response_code = response.getcode() - path = re.match(r'^.*?\.json|^.*?\.xml', response.url).group(0) + path = self.validate_url(response.url) # Response check to remain consistent with fetch_url's response if str(response) == "HTTP Error 400: Bad Request": msg = "{0}".format(response) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index aace7ce9f..04a6cce81 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -52,7 +52,6 @@ from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_bytes, to_native from ansible.module_utils.basic import env_fallback -from ansible.module_utils._text import to_text from ansible.module_utils.connection import Connection # Optional, only used for APIC signature-based authentication @@ -75,7 +74,7 @@ # Optional, only used for XML payload try: - import lxml.etree # noqa + import lxml.etree HAS_LXML_ETREE = True except ImportError: @@ -511,10 +510,7 @@ def cert_auth(self, path=None, payload="", method=None): def response_json(self, rawoutput): """Handle APIC JSON response output""" try: - if isinstance(rawoutput, dict): - jsondata = rawoutput - else: - jsondata = json.loads(rawoutput) + jsondata = json.loads(rawoutput) except Exception as e: # Expose RAW output for troubleshooting self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e) @@ -625,6 +621,7 @@ def _deep_url_path_builder(self, obj): ) def _deep_url_parent_object(self, parent_objects, parent_class): + for parent_object in parent_objects: if parent_object.get("aci_class") is parent_class: return parent_object @@ -773,6 +770,7 @@ def construct_url( child_classes=None, config_only=True, ): + """ This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC. @@ -1495,6 +1493,7 @@ def exit_json(self, filter_existing=None, **kwargs): self.module.exit_json(**self.result) def fail_json(self, msg, **kwargs): + # Return error information, if we have it if self.error.get("code") is not None and self.error.get("text") is not None: self.result["error"] = self.error diff --git a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py index a85f3c696..019729752 100644 --- a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py +++ b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py @@ -18,4 +18,4 @@ def generate_random_ips(self, given_ip, insert_given_ip_at, number_of_ips): else: ips += str((random.choice(RANGE_IPV4))) ips += "," - return ips.rstrip(',') \ No newline at end of file + return ips.rstrip(',') diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index 2c8fc0bcb..e7ec1e68d 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -1,8 +1,8 @@ -# # Test code for the ACI modules -# # Copyright: (c) 2017, Dag Wieers (@dagwieers) -# # Copyright: (c) 2020, Lionel Hercot (@lhercot) +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) +# Copyright: (c) 2020, Lionel Hercot (@lhercot) -# # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) - name: Test that we have an ACI APIC host, ACI username and ACI password fail: @@ -59,7 +59,7 @@ annotation: ansible_test owner_key: ansible_key owner_tag: ansible_tag - output_path: "{{ ansible_host }}_cm_add_tenant.json" + output_path: "{{ aci_hostname }}_cm_add_tenant.json" check_mode: true register: cm_add_tenant @@ -95,7 +95,7 @@ - name: Add tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_present - output_path: "{{ ansible_host }}_cm_add_tenant_again.json" + output_path: "{{ aci_hostname }}_cm_add_tenant_again.json" check_mode: true register: cm_add_tenant_again @@ -149,7 +149,7 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed - output_path: "{{ ansible_host }}_cm_add_tenant_descr.json" + output_path: "{{ aci_hostname }}_cm_add_tenant_descr.json" check_mode: true register: cm_add_tenant_descr @@ -170,7 +170,7 @@ annotation: ansible_test_changed owner_key: ansible_key_changed owner_tag: ansible_tag_changed - output_path: "{{ ansible_host }}_cm_add_tenant_descr_again.json" + output_path: "{{ aci_hostname }}_cm_add_tenant_descr_again.json" check_mode: true register: cm_add_tenant_descr_again @@ -316,7 +316,7 @@ - name: Remove tenant (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent - output_path: "{{ ansible_host }}_cm_remove_tenant.json" + output_path: "{{ aci_hostname }}_cm_remove_tenant.json" check_mode: true register: cm_remove_tenant @@ -329,7 +329,7 @@ - name: Remove tenant again (check_mode) cisco.aci.aci_tenant: <<: *tenant_absent - output_path: "{{ ansible_host }}_cm_remove_tenant_again.json" + output_path: "{{ aci_hostname }}_cm_remove_tenant_again.json" check_mode: true register: cm_remove_tenant_again From bf12ac6767e85cd948c46e81edba38b053955262 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 3 May 2023 10:51:56 -0400 Subject: [PATCH 36/49] [ignore_changes] New test file created --- plugins/httpapi/aci.py | 9 +- plugins/module_utils/aci.py | 24 +- plugins/modules/aci_config_rollback.py | 3 +- plugins/modules/aci_config_snapshot.py | 2 +- plugins/modules/aci_rest.py | 8 +- tests/integration/inventory.networking | 12 +- .../aci_tenant/tasks/httpapi_connection.yml | 615 ++ .../tasks/httpapi_inventory_password.yml | 5021 ----------------- .../targets/aci_tenant/tasks/main.yml | 5 +- 9 files changed, 655 insertions(+), 5044 deletions(-) create mode 100644 tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml delete mode 100644 tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index a51a5fec0..af24eeb2e 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -83,7 +83,7 @@ def set_params(self, params): def login(self, username, password): """Log in to APIC""" # Perform login request - self.connection.queue_message("debug", "Establishing login to {0}".format(self.connection.get_option("host"))) + self.connection.queue_message("debug", "Establishing login for {0} to {1}".format(username, self.connection.get_option("host"))) method = "POST" path = "/api/aaaLogin.json" payload = {"aaaUser": {"attributes": {"name": username, "pwd": password}}} @@ -124,7 +124,7 @@ def set_parameters(self): connection_parameters[key] = value if self.connection_parameters and value != self.connection_parameters.get(key) and key in RESET_KEYS: self.connection._connected = False - self.connection.queue_message("step", "Re-setting connection due to change in {0}".format(key)) + self.connection.queue_message("debug", "Re-setting connection due to change in {0}".format(key)) if self.params.get("private_key") is not None: self.connection.set_option("session_key", None) @@ -136,7 +136,7 @@ def set_parameters(self): else: if self.connection_parameters.get("private_key") is not None: self.connection._connected = False - self.connection.queue_message("step", "Re-setting connection due to change from private/session key authentication to password authentication") + self.connection.queue_message("debug", "Re-setting connection due to change from private/session key authentication to password authentication") self.connection.set_option("session_key", None) connection_parameters["private_key"] = None connection_parameters["certificate_name"] = None @@ -228,6 +228,9 @@ def send_request(self, method, path, data): # Built-in-function def handle_httperror(self, exc): + self.connection.queue_message( + "debug", "Failed to receive response from {0} with {1}".format(self.connection.get_option("host"), exc) + ) if exc.code == 401: raise ConnectionError(exc) elif exc.code == 403 and self.connection_parameters.get("private_key") is None: diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 04a6cce81..47201f96b 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -48,6 +48,7 @@ import json import os from copy import deepcopy +from urllib.parse import urlparse from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_bytes, to_native @@ -423,7 +424,7 @@ def login(self): } } } - resp, auth = self.api_call("POST", None, url, data=json.dumps(payload), output=True) + resp, auth = self.api_call("POST", url, data=json.dumps(payload), output=True) # Handle APIC response if auth.get("status") != 200: @@ -1145,7 +1146,7 @@ def delete_config(self): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes - self.api_call("DELETE", self.path, self.url, None, output=False) + self.api_call("DELETE", self.url, None, output=False) else: self.result["changed"] = True self.method = "DELETE" @@ -1267,9 +1268,8 @@ def get_existing(self): and existing configuration will be added to the self.result dictionary. """ uri = self.url + self.filter_string - path = self.path + self.filter_string - self.api_call("GET", path, uri, data=None, output=False) + self.api_call("GET", uri, data=None, output=False) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1389,6 +1389,7 @@ def post_config(self, parent_class=None): <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD url = self.url if parent_class is not None: @@ -1441,6 +1442,9 @@ def post_config(self, parent_class=None): ======= self.api_call("POST", self.path, self.url, json.dumps(self.config), output=False) >>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) +======= + self.api_call("POST", self.url, json.dumps(self.config), output=False) +>>>>>>> f20fdfe ([ignore_changes] New test file created) else: self.result["changed"] = True self.method = "POST" @@ -1558,6 +1562,7 @@ def dump_json(self): <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD def delete_config_request(self, path): self._config_request(path, "absent") @@ -1610,15 +1615,19 @@ def api_call(self, method): ======= def api_call(self, method, path, url, data=None, output=False): >>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) +======= + def api_call(self, method, url, data=None, output=False): +>>>>>>> f20fdfe ([ignore_changes] New test file created) resp = None - if self.params.get("private_key"): - self.cert_auth(path=path, payload=data, method=method) + path = urlparse(url).path if self.get_connection() is not None: self.get_connection().set_params(self.params) - info = self.get_connection().send_request(method, "/{0}".format(path), data) + info = self.get_connection().send_request(method, path, data) self.url = info.get("url") self.httpapi_logs.extend(self.get_connection().pop_messages()) else: + if self.params.get("private_key"): + self.cert_auth(path=path, payload=data, method=method) resp, info = fetch_url( self.module, url, @@ -1629,7 +1638,6 @@ def api_call(self, method, path, url, data=None, output=False): use_proxy=self.params.get("use_proxy", True), ) - self.response = info.get("msg") self.status = info.get("status") self.method = method diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 7763af0d3..568e65272 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -303,9 +303,8 @@ def get_preview(aci): This function is used to generate a preview between two snapshots and add the parsed results to the aci module return data. """ uri = aci.url + aci.filter_string - path = aci.path + aci.filter_string - resp, info = aci.api_call('GET', path, uri, data=None, output=True) + resp, info = aci.api_call('GET', uri, data=None, output=True) # Handle APIC response if info.get("status") == 200: diff --git a/plugins/modules/aci_config_snapshot.py b/plugins/modules/aci_config_snapshot.py index ceed6ecdd..09b3f6ddb 100644 --- a/plugins/modules/aci_config_snapshot.py +++ b/plugins/modules/aci_config_snapshot.py @@ -309,7 +309,7 @@ def main(): port_url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") else: port_url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") - resp, info = aci.api_call('GET', path, port_url, data=None, output=True) + resp, info = aci.api_call('GET', port_url, data=None, output=True) try: aci.imdata = json.loads(resp.read())["imdata"] except AttributeError: diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index fd8b10943..74544b4af 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -334,6 +334,7 @@ def main(): method=dict(type="str", default="get", choices=["delete", "get", "post"], aliases=["action"]), src=dict(type="path", aliases=["config_file"]), content=dict(type="raw"), + rsp_subtree_preserve=dict(type=bool, default=False), ) module = AnsibleModule( @@ -344,6 +345,7 @@ def main(): content = module.params.get("content") path = module.params.get("path") src = module.params.get("src") + rsp_subtree_preserve = module.params.get("rsp_subtree_preserve") # Report missing file file_exists = False @@ -402,14 +404,14 @@ def main(): aci.url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") else: aci.url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") - if aci.params.get("method") != "get": - path += "?rsp-subtree=modified" + if aci.params.get("method") != "get" and not rsp_subtree_preserve: + #path += "?rsp-subtree=modified" aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) method = aci.params.get("method").upper() # Perform request - resp, info = aci.api_call(method, path, aci.url, data=payload, output=True) + resp, info = aci.api_call(method, aci.url, data=payload, output=True) # Report failure if info.get("status") != 200: diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index ec6249bee..c1ca354a7 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -3,12 +3,18 @@ cn-dmz-apic-m1-02-v42 ansible_host=173.36.219.68 aci_hostname=173.36.219.68 cn-dmz-apic-m1-03-v52 ansible_host=173.36.219.69 aci_hostname=173.36.219.69 cn-dmz-apic-m1-04-v60 ansible_host=173.36.219.70 aci_hostname=173.36.219.70 cn-dmz-apic-m1-07-v32 ansible_host=173.36.219.73 aci_hostname=173.36.219.73 -aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a -azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true -#inventory_hosts aci_hostname=173.36.219.72 ansible_user=ansible_github_ci ansible_password="sJ94G92#8dq2hx*K4qh" ansible_command_timeout=5 ansible_connection=ansible.netcommon.httpapi ansible_network_os=cisco.aci.aci ansible_httpapi_validate_certs=False ansible_httpapi_use_ssl=True ansible_httpapi_use_proxy=True ansible_httpapi_port=443 +aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a +azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=azure region=westus region_2=westus2 vnet_gateway=true [aci:vars] aci_username=ansible_github_ci aci_password="sJ94G92#8dq2hx*K4qh" +ansible_user=ansible_github_ci +ansible_password="sJ94G92#8dq2hx*K4qh" +ansible_network_os=cisco.aci.aci ansible_connection=local +ansible_httpapi_validate_certs=False +ansible_httpapi_use_ssl=True +ansible_httpapi_use_proxy=True +ansible_httpapi_port=443 ansible_python_interpreter=/usr/bin/python3.9 diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml new file mode 100644 index 000000000..51d392ed8 --- /dev/null +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml @@ -0,0 +1,615 @@ +## Tests HTTTP Connection when a list of host are provided + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: "{{ validate_certs | default(false) }}" + use_ssl: "{{ use_ssl | default(true) }}" + timeout: 5 + output_level: debug + +- name: Set vars with the randomly generated list of hosts for the task level operations + ansible.builtin.set_fact: + last_host_active: "{{ aci_hostname | generate_random_ips(5,5) }}" + second_host_active: "{{aci_hostname|generate_random_ips(2,5)}}" + no_host_active: "{{aci_hostname|generate_random_ips(0,5)}}" + +- name: Set the connection to httpapi and set session key used in the inventory + ansible.builtin.set_fact: + ansible_connection: ansible.netcommon.httpapi + ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'certs/admin.key') }}"} + +- name: Add user certificate to be used later in the test + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + aaa_user: "{{ aci_username }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: present + +# XML operation, APIC HTTP Error and Connection reset upon changed parameter tests +- name: Add a tenant using an XML string (Check xml operations through plugin) + cisco.aci.aci_rest: + <<: *aci_info + path: api/mo/uni.xml + method: post + content: '' + register: tenant_xml_plugin + +- name: Add an AP (with non existent tenant) + cisco.aci.aci_ap: + <<: *aci_info + tenant: ansible_test_non_existent + ap: ap + description: default ap + state: present + ignore_errors: true + register: ap_non_existent_tenant + +- name: Delete Tenant with the wrong username and password (Check that connection resets) + cisco.aci.aci_tenant: + <<: *aci_info + username: wrong_username + password: wrong_password + tenant: ansible_test + state: absent + ignore_errors: true + register: wrong_credentials + +- name: Flatten the registered instances + ansible.builtin.set_fact: + ap_non_existent_tenant_flattened: "{{ ap_non_existent_tenant.httpapi_logs | flatten }}" + wrong_credentials_flattened: "{{ wrong_credentials.httpapi_logs | flatten }}" + +- name: Verify XML operation and HTTP error returned by APIC + assert: + that: + - tenant_xml_plugin.status == 200 + - '"Received response from {{ aci_hostname }} for POST operation with HTTP: 400" in ap_non_existent_tenant_flattened' + - '"Re-setting connection due to change in username" in wrong_credentials_flattened' + - '"Re-setting connection due to change in password" in wrong_credentials_flattened' + - '"Connection to {{ aci_hostname }} has failed: HTTP Error 401: Unauthorized" in wrong_credentials_flattened' + +# Simulate HTTP 403 error test +- name: Delete Tenant with only password in the task (Check for 403) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + tenant: ansible_test + state: absent + register: op17_task_pwd_delete_tenant + +- name: Add Tenant with only password in the task (Check for 403) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + tenant: ansible_test + state: present + register: op17_task_pwd_add_tenant + +- name: logout to render the token invalid + cisco.aci.aci_rest: + <<: *aci_info + host: "{{ last_host_active }}" + path: /api/aaaLogout.json + method: post + rsp_subtree_preserve: true + content: | + { + "aaaUser": { + "attributes": { + "name": "{{ aci_username }}" + } + } + } + +- name: Add an AP with only password in the task (Ensure re-login on the same host) + cisco.aci.aci_ap: + <<: *aci_info + host: "{{ last_host_active }}" + tenant: ansible_test + ap: ap + description: default ap + state: present + register: op18_task_pwd_add_ap + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op18_flattened_task_pwd_add_ap: "{{ op18_task_pwd_add_ap.httpapi_logs | flatten }}" + +- name: Verify forbidden error 403 + assert: + that: + - op17_task_pwd_add_tenant is changed + - op18_task_pwd_add_ap is changed + - '"Failed to receive response from {{ aci_hostname }} with HTTP Error 403: Forbidden" in op18_flattened_task_pwd_add_ap' + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op18_flattened_task_pwd_add_ap' + - '"Connection to {{ aci_hostname }} was successful" in op18_flattened_task_pwd_add_ap' + +- name: reset connection to test other scenarios + meta: reset_connection + +# Precedence test +- name: Delete Tenant with password and private key in the task (private_key takes precedence) + cisco.aci.aci_tenant: + <<: *aci_info + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: op1_task_private_key_delete_tenant + +- name: Add Tenant with password and private key in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: op1_task_private_key_add_tenant + +- name: Delete Tenant with only password in the task (password in the task takes precedence) + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_test + state: absent + register: op2_task_pwd_delete_tenant + +- name: Add Tenant with only password in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_test + state: present + register: op2_task_pwd_add_tenant + +- name: Delete Tenant with password and session key in the inventory (session_key takes precedence) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op3_inventory_session_key_delete_tenant + +- name: Add Tenant with password and session key in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op3_inventory_session_key_add_tenant + +- name: Remove session key used in the inventory + ansible.builtin.set_fact: + ansible_httpapi_session_key: + +- name: Delete Tenant with with only password in the inventory (check for authentication with the password in the inventory) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op4_inventory_pwd_delete_tenant + +- name: Add Tenant with with only password in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op4_inventory_pwd_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op1_flattened_task_private_key_delete_tenant: "{{ op1_task_private_key_delete_tenant.httpapi_logs | flatten }}" + op1_flattened_task_private_key_add_tenant: "{{ op1_task_private_key_add_tenant.httpapi_logs | flatten }}" + op2_flattened_task_pwd_delete_tenant: "{{ op2_task_pwd_delete_tenant.httpapi_logs | flatten }}" + op2_flattened_task_pwd_add_tenant: "{{ op2_task_pwd_add_tenant.httpapi_logs | flatten }}" + op3_flattened_inventory_session_key_delete_tenant: "{{ op3_inventory_session_key_delete_tenant.httpapi_logs | flatten }}" + op3_flattened_inventory_session_key_add_tenant: "{{ op3_inventory_session_key_add_tenant.httpapi_logs | flatten }}" + op4_flattened_inventory_pwd_delete_tenant: "{{ op4_inventory_pwd_delete_tenant.httpapi_logs | flatten }}" + op4_flattened_inventory_pwd_add_tenant: "{{ op4_inventory_pwd_add_tenant.httpapi_logs | flatten }}" + +- name: Verify Precedence + assert: + that: + - '"Provided Hosts: [''{{ aci_hostname }}'']" in op1_flattened_task_private_key_delete_tenant' + - '"Initializing operation on {{ aci_hostname }}" in op1_flattened_task_private_key_delete_tenant' + - op1_task_private_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op1_flattened_task_private_key_add_tenant' + - op2_task_pwd_delete_tenant is changed + - '"Re-setting connection due to change from private/session key authentication to password authentication" in op2_flattened_task_pwd_delete_tenant' + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op2_flattened_task_pwd_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" in op2_flattened_task_pwd_delete_tenant' + - op2_task_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op2_flattened_task_pwd_add_tenant' + - op3_inventory_session_key_delete_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op3_flattened_inventory_session_key_delete_tenant' + - op3_inventory_session_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op3_flattened_inventory_session_key_add_tenant' + - op4_inventory_pwd_delete_tenant is changed + - '"Re-setting connection due to change from private/session key authentication to password authentication" in op4_flattened_inventory_pwd_delete_tenant' + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op4_flattened_inventory_pwd_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" in op4_flattened_inventory_pwd_delete_tenant' + - op4_inventory_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op4_flattened_inventory_pwd_add_tenant' + +- name: reset connection to test other scenarios + meta: reset_connection + +# Switching of hosts test with the password in the task +- name: Delete Tenant with only password in the task (Check for successful operation on the last host) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + tenant: ansible_test + state: absent + register: op5_task_pwd_delete_tenant + +- name: Add Tenant with only password in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + tenant: ansible_test + state: present + register: op5_task_pwd_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op5_flattened_task_pwd_delete_tenant: "{{ op5_task_pwd_delete_tenant.httpapi_logs | flatten }}" + op5_flattened_task_pwd_add_tenant: "{{ op5_task_pwd_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the password in the task + assert: + that: + - op5_task_pwd_delete_tenant is changed + - op5_flattened_task_pwd_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op5_flattened_task_pwd_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" in op5_flattened_task_pwd_delete_tenant' + - op5_task_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op5_flattened_task_pwd_add_tenant' + +# Continuing on the connected host test with the password in the task +- name: Delete Tenant with only password in the task + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + tenant: ansible_test + state: absent + register: op6_task_pwd_delete_tenant + +- name: Add Tenant with only password in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + tenant: ansible_test + state: present + register: op6_task_pwd_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op6_flattened_task_pwd_delete_tenant: "{{ op6_task_pwd_delete_tenant.httpapi_logs | flatten }}" + op6_flattened_task_pwd_add_tenant: "{{ op6_task_pwd_add_tenant.httpapi_logs | flatten }}" + +- name: Verify continuation of the operations on the connected host with the password in the task + assert: + that: + - op6_task_pwd_delete_tenant is changed + - '"Connected host {{ aci_hostname }} found in the provided hosts. Continuing with it." in op6_flattened_task_pwd_delete_tenant' + - op6_task_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op6_flattened_task_pwd_add_tenant' + +# Change of hosts and no hosts active test with the password in the task +- name: Delete Tenant with only password in the task (Check for failed operation) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ no_host_active }}" + tenant: ansible_test + state: absent + register: op7_task_pwd_delete_tenant + ignore_errors: True + +- name: Add Tenant with only password in the task (Check the reset of the provided list of hosts) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + tenant: ansible_test + state: present + register: op7_task_pwd_add_tenant + +- name: Verify failure when no hosts are active + assert: + that: + - op7_task_pwd_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op7_task_pwd_add_tenant is not changed + +# Switching of hosts test with the the inventory password +- name: Set list of hosts in the inventory with the last host as active + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(5,5)}}" + +- name: Delete Tenant with only password in the inventory (Check for successful operation on the last host) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op8_inventory_pwd_delete_tenant + +- name: Add Tenant with only password in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op8_inventory_pwd_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op8_flattened_inventory_pwd_delete_tenant: "{{ op8_inventory_pwd_delete_tenant.httpapi_logs | flatten }}" + op8_flattened_inventory_pwd_add_tenant: "{{ op8_inventory_pwd_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the password in the inventory + assert: + that: + - op8_inventory_pwd_delete_tenant is changed + - op8_flattened_inventory_pwd_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op8_flattened_inventory_pwd_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" in op8_flattened_inventory_pwd_delete_tenant' + - op8_inventory_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op8_flattened_inventory_pwd_add_tenant' + +# Continuing on the connected host test with the inventory password +- name: Set list of hosts in the inventory with the second host as active + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(2,5)}}" + +- name: Delete Tenant with only password in the inventory (Check for execution on the previously connected host) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op9_inventory_pwd_delete_tenant + +- name: Add Tenant with only password in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op9_inventory_pwd_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op9_flattened_inventory_pwd_delete_tenant: "{{ op9_inventory_pwd_delete_tenant.httpapi_logs | flatten }}" + op9_flattened_inventory_pwd_add_tenant: "{{ op9_inventory_pwd_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the password in the inventory + assert: + that: + - op9_inventory_pwd_delete_tenant is changed + - op9_flattened_inventory_pwd_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" in op9_flattened_inventory_pwd_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" in op9_flattened_inventory_pwd_delete_tenant' + - op9_inventory_pwd_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op9_flattened_inventory_pwd_add_tenant' + +# Change of hosts and no hosts active test with the inventory password +- name: Set list of hosts in the inventory with no active hosts + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(0,5)}}" + +- name: Delete Tenant with only password in the inventory (Check for failed operation) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op10_inventory_pwd_delete_tenant + ignore_errors: True + +- name: Verify failure when no hosts are active in the inventory + assert: + that: + - op10_inventory_pwd_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + +# Switching of hosts test with the private key in the task +- name: Delete Tenant with only private key in the task (Check for successful operation on the last host) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: op11_task_private_key_delete_tenant + +- name: Add Tenant with only private key in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ last_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: op11_task_private_key_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op11_flattened_task_private_key_delete_tenant: "{{ op11_task_private_key_delete_tenant.httpapi_logs | flatten }}" + op11_flattened_task_private_key_add_tenant: "{{ op11_task_private_key_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the private key in the task + assert: + that: + - op11_task_private_key_delete_tenant is changed + - op11_flattened_task_private_key_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" not in op11_flattened_task_private_key_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" not in op11_flattened_task_private_key_delete_tenant' + - op11_task_private_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op11_flattened_task_private_key_add_tenant' + +# Continuing on the connected host test with the private key in the task +- name: Delete Tenant with only private key in the task (Check for execution on the previously connected host) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: op12_task_private_key_delete_tenant + +- name: Add Tenant with only private key in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: op12_task_private_key_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op12_flattened_task_private_key_delete_tenant: "{{ op12_task_private_key_delete_tenant.httpapi_logs | flatten }}" + op12_flattened_task_private_key_add_tenant: "{{ op12_task_private_key_add_tenant.httpapi_logs | flatten }}" + +- name: Verify continuation of the operations on the connected host with the password in the task + assert: + that: + - op12_task_private_key_delete_tenant is changed + - '"Connected host {{ aci_hostname }} found in the provided hosts. Continuing with it." in op12_flattened_task_private_key_delete_tenant' + - op12_task_private_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op12_flattened_task_private_key_add_tenant' + +# Change of hosts and no hosts active test with the private key in the task +- name: Delete Tenant with only private key in the task (Check for failed operation) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ no_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: absent + register: op13_task_private_key_delete_tenant + ignore_errors: True + +- name: Add Tenant with only private key in the task (Check the reset of the provided list of hosts) + cisco.aci.aci_tenant: + <<: *aci_info + host: "{{ second_host_active }}" + private_key: "{{ lookup('file', 'certs/admin.key') }}" + tenant: ansible_test + state: present + register: op13_task_private_key_add_tenant + +- name: Verify failure when no hosts are active in the task + assert: + that: + - op13_task_private_key_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op13_task_private_key_add_tenant is not changed + +# Switching of hosts test with the the inventory session key +- name: Set list of hosts in the inventory with the last host as active + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(5,5)}}" + ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'certs/admin.key') }}"} + +- name: Delete Tenant with session key in the inventory (Check for successful operation on the last host) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op14_inventory_session_key_delete_tenant + +- name: Add Tenant with session key in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op14_inventory_session_key_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op14_flattened_inventory_session_key_delete_tenant: "{{ op14_inventory_session_key_delete_tenant.httpapi_logs | flatten }}" + op14_flattened_inventory_session_key_add_tenant: "{{ op14_inventory_session_key_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the session key in the inventory + assert: + that: + - op14_inventory_session_key_delete_tenant is changed + - op14_flattened_inventory_session_key_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" not in op14_flattened_inventory_session_key_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" not in op14_flattened_inventory_session_key_delete_tenant' + - op14_inventory_session_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op14_flattened_inventory_session_key_add_tenant' + +# Continuing on the connected host test with the inventory session key +- name: Set list of hosts in the inventory with the second host as active + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(2,5)}}" + +- name: Delete Tenant with session key in the inventory (Check for execution on the previously connected host) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op15_inventory_session_key_delete_tenant + +- name: Add Tenant with session key in the inventory (Check for execution on the provided aci_hostname with no attempts at re-connection) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: present + register: op15_inventory_session_key_add_tenant + +- name: Flatten the registered instances + ansible.builtin.set_fact: + op15_flattened_inventory_session_key_delete_tenant: "{{ op15_inventory_session_key_delete_tenant.httpapi_logs | flatten }}" + op15_flattened_inventory_session_key_add_tenant: "{{ op15_inventory_session_key_add_tenant.httpapi_logs | flatten }}" + +- name: Verify switching of hosts with the session key in the inventory + assert: + that: + - op15_inventory_session_key_delete_tenant is changed + - op15_flattened_inventory_session_key_delete_tenant | regex_search('Switching host from [0-9]+(?:\.[0-9]+){3} to {{ aci_hostname }}') is not none + - '"Establishing login for {{ aci_username }} to {{ aci_hostname }}" not in op15_flattened_inventory_session_key_delete_tenant' + - '"Connection to {{ aci_hostname }} was successful" not in op15_flattened_inventory_session_key_delete_tenant' + - op15_inventory_session_key_add_tenant is changed + - '"Connection to {{ aci_hostname }} was successful" not in op15_flattened_inventory_session_key_add_tenant' + +# Change of hosts and no hosts active test with the inventory session key +- name: Set list of hosts in the inventory with no active hosts + ansible.builtin.set_fact: + ansible_host: "{{aci_hostname|generate_random_ips(0,5)}}" + +- name: Delete Tenant with session key in the inventory (Check for failed operation) + cisco.aci.aci_tenant: + output_level: debug + tenant: ansible_test + state: absent + register: op16_inventory_session_key_delete_tenant + ignore_errors: True + +- name: Verify failure when no hosts are active in the inventory + assert: + that: + - op16_inventory_session_key_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + +# Clean up Environment +- name: Delete a tenant using an XML string + cisco.aci.aci_rest: + <<: *aci_info + path: api/mo/uni/tn-[Sales].xml + method: delete + content: '' + +- name: Remove user certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + aaa_user: "{{ aci_username }}" + name: admin + certificate: "{{ lookup('file', 'certs/admin.crt') }}" + state: absent + +- name: Delete Tenant + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_test + state: absent + +- name: Remove session key used in the inventory + ansible.builtin.set_fact: + ansible_connection: local + ansible_httpapi_session_key: + +- name: refresh inventory for other tests in integration + meta: refresh_inventory diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml deleted file mode 100644 index 33fa9e1ab..000000000 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_inventory_password.yml +++ /dev/null @@ -1,5021 +0,0 @@ -##TEST CONNECTION - -## If credentials in the inventory are used. Remove session_key from inventory ## - -## Active Host (PASS) -# Run on single active Host at task level using credentials -# Run on single active Host at task level using certificate -# Run on single active Host at task level using credentials -# Run on single active Host in inventory using credentials -# Run on single active Host at task level using credentials -# Run on single active Host at task level using certificate -# Run on single active Host in inventory using credentials -# Run on single active Host at task level using certificate - -## Inactive Host (FAIL use ignore errors) -# Run on single active Host at task level using credentials -# Run on single active Host at task level using certificate -# Run on single active Host at task level using credentials -# Run on single active Host in inventory using credentials -# Run on single active Host at task level using credentials -# Run on single active Host at task level using certificate -# Run on single active Host at task level using credentials -# Run on single active Host in inventory using credentials - -## First Host is Active (PASS on First Host) -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials - -## Second Host is Active (PASS on Second Host) -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials - -## Last Host is Active (PASS on last Host) -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials - -## None of the Hosts are Active (FAIL use ignore errors) -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts at task level using certificate -# Run on multiple Hosts at task level using credentials -# Run on multiple Hosts in inventory using credentials - -# NOTE - ansible.builtin.add_host is only used for testing!!! ansible_host variable needs to be absent in the inventory while using ansible.builtin.add_host -# - If you set same variable names across the playbook to tasks and inventory, the precedence changes where the higher precedence is given to the inventory and not vars set by set_fact. We make sure that the variable names are different. - - # Setup Environment ## - - name: Set vars - set_fact: - aci_info_single_host_active: &aci_info_single_host_active - host: 173.36.219.72 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_single_host_active_certificate: &aci_info_single_host_active_certificate - host: 173.36.219.72 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Add a tenant using an XML string - cisco.aci.aci_rest: - <<: *aci_info_single_host_active - path: /api/mo/uni.xml - method: post - content: '' - ignore_errors: yes - - - name: Add user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: present - - ## Plugin Tests using password from inventory (Part1) ## - - - name: Set Single active host in inventory - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{aci_hostname|generate_random_ips(5,5)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - output_level: debug - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_1 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_2 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_2 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_2 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_3 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_3 - - - name: Add a new AP on single active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_3 - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_4 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_4 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_4 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_5 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_5 - - - name: Add a new AP on single active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_5 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_6 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_6 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_6 - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_7 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_7 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_7 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_8 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_8 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_8 - - - name: Verify operations on single active host - assert: - that: - - task_pwd_delete_tenant_single_active_host_1.current == [] - - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1 is changed - - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_1 is changed - - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_2 is changed - - task_cert_delete_tenant_single_active_host_2.current == [] - - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2 is changed - - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_2 is changed - - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_single_active_host_3 is changed - - task_pwd_delete_tenant_single_active_host_3.current == [] - - task_pwd_delete_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_3 is changed - - task_pwd_add_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_3 is changed - - task_pwd_add_ap_single_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_4 is changed - - inventory_pwd_delete_tenant_single_active_host_4.current == [] - - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4 is changed - - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_4 is changed - - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_single_active_host_5 is changed - - task_pwd_delete_tenant_single_active_host_5.current == [] - - task_pwd_delete_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_5 is changed - - task_pwd_add_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_5 is changed - - task_pwd_add_ap_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_6 is changed - - task_cert_delete_tenant_single_active_host_6.current == [] - - task_cert_delete_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_6 is changed - - task_cert_add_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_6 is changed - - task_cert_add_ap_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_7 is changed - - inventory_pwd_delete_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_single_active_host_7.current == [] - - inventory_pwd_add_tenant_single_active_host_7 is changed - - inventory_pwd_add_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_7 is changed - - inventory_pwd_add_ap_single_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_single_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_8 is changed - - task_cert_delete_tenant_single_active_host_8.current == [] - - task_cert_delete_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_8 is changed - - task_cert_add_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_8 is changed - - task_cert_add_ap_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - -###################################################################### - - - name: Set vars - set_fact: - aci_info_single_host_inactive: &aci_info_single_host_inactive - host: 124.145.225.187 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_single_host_inactive_certificate: &aci_info_single_host_inactive_certificate - host: 124.145.225.187 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set Single inactive host in inventory - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - -############################################################################### - - - name: Set vars - set_fact: - aci_info_first_host_active: &aci_info_first_host_active - host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_first_host_active_certificate: &aci_info_first_host_active_certificate - host: 173.36.219.72,124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set the inventory with the first host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_1 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_1 - - - name: Add a new AP on the first active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_1 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_2 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_2 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_2 - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_3 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_3 - - - name: Add a new AP on the first active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_3 - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_4 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_4 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_4 - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_5 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_5 - - - name: Add a new AP on the first active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_5 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_6 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_6 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_6 - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_7 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_7 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_7 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_8 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_8 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_8 - - - name: Verify operations on first active host - assert: - that: - - task_pwd_delete_tenant_first_active_host_1.current == [] - - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1 is changed - - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_1 is changed - - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_2 is changed - - task_cert_delete_tenant_first_active_host_2.current == [] - - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2 is changed - - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_2 is changed - - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_3 is changed - - task_pwd_delete_tenant_first_active_host_3.current == [] - - task_pwd_delete_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_3 is changed - - task_pwd_add_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_3 is changed - - task_pwd_add_ap_first_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_4 is changed - - inventory_pwd_delete_tenant_first_active_host_4.current == [] - - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4 is changed - - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_4 is changed - - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_5 is changed - - task_pwd_delete_tenant_first_active_host_5.current == [] - - task_pwd_delete_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_5 is changed - - task_pwd_add_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_5 is changed - - task_pwd_add_ap_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_6 is changed - - task_cert_delete_tenant_first_active_host_6.current == [] - - task_cert_delete_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_6 is changed - - task_cert_add_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_6 is changed - - task_cert_add_ap_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_7 is changed - - inventory_pwd_delete_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_first_active_host_7.current == [] - - inventory_pwd_add_tenant_first_active_host_7 is changed - - inventory_pwd_add_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_7 is changed - - inventory_pwd_add_ap_first_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_first_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_8 is changed - - task_cert_delete_tenant_first_active_host_8.current == [] - - task_cert_delete_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_8 is changed - - task_cert_add_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_8 is changed - - task_cert_add_ap_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################## - - - name: Set vars - set_fact: - aci_info_second_active_host: &aci_info_second_active_host - host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_second_active_host_certificate: &aci_info_second_active_host_certificate - host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set the inventory with the second host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_1 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_1 - - - name: Add a new AP on the second active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_1 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_2 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_2 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_2 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_3 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_3 - - - name: Add a new AP on the second active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_3 - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_4 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_4 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_4 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_5 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_5 - - - name: Add a new AP on the second active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_5 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_6 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_6 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_6 - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_7 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_7 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_7 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_8 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_8 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_8 - - - name: Verify operations on second active host - assert: - that: - - task_pwd_delete_tenant_second_active_host_1.current == [] - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_1 is changed - - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_1 is changed - - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_2 is changed - - task_cert_delete_tenant_second_active_host_2.current == [] - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_2 is changed - - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_2 is changed - - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_3 is changed - - task_pwd_delete_tenant_second_active_host_3.current == [] - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_3 is changed - - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_3 is changed - - task_pwd_add_ap_second_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_4 is changed - - inventory_pwd_delete_tenant_second_active_host_4.current == [] - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_second_active_host_4 is changed - - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_4 is changed - - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_5 is changed - - task_pwd_delete_tenant_second_active_host_5.current == [] - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_5 is changed - - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_5 is changed - - task_pwd_add_ap_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_6 is changed - - task_cert_delete_tenant_second_active_host_6.current == [] - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_6 is changed - - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_6 is changed - - task_cert_add_ap_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_7 is changed - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_delete_tenant_second_active_host_7.current == [] - - inventory_pwd_add_tenant_second_active_host_7 is changed - - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_7 is changed - - inventory_pwd_add_ap_second_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_second_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_8 is changed - - task_cert_delete_tenant_second_active_host_8.current == [] - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_8 is changed - - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_8 is changed - - task_cert_add_ap_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################### - - - name: Set vars - set_fact: - aci_info_last_active_host: &aci_info_last_active_host - host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_last_active_host_certificate: &aci_info_last_active_host_certificate - host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set the inventory with the last host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_1 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_1 - - - name: Add a new AP on the last active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_1 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_2 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_2 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_2 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_3 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_3 - - - name: Add a new AP on the last active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_3 - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_4 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_4 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_4 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_5 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_5 - - - name: Add a new AP on the last active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_5 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_6 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_6 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_6 - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_7 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_7 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_7 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_8 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_8 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_8 - - - name: Verify operations on last active host - assert: - that: - - task_pwd_delete_tenant_last_active_host_1.current == [] - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_1 is changed - - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_1 is changed - - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_2 is changed - - task_cert_delete_tenant_last_active_host_2.current == [] - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_2 is changed - - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_2 is changed - - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_3 is changed - - task_pwd_delete_tenant_last_active_host_3.current == [] - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_3 is changed - - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_3 is changed - - task_pwd_add_ap_last_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_4 is changed - - inventory_pwd_delete_tenant_last_active_host_4.current == [] - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_last_active_host_4 is changed - - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_4 is changed - - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_5 is changed - - task_pwd_delete_tenant_last_active_host_5.current == [] - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_5 is changed - - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_5 is changed - - task_pwd_add_ap_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_6 is changed - - task_cert_delete_tenant_last_active_host_6.current == [] - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_6 is changed - - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_6 is changed - - task_cert_add_ap_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_7 is changed - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_delete_tenant_last_active_host_7.current == [] - - inventory_pwd_add_tenant_last_active_host_7 is changed - - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_7 is changed - - inventory_pwd_add_ap_last_active_host_7.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_last_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_8 is changed - - task_cert_delete_tenant_last_active_host_8.current == [] - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_8 is changed - - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_8 is changed - - task_cert_add_ap_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################### - - - name: Set vars - set_fact: - aci_info_all_hosts_inactive: &aci_info_all_hosts_inactive - host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - username: "{{ aci_username }}" - password: "{{ aci_password }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set vars - set_fact: - aci_info_all_hosts_inactive_certificate: &aci_info_all_hosts_inactive_certificate - host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - username: "{{ aci_username }}" - validate_certs: '{{ validate_certs | default(false) }}' - use_ssl: '{{ use_ssl | default(true) }}' - timeout: 5 - output_level: debug - - - name: Set the inventory with all the hosts in the inventory as inactive - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - -############################################################################################# -#### Random Tets - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_1 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_single_active_host_1 - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_1 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_1 - - - name: Add a new AP on the first active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_1 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_1 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_1 - - - name: Add a new AP on the second active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_1 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_1 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_1 - - - name: Add a new AP on the last active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_1 - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_2 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_2 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_2 - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_2 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_2 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_2 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_2 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_2 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_2 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_2 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_2 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_2 - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Set hosts 1 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_4_random1 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_4_random1 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_4_random1 - - ## HTTP Error - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Set hosts 2 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Set hosts 3 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_4_random2 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_4_random2 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_4_random2 - - - name: Set hosts 4 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_4_random3 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_4_random3 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_4_random3 - - - name: Set hosts 5 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_4_random4 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_4_random4 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_4_random4 - - - name: Set hosts 6 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Verify random operations - assert: - that: - - task_pwd_delete_tenant_single_active_host_1.current == [] - - task_pwd_delete_tenant_second_single_active_host_1.current == [] - - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1 is changed - - task_pwd_add_tenant_second_single_active_host_1 is changed - - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_tenant_second_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_1 is changed - - task_pwd_add_ap_second_single_active_host_1 is changed - - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_add_ap_second_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_1.current == [] - - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1 is changed - - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_1 is changed - - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_1.current == [] - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_1 is changed - - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_1 is changed - - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_1.current == [] - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_1 is changed - - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_1 is changed - - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_2 is changed - - task_cert_delete_tenant_single_active_host_2.current == [] - - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2 is changed - - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_2 is changed - - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_2 is changed - - task_cert_delete_tenant_first_active_host_2.current == [] - - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2 is changed - - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_2 is changed - - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_2 is changed - - task_cert_delete_tenant_second_active_host_2.current == [] - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_2 is changed - - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_2 is changed - - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_2 is changed - - task_cert_delete_tenant_last_active_host_2.current == [] - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_2 is changed - - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_2 is changed - - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_4_random1 is changed - - inventory_pwd_delete_tenant_single_active_host_4_random1.current == [] - - inventory_pwd_delete_tenant_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4_random1 is changed - - inventory_pwd_add_tenant_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4_random1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_4_random1 is changed - - inventory_pwd_add_ap_single_active_host_4_random1.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_single_active_host_4_random1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_4_random2 is not changed - - inventory_pwd_delete_tenant_first_active_host_4_random2.current == [] - - inventory_pwd_delete_tenant_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4_random2 is changed - - inventory_pwd_add_tenant_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4_random2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_4_random2 is changed - - inventory_pwd_add_ap_first_active_host_4_random2.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_first_active_host_4_random2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_4_random3 is changed - - inventory_pwd_delete_tenant_second_active_host_4_random3.current == [] - - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4_random3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_second_active_host_4_random3 is changed - - inventory_pwd_add_tenant_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_4_random3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_4_random3 is changed - - inventory_pwd_add_ap_second_active_host_4_random3.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_second_active_host_4_random3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_4_random4 is changed - - inventory_pwd_delete_tenant_last_active_host_4_random4.current == [] - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4_random4.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_last_active_host_4_random4 is changed - - inventory_pwd_add_tenant_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_4_random4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_4_random4 is changed - - inventory_pwd_add_ap_last_active_host_4_random4.httpapi_logs.0.1 == "Setting credential authentication from inventory" - - inventory_pwd_add_ap_last_active_host_4_random4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - ## Plugin Tests using session_key from inventory (Part2) ## - - name: Set Single active host in inventory - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - output_level: debug - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_1 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_2 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_2 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_2 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_3 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_3 - - - name: Add a new AP on single active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_3 - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_4 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_4 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_4 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_5 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_5 - - - name: Add a new AP on single active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_5 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_6 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_6 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_6 - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_7 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_7 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_7 - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_8 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_8 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_8 - - - name: Verify operations on single active host - assert: - that: - - task_pwd_delete_tenant_single_active_host_1.current == [] - - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1 is changed - - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_1 is changed - - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_2 is changed - - task_cert_delete_tenant_single_active_host_2.current == [] - - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2 is changed - - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_2 is changed - - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_single_active_host_3 is changed - - task_pwd_delete_tenant_single_active_host_3.current == [] - - task_pwd_delete_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_3 is changed - - task_pwd_add_tenant_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_3 is changed - - task_pwd_add_ap_single_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_4 is changed - - inventory_pwd_delete_tenant_single_active_host_4.current == [] - - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4 is changed - - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_4 is changed - - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_single_active_host_5 is changed - - task_pwd_delete_tenant_single_active_host_5.current == [] - - task_pwd_delete_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_5 is changed - - task_pwd_add_tenant_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_5 is changed - - task_pwd_add_ap_single_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_6 is changed - - task_cert_delete_tenant_single_active_host_6.current == [] - - task_cert_delete_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_6 is changed - - task_cert_add_tenant_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_6 is changed - - task_cert_add_ap_single_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_7 is changed - - inventory_pwd_delete_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_single_active_host_7.current == [] - - inventory_pwd_add_tenant_single_active_host_7 is changed - - inventory_pwd_add_tenant_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_7 is changed - - inventory_pwd_add_ap_single_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_single_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_8 is changed - - task_cert_delete_tenant_single_active_host_8.current == [] - - task_cert_delete_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_8 is changed - - task_cert_add_tenant_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_8 is changed - - task_cert_add_ap_single_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - -###################################################################### - - - name: Set Single inactive host in inventory - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - -############################################################################### - - - name: Set the inventory with the first host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_1 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_1 - - - name: Add a new AP on the first active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_1 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_2 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_2 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_2 - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_3 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_3 - - - name: Add a new AP on the first active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_3 - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_4 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_4 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_4 - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_5 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_5 - - - name: Add a new AP on the first active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_5 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_6 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_6 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_6 - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_7 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_7 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_7 - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_8 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_8 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_8 - - - name: Verify operations on first active host - assert: - that: - - task_pwd_delete_tenant_first_active_host_1.current == [] - - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1 is changed - - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_1 is changed - - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_2 is changed - - task_cert_delete_tenant_first_active_host_2.current == [] - - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2 is changed - - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_2 is changed - - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_3 is changed - - task_pwd_delete_tenant_first_active_host_3.current == [] - - task_pwd_delete_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_3 is changed - - task_pwd_add_tenant_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_3 is changed - - task_pwd_add_ap_first_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_4 is changed - - inventory_pwd_delete_tenant_first_active_host_4.current == [] - - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4 is changed - - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_4 is changed - - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_5 is changed - - task_pwd_delete_tenant_first_active_host_5.current == [] - - task_pwd_delete_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_5 is changed - - task_pwd_add_tenant_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_5 is changed - - task_pwd_add_ap_first_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_6 is changed - - task_cert_delete_tenant_first_active_host_6.current == [] - - task_cert_delete_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_6 is changed - - task_cert_add_tenant_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_6 is changed - - task_cert_add_ap_first_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_7 is changed - - inventory_pwd_delete_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_first_active_host_7.current == [] - - inventory_pwd_add_tenant_first_active_host_7 is changed - - inventory_pwd_add_tenant_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_7 is changed - - inventory_pwd_add_ap_first_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_first_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_8 is changed - - task_cert_delete_tenant_first_active_host_8.current == [] - - task_cert_delete_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_8 is changed - - task_cert_add_tenant_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_8 is changed - - task_cert_add_ap_first_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################## - - - name: Set the inventory with the second host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_1 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_1 - - - name: Add a new AP on the second active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_1 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_2 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_2 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_2 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_3 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_3 - - - name: Add a new AP on the second active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_3 - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_4 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_4 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_4 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_5 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_5 - - - name: Add a new AP on the second active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_5 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_6 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_6 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_6 - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_7 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_7 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_7 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_8 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_8 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_8 - - - name: Verify operations on second active host - assert: - that: - - task_pwd_delete_tenant_second_active_host_1.current == [] - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_1 is changed - - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_1 is changed - - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_2 is changed - - task_cert_delete_tenant_second_active_host_2.current == [] - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_2 is changed - - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_2 is changed - - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_3 is changed - - task_pwd_delete_tenant_second_active_host_3.current == [] - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_3 is changed - - task_pwd_add_tenant_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_3 is changed - - task_pwd_add_ap_second_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_4 is changed - - inventory_pwd_delete_tenant_second_active_host_4.current == [] - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_second_active_host_4 is changed - - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_4 is changed - - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_5 is changed - - task_pwd_delete_tenant_second_active_host_5.current == [] - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_5 is changed - - task_pwd_add_tenant_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_5 is changed - - task_pwd_add_ap_second_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_6 is changed - - task_cert_delete_tenant_second_active_host_6.current == [] - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_6 is changed - - task_cert_add_tenant_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_6 is changed - - task_cert_add_ap_second_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_7 is changed - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_delete_tenant_second_active_host_7.current == [] - - inventory_pwd_add_tenant_second_active_host_7 is changed - - inventory_pwd_add_tenant_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_7 is changed - - inventory_pwd_add_ap_second_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_second_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_8 is changed - - task_cert_delete_tenant_second_active_host_8.current == [] - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_8 is changed - - task_cert_add_tenant_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_8 is changed - - task_cert_add_ap_second_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################### - - - name: Set the inventory with the last host in the list as active - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_1 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_1 - - - name: Add a new AP on the last active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_1 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_2 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_2 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_2 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_3 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_3 - - - name: Add a new AP on the last active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_3 - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_4 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_4 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_4 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_5 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_5 - - - name: Add a new AP on the last active host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_5 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_6 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_6 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_6 - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_7 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_7 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_7 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_8 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_8 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_8 - - - name: Verify operations on last active host - assert: - that: - - task_pwd_delete_tenant_last_active_host_1.current == [] - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_1 is changed - - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_1 is changed - - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_2 is changed - - task_cert_delete_tenant_last_active_host_2.current == [] - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_2 is changed - - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_2 is changed - - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_3 is changed - - task_pwd_delete_tenant_last_active_host_3.current == [] - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_3.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_3 is changed - - task_pwd_add_tenant_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_3.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_3.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_3 is changed - - task_pwd_add_ap_last_active_host_3.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_4 is changed - - inventory_pwd_delete_tenant_last_active_host_4.current == [] - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_last_active_host_4 is changed - - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_4 is changed - - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_5 is changed - - task_pwd_delete_tenant_last_active_host_5.current == [] - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_5.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_5 is changed - - task_pwd_add_tenant_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_5.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_5 is changed - - task_pwd_add_ap_last_active_host_5.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_5.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_6 is changed - - task_cert_delete_tenant_last_active_host_6.current == [] - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_6.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_6 is changed - - task_cert_add_tenant_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_6.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_6 is changed - - task_cert_add_ap_last_active_host_6.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_6.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_7 is changed - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_7.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_delete_tenant_last_active_host_7.current == [] - - inventory_pwd_add_tenant_last_active_host_7 is changed - - inventory_pwd_add_tenant_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_7.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_7 is changed - - inventory_pwd_add_ap_last_active_host_7.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_last_active_host_7.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_8 is changed - - task_cert_delete_tenant_last_active_host_8.current == [] - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_8.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_8 is changed - - task_cert_add_tenant_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_8.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_8 is changed - - task_cert_add_ap_last_active_host_8.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_8.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# ############################################################################### - - - name: Set the inventory with all the hosts in the inventory as inactive - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - -############################################################################################# -#### Random Tets - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_single_active_host_1 - - - name: Delete Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_single_active_host_1 - - - name: Add Tenant on single active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_single_active_host_1 - - - name: Add a new AP on single active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_single_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_single_active_host_1 - - - name: Delete Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with password - cisco.aci.aci_ap: - <<: *aci_info_single_host_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_first_active_host_1 - - - name: Add Tenant on the first active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active - tenant: ansible_test - state: present - register: task_pwd_add_tenant_first_active_host_1 - - - name: Add a new AP on the first active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_first_host_active - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_first_active_host_1 - - - name: Delete Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_second_active_host_1 - - - name: Add Tenant on the second active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_second_active_host_1 - - - name: Add a new AP on the second active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_second_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_second_active_host_1 - - - name: Delete Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: absent - register: task_pwd_delete_tenant_last_active_host_1 - - - name: Add Tenant on the last active host present in the task with password - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host - tenant: ansible_test - state: present - register: task_pwd_add_tenant_last_active_host_1 - - - name: Add a new AP on the last active host present in the task password - cisco.aci.aci_ap: - <<: *aci_info_last_active_host - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_pwd_add_ap_last_active_host_1 - - - name: Delete Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts credential - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts credential - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_single_active_host_2 - - - name: Add Tenant on single active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_single_active_host_2 - - - name: Add a new AP on single active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_single_active_host_2 - - - name: Delete Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_single_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Delete Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_first_active_host_2 - - - name: Add Tenant on the first active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_first_active_host_2 - - - name: Add a new AP on the first active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_first_host_active_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_first_active_host_2 - - - name: Delete Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_second_active_host_2 - - - name: Add Tenant on the second active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_second_active_host_2 - - - name: Add a new AP on the second active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_second_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_second_active_host_2 - - - name: Delete Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - register: task_cert_delete_tenant_last_active_host_2 - - - name: Add Tenant on the last active host present in the task with certificate - cisco.aci.aci_tenant: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - register: task_cert_add_tenant_last_active_host_2 - - - name: Add a new AP on the last active host present in the task with certificate - cisco.aci.aci_ap: - <<: *aci_info_last_active_host_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - register: task_cert_add_ap_last_active_host_2 - - - name: Delete Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via task hosts certificate - cisco.aci.aci_tenant: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via task hosts certificate - cisco.aci.aci_ap: - <<: *aci_info_all_hosts_inactive_certificate - certificate_name: admin - private_key: "{{ lookup('file', 'certs/admin.key') }}" - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Set hosts 1 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_single_active_host_4_random1 - - - name: Add Tenant on single active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_single_active_host_4_random1 - - - name: Add a new AP on single active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_single_active_host_4_random1 - - - name: Set hosts 2 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant on single inactive host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP on single inactive host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Set hosts 3 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_first_active_host_4_random3 - - - name: Add Tenant on the first active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_first_active_host_4_random3 - - - name: Add a new AP on the first active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_first_active_host_4_random3 - - - name: Set hosts 4 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,173.36.219.72,161.65.67.15,100.139.68.211,124.145.225.187,161.65.67.15,161.46.181.230 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_second_active_host_4_random4 - - - name: Add Tenant on the second active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_second_active_host_4_random4 - - - name: Add a new AP on the second active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_second_active_host_4_random4 - - - name: Set hosts 5 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,89.208.146.34,128.228.131.123,100.139.68.211,124.145.225.187,173.36.219.72 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - register: inventory_pwd_delete_tenant_last_active_host_4_random5 - - - name: Add Tenant on the last active host present in the inventory with password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - register: inventory_pwd_add_tenant_last_active_host_4_random5 - - - name: Add a new AP on the last active host present in the inventory with password - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - register: inventory_pwd_add_ap_last_active_host_4_random5 - - - name: Set hosts 6 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: 124.145.225.187,161.65.67.15,100.139.68.211,89.208.146.34 - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'admin': '{{ role_path }}/tasks/certs/admin.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: present - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - ## HTTP Error - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - - - name: Add a new AP via inventory host - cisco.aci.aci_ap: - output_level: debug - tenant: ansible_test - ap: ap - description: default ap - state: present - ignore_errors: yes - - - name: Verify random operations - assert: - that: - - task_pwd_delete_tenant_single_active_host_1.current == [] - - task_pwd_delete_tenant_second_single_active_host_1.current == [] - - task_pwd_delete_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1 is changed - - task_pwd_add_tenant_second_single_active_host_1 is changed - - task_pwd_add_tenant_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_tenant_second_single_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_single_active_host_1 is changed - - task_pwd_add_ap_second_single_active_host_1 is changed - - task_pwd_add_ap_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_single_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_add_ap_second_single_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_first_active_host_1.current == [] - - task_pwd_delete_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1 is changed - - task_pwd_add_tenant_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_first_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_first_active_host_1 is changed - - task_pwd_add_ap_first_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_first_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_second_active_host_1.current == [] - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_second_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_second_active_host_1 is changed - - task_pwd_add_tenant_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_second_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_second_active_host_1 is changed - - task_pwd_add_ap_second_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_second_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_pwd_delete_tenant_last_active_host_1.current == [] - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.5.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.11.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.17.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.23.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_pwd_delete_tenant_last_active_host_1.httpapi_logs.29.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_pwd_add_tenant_last_active_host_1 is changed - - task_pwd_add_tenant_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_tenant_last_active_host_1.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_pwd_add_ap_last_active_host_1 is changed - - task_pwd_add_ap_last_active_host_1.httpapi_logs.0.1 == "Setting credential authentication at task level" - - task_pwd_add_ap_last_active_host_1.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_single_active_host_2 is changed - - task_cert_delete_tenant_single_active_host_2.current == [] - - task_cert_delete_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2 is changed - - task_cert_add_tenant_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_single_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_single_active_host_2 is changed - - task_cert_add_ap_single_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_single_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_first_active_host_2 is changed - - task_cert_delete_tenant_first_active_host_2.current == [] - - task_cert_delete_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2 is changed - - task_cert_add_tenant_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_first_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_first_active_host_2 is changed - - task_cert_add_ap_first_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_first_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_second_active_host_2 is changed - - task_cert_delete_tenant_second_active_host_2.current == [] - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_second_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_second_active_host_2 is changed - - task_cert_add_tenant_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_second_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_second_active_host_2 is changed - - task_cert_add_ap_second_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_second_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - task_cert_delete_tenant_last_active_host_2 is changed - - task_cert_delete_tenant_last_active_host_2.current == [] - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - task_cert_delete_tenant_last_active_host_2.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - task_cert_add_tenant_last_active_host_2 is changed - - task_cert_add_tenant_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_tenant_last_active_host_2.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - task_cert_add_ap_last_active_host_2 is changed - - task_cert_add_ap_last_active_host_2.httpapi_logs.0.1 == "Setting certificate authentication at task level" - - task_cert_add_ap_last_active_host_2.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_single_active_host_4 is changed - - inventory_pwd_delete_tenant_single_active_host_4.current == [] - - inventory_pwd_delete_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4 is changed - - inventory_pwd_add_tenant_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_single_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_single_active_host_4 is changed - - inventory_pwd_add_ap_single_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_single_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_first_active_host_4 is changed - - inventory_pwd_delete_tenant_first_active_host_4.current == [] - - inventory_pwd_delete_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4 is changed - - inventory_pwd_add_tenant_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_first_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_first_active_host_4 is changed - - inventory_pwd_add_ap_first_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_first_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_second_active_host_4 is changed - - inventory_pwd_delete_tenant_second_active_host_4.current == [] - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_second_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_second_active_host_4 is changed - - inventory_pwd_add_tenant_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_second_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_second_active_host_4 is changed - - inventory_pwd_add_ap_second_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_second_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - - inventory_pwd_delete_tenant_last_active_host_4 is changed - - inventory_pwd_delete_tenant_last_active_host_4.current == [] - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.3.1 == "Switching host from 124.145.225.187 to 89.208.146.34" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.8.1 == "Switching host from 89.208.146.34 to 128.228.131.123" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.13.1 == "Switching host from 128.228.131.123 to 100.139.68.211" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.18.1 == "Switching host from 100.139.68.211 to 124.145.225.187" - - inventory_pwd_delete_tenant_last_active_host_4.httpapi_logs.23.1 == "Switching host from 124.145.225.187 to 173.36.219.72" - - inventory_pwd_add_tenant_last_active_host_4 is changed - - inventory_pwd_add_tenant_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_tenant_last_active_host_4.current.0.fvTenant.attributes.dn == "uni/tn-ansible_test" - - inventory_pwd_add_ap_last_active_host_4 is changed - - inventory_pwd_add_ap_last_active_host_4.httpapi_logs.0.1 == "Setting certificate authentication from inventory" - - inventory_pwd_add_ap_last_active_host_4.current.0.fvAp.attributes.dn == "uni/tn-ansible_test/ap-ap" - -# Invalid session_key - - name: Set host 7 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/admin_invalid.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - -# Non existing session_key - - name: Set host 8 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "{{ ansible_password }}" - ansible_httpapi_session_key: {'ansible_github_ci': '{{ role_path }}/tasks/certs/non_existing.key'} - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant via inventory host - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - -# Incorrect Password - - name: Set host 9 - ansible.builtin.add_host: - name: inventory_hosts - ansible_host: "{{5|cisco.aci.generate_random_ips(0)}}" - ansible_user: "{{ ansible_user }}" - ansible_password: "incorrect_password" - ansible_command_timeout: 5 - ansible_connection: "{{ ansible_connection }}" - ansible_network_os: "{{ ansible_network_os }}" - ansible_httpapi_validate_certs: "{{ ansible_httpapi_validate_certs }}" - ansible_httpapi_use_ssl: "{{ ansible_httpapi_use_ssl }}" - ansible_httpapi_use_proxy: "{{ ansible_httpapi_use_proxy }}" - - - name: Delete Tenant with incorrect password - cisco.aci.aci_tenant: - output_level: debug - tenant: ansible_test - state: absent - ignore_errors: yes - -# Clean up Environment - - name: Delete a tenant using an XML string - cisco.aci.aci_rest: - <<: *aci_info_single_host_active - path: /api/mo/uni/tn-[Sales].xml - method: delete - content: '' - ignore_errors: yes - - - name: Remove user certificate - cisco.aci.aci_aaa_user_certificate: - <<: *aci_info_single_host_active - aaa_user: "{{ ansible_user }}" - name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" - state: absent - ignore_errors: yes - - - name: Delete Tenant - cisco.aci.aci_tenant: - <<: *aci_info_single_host_active - tenant: ansible_test - state: absent - ignore_errors: yes diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index e7ec1e68d..4738e2eb3 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -9,9 +9,8 @@ msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined -- include_tasks: httpapi_inventory_password.yml - tags: httpapi_inventory_password - when: inventory_hostname == play_hosts[6] +- include_tasks: httpapi_connection.yml + tags: httpapi_connection - name: Delete old log files to clean test directory file: From c0065f4fde27cbc2c2e9dbeb8b5d4f97f0a3bd69 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 10 May 2023 11:49:10 -0400 Subject: [PATCH 37/49] [ignore_changes] Changes made to test files for intergration tests --- plugins/httpapi/aci.py | 29 +- plugins/module_utils/aci.py | 54 ++- plugins/modules/aci_config_rollback.py | 2 +- plugins/modules/aci_config_snapshot.py | 4 +- plugins/modules/aci_rest.py | 5 +- tests/integration/inventory.networking | 8 +- .../aci_aaa_user_certificate/tasks/main.yml | 82 +++- .../targets/aci_dns_domain/tasks/main.yml | 10 - .../targets/aci_dns_provider/tasks/main.yml | 10 - .../aci_interface_blacklist/tasks/main.yml | 34 ++ .../aci_interface_config/tasks/main.yml | 18 +- .../targets/aci_rest/tasks/error_handling.yml | 2 +- .../targets/aci_rest/tasks/json_inline.yml | 10 - .../aci_static_binding_to_epg/tasks/main.yml | 2 +- .../aci_tenant/{tasks/certs => pki}/admin.crt | 0 .../aci_tenant/{tasks/certs => pki}/admin.key | 0 .../{tasks/certs => pki}/admin_invalid.key | 0 .../targets/aci_tenant/pki/openssh_rsa.key | 16 + .../targets/aci_tenant/pki/rsa_ansible.key | 15 + .../targets/aci_tenant/pki/rsa_user.crt | 14 + .../targets/aci_tenant/pki/rsa_user.key | 16 + .../aci_tenant/tasks/httpapi_connection.yml | 49 +- .../targets/aci_tenant/tasks/main.yml | 19 - .../aci_tenant_span_dst_group/tasks/main.yml | 440 +++++++++--------- 24 files changed, 503 insertions(+), 336 deletions(-) rename tests/integration/targets/aci_tenant/{tasks/certs => pki}/admin.crt (100%) rename tests/integration/targets/aci_tenant/{tasks/certs => pki}/admin.key (100%) rename tests/integration/targets/aci_tenant/{tasks/certs => pki}/admin_invalid.key (100%) create mode 100644 tests/integration/targets/aci_tenant/pki/openssh_rsa.key create mode 100644 tests/integration/targets/aci_tenant/pki/rsa_ansible.key create mode 100644 tests/integration/targets/aci_tenant/pki/rsa_user.crt create mode 100644 tests/integration/targets/aci_tenant/pki/rsa_user.key diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index af24eeb2e..5f22e5040 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -96,8 +96,9 @@ def login(self, username, password): "Cookie": "APIC-Cookie={0}".format(self._response_to_json(response_value).get("imdata")[0]["aaaLogin"]["attributes"]["token"]) } self.connection.queue_message("debug", "Connection to {0} was successful".format(self.connection.get_option("host"))) - except Exception: + except Exception as exc_login: self.connection._connected = False + exc_login.path = path raise def logout(self): @@ -109,7 +110,7 @@ def logout(self): response, response_data = self.connection.send(path, data, method=method) except Exception as exc_logout: msg = "Error on attempt to logout from APIC. {0}".format(exc_logout) - raise ConnectionError(self._return_info(None, method, path, msg)) + raise ConnectionError(self._return_info("", method, path, msg)) self.connection._auth = None self._verify_response(response, method, path, response_data) @@ -213,8 +214,10 @@ def send_request(self, method, path, data): if len(self.backup_hosts) == 0: self.provided_hosts = None self.connection._connected = False - msg = str("No hosts left in the cluster to continue operation! Error on final host {0}: {1}".format(self.connection.get_option("host"), exc_response)) - return self._return_info("", method, self.validate_url(self.connection._url+path), msg) + error = dict(code=-1, text="No hosts left in the cluster to continue operation! Error on final host {0}".format(self.connection.get_option("host"))) + if 'path' in dir(exc_response): + path = exc_response.path + return self._return_info("", method, self.validate_url(self.connection._url+path), str(exc_response), error=error) else: self.current_host = self.backup_hosts.pop(0) self.connection.queue_message( @@ -240,15 +243,15 @@ def handle_httperror(self, exc): return exc def validate_url(self, url): - return re.match(r'^.*?\.json|^.*?\.xml', url).group(0) + validated_url = re.match(r'^.*?\.json|^.*?\.xml', url).group(0) + if self.connection_parameters.get("port") is None: + return validated_url.replace(re.match(r'(https?:\/\/.*)(:\d*)\/?(.*)',url).group(2),"") + else: + return validated_url def _verify_response(self, response, method, path, response_data): """Process the return code and response object from APIC""" response_value = self._get_response_value(response_data) - if path.find(".json") != -1: - respond_data = self._response_to_json(response_value) - else: - respond_data = response_value response_code = response.getcode() path = self.validate_url(response.url) # Response check to remain consistent with fetch_url's response @@ -256,7 +259,7 @@ def _verify_response(self, response, method, path, response_data): msg = "{0}".format(response) else: msg = "{0} ({1} bytes)".format(response.msg, len(response_value)) - return self._return_info(response_code, method, path, msg, respond_data) + return self._return_info(response_code, method, path, msg, respond_data=response_value) def _get_response_value(self, response_data): """Extract string data from response_data returned from APIC""" @@ -270,13 +273,15 @@ def _response_to_json(self, response_text): except Exception: return "Invalid JSON response: {0}".format(response_text) - def _return_info(self, response_code, method, path, msg, respond_data=None): + def _return_info(self, response_code, method, path, msg, respond_data=None, error={}): """Format success/error data and return with consistent format""" info = {} info["status"] = response_code info["method"] = method info["url"] = path info["msg"] = msg + info["error"] = error + # Response check to trigger key error if response_data is invalid if respond_data is not None: info["body"] = respond_data return info @@ -320,7 +325,7 @@ def cert_auth(self, method, path, payload=""): self.connection_parameters["certificate_name"] = os.path.basename(os.path.splitext(self.connection_parameters.get("private_key"))[0]) else: raise ConnectionError( - "Provided private key {0} does not appear to be a private key.".format(self.connection_parameters.get("private_key")) + "Provided private key {0} does not appear to be a private key or provided file does not exist.".format(self.connection_parameters.get("private_key")) ) if self.connection_parameters.get("certificate_name") is None: self.connection_parameters["certificate_name"] = self.connection.get_option("remote_user") diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 47201f96b..3f374adb7 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -48,7 +48,6 @@ import json import os from copy import deepcopy -from urllib.parse import urlparse from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_bytes, to_native @@ -89,6 +88,13 @@ except ImportError: HAS_XMLJSON_COBRA = False +try: + from ansible.module_utils.six.moves.urllib.parse import urlparse + + HAS_URLPARSE = True +except Exception: + HAS_URLPARSE = False + def aci_argument_spec(): return dict( @@ -309,6 +315,7 @@ def __init__(self, module): self.result = dict(changed=False) self.headers = dict() self.child_classes = set() + self.connection = None # error output self.error = dict(code=None, text=None) @@ -331,7 +338,6 @@ def __init__(self, module): self.response = None self.status = None self.url = None - self.connection = None self.httpapi_logs = list() # aci_rest output @@ -400,7 +406,7 @@ def define_method(self): self.params["method"] = state_map.get(self.params.get("state")) def get_connection(self): - if self.connection: + if self.connection is not None: return self.connection if self.module._socket_path: @@ -424,7 +430,7 @@ def login(self): } } } - resp, auth = self.api_call("POST", url, data=json.dumps(payload), output=True) + resp, auth = self.api_call("POST", url, data=json.dumps(payload), return_response=True) # Handle APIC response if auth.get("status") != 200: @@ -490,7 +496,7 @@ def cert_auth(self, path=None, payload="", method=None): if self.params.get("certificate_name") is None: self.params["certificate_name"] = os.path.basename(os.path.splitext(self.params.get("private_key"))[0]) else: - self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params) + self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key or provided file does not exist." % self.params) if self.params.get('certificate_name') is None: self.params['certificate_name'] = self.params.get('username', 'admin') @@ -1146,7 +1152,7 @@ def delete_config(self): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes - self.api_call("DELETE", self.url, None, output=False) + self.api_call("DELETE", self.url, None, return_response=False) else: self.result["changed"] = True self.method = "DELETE" @@ -1269,7 +1275,7 @@ def get_existing(self): """ uri = self.url + self.filter_string - self.api_call("GET", uri, data=None, output=False) + self.api_call("GET", uri, data=None, return_response=False) @staticmethod def get_nested_config(proposed_child, existing_children): @@ -1390,6 +1396,7 @@ def post_config(self, parent_class=None): <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD url = self.url if parent_class is not None: @@ -1445,6 +1452,9 @@ def post_config(self, parent_class=None): ======= self.api_call("POST", self.url, json.dumps(self.config), output=False) >>>>>>> f20fdfe ([ignore_changes] New test file created) +======= + self.api_call("POST", self.url, json.dumps(self.config), return_response=False) +>>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) else: self.result["changed"] = True self.method = "POST" @@ -1471,7 +1481,7 @@ def exit_json(self, filter_existing=None, **kwargs): self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url - if self.httpapi_logs: + if self.httpapi_logs is not None: self.result["httpapi_logs"] = self.httpapi_logs if self.stdout: self.result["stdout"] = self.stdout @@ -1502,12 +1512,13 @@ def fail_json(self, msg, **kwargs): if self.error.get("code") is not None and self.error.get("text") is not None: self.result["error"] = self.error + if self.stdout: + self.result["stdout"] = self.stdout + if "state" in self.params: if self.params.get("state") in ("absent", "present"): if self.params.get("output_level") in ("debug", "info"): self.result["previous"] = self.existing - if self.stdout: - self.result["stdout"] = self.stdout # Return the gory details when we need it if self.params.get("output_level") == "debug": @@ -1524,7 +1535,7 @@ def fail_json(self, msg, **kwargs): self.result["response"] = self.response self.result["status"] = self.status self.result["url"] = self.url - if self.httpapi_logs: + if self.httpapi_logs is not None: self.result["httpapi_logs"] = self.httpapi_logs if "state" in self.params: @@ -1563,6 +1574,7 @@ def dump_json(self): <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD def delete_config_request(self, path): self._config_request(path, "absent") @@ -1618,16 +1630,26 @@ def api_call(self, method, path, url, data=None, output=False): ======= def api_call(self, method, url, data=None, output=False): >>>>>>> f20fdfe ([ignore_changes] New test file created) +======= + def parsed_url_path(self, url): + parse_result = urlparse(url) + if parse_result.query == '': + return parse_result.path + else: + return parse_result.path + '?' + parse_result.query + + def api_call(self, method, url, data=None, return_response=False): +>>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) resp = None - path = urlparse(url).path if self.get_connection() is not None: self.get_connection().set_params(self.params) - info = self.get_connection().send_request(method, path, data) + info = self.get_connection().send_request(method, self.parsed_url_path(url), data) self.url = info.get("url") + self.error = info.get("error") self.httpapi_logs.extend(self.get_connection().pop_messages()) else: if self.params.get("private_key"): - self.cert_auth(path=path, payload=data, method=method) + self.cert_auth(path=self.parsed_url_path(url), payload=data, method=method) resp, info = fetch_url( self.module, url, @@ -1642,7 +1664,7 @@ def api_call(self, method, url, data=None, output=False): self.status = info.get("status") self.method = method - if output: + if return_response: return resp, info else: <<<<<<< HEAD @@ -1674,7 +1696,7 @@ def api_call(self, method, url, data=None, output=False): self.response_json(resp.read()) except AttributeError: if method == "GET": - self.existing = info["body"]["imdata"] + self.existing = json.loads(info.get("body"))["imdata"] else: self.response_json(info.get("body")) else: diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 568e65272..582c1ecf3 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -304,7 +304,7 @@ def get_preview(aci): """ uri = aci.url + aci.filter_string - resp, info = aci.api_call('GET', uri, data=None, output=True) + resp, info = aci.api_call('GET', uri, data=None, return_response=True) # Handle APIC response if info.get("status") == 200: diff --git a/plugins/modules/aci_config_snapshot.py b/plugins/modules/aci_config_snapshot.py index 09b3f6ddb..b147cefe3 100644 --- a/plugins/modules/aci_config_snapshot.py +++ b/plugins/modules/aci_config_snapshot.py @@ -309,11 +309,11 @@ def main(): port_url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") else: port_url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") - resp, info = aci.api_call('GET', port_url, data=None, output=True) + resp, info = aci.api_call('GET', port_url, data=None, return_response=True) try: aci.imdata = json.loads(resp.read())["imdata"] except AttributeError: - aci.imdata = info["body"]["imdata"] + aci.imdata = json.loads(info.get("body"))["imdata"] aci.result["job_details"] = aci.imdata[0].get("configJobCont", {}) # Reset state and url to display correct in output and trigger get_existing() function with correct url diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 74544b4af..28cfb80d0 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -405,19 +405,18 @@ def main(): else: aci.url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") if aci.params.get("method") != "get" and not rsp_subtree_preserve: - #path += "?rsp-subtree=modified" aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) method = aci.params.get("method").upper() # Perform request - resp, info = aci.api_call(method, aci.url, data=payload, output=True) + resp, info = aci.api_call(method, aci.url, data=payload, return_response=True) # Report failure if info.get("status") != 200: try: # APIC error - aci.response_type(info.get("body"), rest_type) + aci.response_type(info["body"], rest_type) aci.fail_json(msg="APIC Error %(code)s: %(text)s" % aci.error) except KeyError: # Connection error diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index c1ca354a7..0c1c59744 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -9,12 +9,6 @@ azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=a [aci:vars] aci_username=ansible_github_ci aci_password="sJ94G92#8dq2hx*K4qh" -ansible_user=ansible_github_ci -ansible_password="sJ94G92#8dq2hx*K4qh" ansible_network_os=cisco.aci.aci -ansible_connection=local -ansible_httpapi_validate_certs=False -ansible_httpapi_use_ssl=True -ansible_httpapi_use_proxy=True -ansible_httpapi_port=443 +ansible_connection=ansible.netcommon.httpapi ansible_python_interpreter=/usr/bin/python3.9 diff --git a/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml b/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml index 9d300e4d7..6ed63c2d1 100644 --- a/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml @@ -300,13 +300,13 @@ state: present - name: Remove user ansible_test - aci_aaa_user: - <<: *aci_info - aaa_user: ansible_test - state: absent + cisco.aci.aci_aaa_user: + <<: *aci_info + aaa_user: ansible_test + state: absent - name: Add user ansible_test - aci_aaa_user: + cisco.aci.aci_aaa_user: <<: *aci_info aaa_user: ansible_test aaa_password: ansible_5351 @@ -315,7 +315,7 @@ state: present - name: Add user certificate - aci_aaa_user_certificate: + cisco.aci.aci_aaa_user_certificate: <<: *aci_info aaa_user: ansible_test name: test @@ -323,10 +323,8 @@ state: present - name: Query test certificate - aci_aaa_user_certificate: - host: "{{ aci_hostname }}" - username: "{{ aci_username }}" - validate_certs: '{{ aci_validate_certs | default(false) }}' + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info certificate_name: admin private_key: '{{ role_path }}/pki/admin.key' aaa_user: ansible_test @@ -334,13 +332,75 @@ state: query register: query_test +- name: Query test certificate with a private key file and no certificate name + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + private_key: '{{ role_path }}/pki/admin.key' + aaa_user: ansible_test + name: test + state: query + register: query_test_pk_file + +- name: Query test certificate with a non existent key + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + private_key: '{{ role_path }}/pki/non_existent.key' + aaa_user: ansible_test + name: test + state: query + register: query_test_non_existent_key + ignore_errors: true + +- name: Query test certificate with private key content and no certificate name present + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + private_key: | + -----BEGIN PRIVATE KEY----- + MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKIRv+2sbbewm0mj + D+6/tpoUymzYIdFsN+gu02teIr/lZi8ipEB514pyhoaerstzboPteWvniLuwq4KQ + VTEHgoln7J8EaHCnECViGA61pVx8RkJ99cmCkepspROw3I96zBcm58oXs6+Q/BnD + /OWET5sBvR9oTv9GNRVJ1rvSMAEJAgMBAAECgYByu3QO0qF9h7X3JEu0Ld4cKBnB + giQ2uJC/et7KxIJ/LOvw9GopBthyt27KwG1ntBkJpkTuAaQHkyNns7vLkNB0S0IR + +owVFEcKYq9VCHTaiQU8TDp24gN+yPTrpRuH8YhDVq5SfVdVuTMgHVQdj4ya4VlF + Gj+a7+ipxtGiLsVGrQJBAM7p0Fm0xmzi+tBOASUAcVrPLcteFIaTBFwfq16dm/ON + 00Khla8Et5kMBttTbqbukl8mxFjBEEBlhQqb6EdQQ0sCQQDIhHx1a9diG7y/4DQA + 4KvR3FCYwP8PBORlSamegzCo+P1OzxiEo0amX7yQMA5UyiP/kUsZrme2JBZgna8S + p4R7AkEAr7rMhSOPUnMD6V4WgsJ5g1Jp5kqkzBaYoVUUSms5RASz4+cwJVCwTX91 + Y1jcpVIBZmaaY3a0wrx13ajEAa0dOQJBAIpjnb4wqpsEh7VpmJqOdSdGxb1XXfFQ + sA0T1OQYqQnFppWwqrxIL+d9pZdiA1ITnNqyvUFBNETqDSOrUHwwb2cCQGArE+vu + ffPUWQ0j+fiK+covFG8NL7H+26NSGB5+Xsn9uwOGLj7K/YT6CbBtr9hJiuWjM1Al + 0V4ltlTuu2mTMaw= + -----END PRIVATE KEY----- + aaa_user: ansible_test + name: test + state: query + register: query_test_pk_content + - name: Verify query_test assert: that: - query_test is not changed + - query_test_pk_file is not changed + - query_test_non_existent_key.response == "Provided private key ******** does not appear to be a private key or provided file does not exist." + - query_test_pk_content is not changed + +# Cleanup environment +- name: Remove test certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + aaa_user: ansible_test + name: test + state: absent - name: Remove user to clean environment for next test on ci - aci_aaa_user: + cisco.aci.aci_aaa_user: <<: *aci_info aaa_user: ansible_test state: absent + +- name: Remove test certificate + cisco.aci.aci_aaa_user_certificate: + <<: *aci_info + aaa_user: '{{ aci_username }}' + name: admin + state: absent diff --git a/tests/integration/targets/aci_dns_domain/tasks/main.yml b/tests/integration/targets/aci_dns_domain/tasks/main.yml index 2d3de4dd0..af9c1026b 100644 --- a/tests/integration/targets/aci_dns_domain/tasks/main.yml +++ b/tests/integration/targets/aci_dns_domain/tasks/main.yml @@ -26,7 +26,6 @@ <<: *aci_info profile_name: ansible_dns_profile state: absent - delegate_to: localhost # ADD DNS PROFILE - name: Add DNS profile @@ -34,7 +33,6 @@ <<: *aci_info profile_name: ansible_dns_profile state: present - delegate_to: localhost # ADD DNS DOMAIN - name: Add a new DNS domain @@ -44,7 +42,6 @@ domain: example.com default: false state: present - delegate_to: localhost register: add_dns_domain - name: Verify DNS domain creation @@ -64,7 +61,6 @@ domain: example.com default: false state: present - delegate_to: localhost register: add_dns_domain_again - name: Verify DNS domain creation idempotence @@ -83,7 +79,6 @@ domain: example.com default: true state: present - delegate_to: localhost register: update_dns_domain - name: Verify DNS domain update @@ -101,7 +96,6 @@ dns_profile: ansible_dns_profile domain: example.com state: query - delegate_to: localhost register: query_dns_domain - name: Verify DNS domain attributes @@ -118,7 +112,6 @@ <<: *aci_info dns_profile: ansible_dns_profile state: query - delegate_to: localhost register: query_dns_domain_all - name: Verify DNS domain query idempotence @@ -133,7 +126,6 @@ dns_profile: ansible_dns_profile domain: example.com state: absent - delegate_to: localhost register: delete_dns_domain - name: Verify DNS domain deletion @@ -152,7 +144,6 @@ dns_profile: ansible_dns_profile domain: example.com state: absent - delegate_to: localhost register: delete_dns_domain_again - name: Verify DNS domain deletion idempotence @@ -166,4 +157,3 @@ <<: *aci_info profile_name: ansible_dns_profile state: absent - delegate_to: localhost diff --git a/tests/integration/targets/aci_dns_provider/tasks/main.yml b/tests/integration/targets/aci_dns_provider/tasks/main.yml index 8deca651c..3e31d2b52 100644 --- a/tests/integration/targets/aci_dns_provider/tasks/main.yml +++ b/tests/integration/targets/aci_dns_provider/tasks/main.yml @@ -26,7 +26,6 @@ <<: *aci_info profile_name: ansible_dns_profile state: absent - delegate_to: localhost # ADD DNS PROFILE - name: Add DNS profile @@ -34,7 +33,6 @@ <<: *aci_info profile_name: ansible_dns_profile state: present - delegate_to: localhost # ADD DNS PROVIDER - name: Add a new DNS provider @@ -44,7 +42,6 @@ addr: 10.20.30.40 preferred: false state: present - delegate_to: localhost register: add_dns_provider - name: Verify DNS provider creation @@ -64,7 +61,6 @@ addr: 10.20.30.40 preferred: false state: present - delegate_to: localhost register: add_dns_provider_again - name: Verify DNS provider creation idempotence @@ -83,7 +79,6 @@ addr: 10.20.30.40 preferred: true state: present - delegate_to: localhost register: update_dns_provider - name: Verify DNS provider update @@ -101,7 +96,6 @@ dns_profile: ansible_dns_profile addr: 10.20.30.40 state: query - delegate_to: localhost register: query_dns_provider - name: Verify DNS provider attributes @@ -118,7 +112,6 @@ <<: *aci_info dns_profile: ansible_dns_profile state: query - delegate_to: localhost register: query_dns_provider_all - name: Verify DNS provider query idempotence @@ -133,7 +126,6 @@ dns_profile: ansible_dns_profile addr: 10.20.30.40 state: absent - delegate_to: localhost register: delete_dns_provider - name: Verify DNS provider deletion @@ -152,7 +144,6 @@ dns_profile: ansible_dns_profile addr: 10.20.30.40 state: absent - delegate_to: localhost register: delete_dns_provider_again - name: Verify DNS provider deletion idempotence @@ -166,4 +157,3 @@ <<: *aci_info profile_name: ansible_dns_profile state: absent - delegate_to: localhost diff --git a/tests/integration/targets/aci_interface_blacklist/tasks/main.yml b/tests/integration/targets/aci_interface_blacklist/tasks/main.yml index d7b125267..17e0bb67c 100644 --- a/tests/integration/targets/aci_interface_blacklist/tasks/main.yml +++ b/tests/integration/targets/aci_interface_blacklist/tasks/main.yml @@ -45,6 +45,40 @@ - name: Execute tasks only for non-cloud sites when: query_cloud.current == [] # This condition will execute only non-cloud sites block: # block specifies execution of tasks within, based on conditions + + - name: Query blacklisted interfaces + cisco.aci.aci_interface_blacklist: + <<: *aci_info + state: query + register: enable_and_clear + + - name: set regex + set_fact: + regexp: '(topology/pod-)(\d)(/paths-)(\d*)(/pathep-\[eth)(.*)(])' + + - name: Save Target DNs + set_fact: + tdn: "{{ item.fabricRsOosPath.attributes.tDn }}" + loop: "{{ enable_and_clear.current }}" + register: enabled_tdn + + - name: Enable interfaces that were blacklisted + cisco.aci.aci_interface_blacklist: + <<: *aci_info + pod_id: "{{ item.ansible_facts.tdn | regex_search(regexp, '\\2') | first }}" + node_id: "{{ item.ansible_facts.tdn | regex_search(regexp, '\\4') | first }}" + interface: "{{ item.ansible_facts.tdn | regex_search(regexp, '\\6') | first }}" + state: absent + loop: "{{ enabled_tdn.results }}" + + - name: Ensure Interfaces are absent - Clean up test environment + cisco.aci.aci_interface_config: + <<: *aci_info + node: "{{ item.ansible_facts.tdn | regex_search(regexp, '\\4') | first }}" + interface: "{{ item.ansible_facts.tdn | regex_search(regexp, '\\6') | first }}" + state: absent + loop: "{{ enabled_tdn.results }}" + - name: Spine - Clean test environment with enabled interface cisco.aci.aci_interface_blacklist: <<: *aci_info diff --git a/tests/integration/targets/aci_interface_config/tasks/main.yml b/tests/integration/targets/aci_interface_config/tasks/main.yml index 75253bd40..4a3915148 100644 --- a/tests/integration/targets/aci_interface_config/tasks/main.yml +++ b/tests/integration/targets/aci_interface_config/tasks/main.yml @@ -527,7 +527,7 @@ assert: that: - query_all_access_interfaces is not changed - - query_all_access_interfaces.current|length >= 6 + - query_all_access_interfaces.current|length >= 5 - name: Query all fabric interfaces cisco.aci.aci_interface_config: @@ -678,3 +678,19 @@ that: - query_interface_509 is not changed - query_interface_509.current == [] + + - name: Ensure Interfaces 50* are absent - Clean up test environment + cisco.aci.aci_interface_config: + <<: *aci_info + node: "{{ item.node }}" + interface: "{{ item.interface }}" + state: absent + with_items: + - { node: "501", interface: "1/1/1" } + - { node: "502", interface: "2/2/2" } + - { node: "503", interface: "1/1/1" } + - { node: "505", interface: "5/5/5" } + - { node: "506", interface: "6/6/6" } + - { node: "507", interface: "7/7/7" } + - { node: "508", interface: "8/8/8" } + - { node: "509", interface: "9/9/9" } diff --git a/tests/integration/targets/aci_rest/tasks/error_handling.yml b/tests/integration/targets/aci_rest/tasks/error_handling.yml index 1c503c5f2..9ecd7751f 100644 --- a/tests/integration/targets/aci_rest/tasks/error_handling.yml +++ b/tests/integration/targets/aci_rest/tasks/error_handling.yml @@ -27,7 +27,7 @@ assert: that: - error_on_name_resolution is failed - - error_on_name_resolution.msg.startswith("Connection failed for https://foo.bar.cisco.com/api/aaaLogin.json. Request failed:") + - error_on_name_resolution.msg.startswith("Connection failed for https://foo.bar.cisco.com/api/aaaLogin.json.") - "'current' not in error_on_name_resolution" - "'previous' not in error_on_name_resolution" - "'sent' not in error_on_name_resolution" diff --git a/tests/integration/targets/aci_rest/tasks/json_inline.yml b/tests/integration/targets/aci_rest/tasks/json_inline.yml index dd27525f9..731c749c3 100644 --- a/tests/integration/targets/aci_rest/tasks/json_inline.yml +++ b/tests/integration/targets/aci_rest/tasks/json_inline.yml @@ -37,12 +37,10 @@ } } } - delegate_to: localhost register: nm_add_tenant - name: Add tenant again (normal mode) cisco.aci.aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again - name: Verify add_tenant @@ -72,12 +70,10 @@ } } } - delegate_to: localhost register: nm_add_tenant_descr - name: Change description of tenant again (normal mode) cisco.aci.aci_rest: *tenant_changed - delegate_to: localhost register: nm_add_tenant_descr_again - name: Verify add_tenant_descr @@ -89,7 +85,6 @@ # ADD TENANT AGAIN - name: Add tenant again with no description (normal mode) cisco.aci.aci_rest: *tenant_present - delegate_to: localhost register: nm_add_tenant_again_no_descr - name: Verify add_tenant_again_no_descr @@ -109,7 +104,6 @@ output_level: '{{ aci_output_level | default("info") }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_all_tenants - name: Verify query_all_tenants @@ -129,7 +123,6 @@ output_level: '{{ aci_output_level | default("info") }}' path: /api/mo/uni/tn-[ansible_test].json method: get - delegate_to: localhost register: nm_query_tenant - name: Verify query_tenant @@ -140,12 +133,10 @@ # REMOVE TENANT - name: Remove tenant (normal mode) cisco.aci.aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant - name: Remove tenant again (normal mode) cisco.aci.aci_rest: *tenant_absent - delegate_to: localhost register: nm_remove_tenant_again - name: Verify remove_tenant @@ -157,7 +148,6 @@ # QUERY NON-EXISTING TENANT - name: Query non-existing tenant (normal mode) cisco.aci.aci_rest: *tenant_query - delegate_to: localhost register: nm_query_non_tenant - name: Verify query_non_tenant diff --git a/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml b/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml index 180848e28..0e9096b85 100644 --- a/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml +++ b/tests/integration/targets/aci_static_binding_to_epg/tasks/main.yml @@ -306,7 +306,7 @@ - 104 - 105 ignore_errors: true - register: fex_vpc_three extpaths + register: fex_vpc_three_extpaths - name: Bind static-binding to epg - fex_port_channel with multiple extpaths (check_mode) cisco.aci.aci_static_binding_to_epg: diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin.crt b/tests/integration/targets/aci_tenant/pki/admin.crt similarity index 100% rename from tests/integration/targets/aci_tenant/tasks/certs/admin.crt rename to tests/integration/targets/aci_tenant/pki/admin.crt diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin.key b/tests/integration/targets/aci_tenant/pki/admin.key similarity index 100% rename from tests/integration/targets/aci_tenant/tasks/certs/admin.key rename to tests/integration/targets/aci_tenant/pki/admin.key diff --git a/tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key b/tests/integration/targets/aci_tenant/pki/admin_invalid.key similarity index 100% rename from tests/integration/targets/aci_tenant/tasks/certs/admin_invalid.key rename to tests/integration/targets/aci_tenant/pki/admin_invalid.key diff --git a/tests/integration/targets/aci_tenant/pki/openssh_rsa.key b/tests/integration/targets/aci_tenant/pki/openssh_rsa.key new file mode 100644 index 000000000..0c18da5c5 --- /dev/null +++ b/tests/integration/targets/aci_tenant/pki/openssh_rsa.key @@ -0,0 +1,16 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEA3VnrdPOQbr3DPF5GbC31W7ScloEpU9BSDqPmpyYPUdsWl21UXBB8 +exip3GVOl+7GbB1WkDKYr7uMuBjsfDzMzZkDAFVEpud+IUzZB7aSfSd+L9bdeFG2sGI+Fv +y1QmiMBT5gcvXaM16vRKe4FywM07/Fmd3REm/+wtmFG/C4sYUAAAIQLuIWNS7iFjUAAAAH +c3NoLXJzYQAAAIEA3VnrdPOQbr3DPF5GbC31W7ScloEpU9BSDqPmpyYPUdsWl21UXBB8ex +ip3GVOl+7GbB1WkDKYr7uMuBjsfDzMzZkDAFVEpud+IUzZB7aSfSd+L9bdeFG2sGI+Fvy1 +QmiMBT5gcvXaM16vRKe4FywM07/Fmd3REm/+wtmFG/C4sYUAAAADAQABAAAAgHj5rhALFg +MQP2X8+GwjahemzHYNPXMLRe2ucl8kE/de0CgOnq56bC4yupMz4xJyc4ufNTI2FPDmhfAP +3x+/cwZeYFsipyGdL1IYbfk0QYSP65Btr2yq8+QyN7zWdFXQ8csT0ImZgNiQKehc69ctLH +XcyelsdwNiUCRZYa7kCpf5AAAAQQCo7OSWQUa16xP9KrKm0F3fnaAKewhQNDIwok5PRgoN +03k/IpGOCAjvNuOb7DkXmVvxjO8Rj4L16vL+RTzHg8n7AAAAQQD7tej6gJy3MLcmrQ4aHb +FeLzQ/ZXS2IgdIRC8rcNB1h9Rso7+fySVFwnmwy2Um7wwsjNnr2xyhigwfQCSyRubfAAAA +QQDhH5EX7+hdm/fPLM6Goz9N3ERbIgBq2Mel5CCi/Ns7vDfBQiEla1atdKTV0S2EYfxIw2 +ehkMGbmXl2/9JHxKgbAAAAFGNpemhhb0BDSVpIQU8tTS05MjhRAQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/integration/targets/aci_tenant/pki/rsa_ansible.key b/tests/integration/targets/aci_tenant/pki/rsa_ansible.key new file mode 100644 index 000000000..ac63a0055 --- /dev/null +++ b/tests/integration/targets/aci_tenant/pki/rsa_ansible.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDVyLS8/ix6QOH7R83B4WuhsliL6nffBvrkHXXsqViit3OZd+/K +fSrNlZysUvHS4hxfRtJrFQfpkogwXEEupBPF3p0xy7wZzvjjWWJk0NQ8PoVlOhUY +emZTfMX+FFNr9pAjjjaVHb9jCuxko7upAyj8POhhETY2zYoJoa8TR6fLZwIDAQAB +AoGBALo5GzeGMThNTIyW/6Tjt94ifP9kPwcIDYSoJRECczNKmmgVEcxRO/fZW6DA +n+YTEKPuDV059KqB+iAmPKFkS9N41kaq+NUAknlFJPV6Vs3gpvJGqWgu++73dhR5 +cKsHTlK2KBsRtsXnOJ9odKWFjiTnZ1Eyvmhw7ct+Fojb/7ABAkEA9+Wwm+HGlYqw +ghuFaBtNuqC/S2vO6SEfdQvTDQKKO5ROei5m+ryjWj6flbCcG+5dLs8l4Zh3sQUL +kc0RQfHSWQJBANzFkdO6wXXPOw7RhAEP2sA2W2VacMbGynjsoDJXmypeJ7Z+odLb +5gNXET9RA77RY/saIBdwR4JNnku2WnoxU78CQQDhYirVP0vu8H6UfHMpeRGNqdLi +vq0LlrrkDxEe1f1aN/e17HRiaZnXVfKABWeZmXmNMndNifLgtiaTtC+JllRZAkEA +ydAdV0SANvaCETC7j9DzcgP+lm8PatYsHlCIvJxS7m71tKCbw0pbQDBmRtADMXrt +/4vJTEPKSrYzfxiqKstOtwJAXkWXaqVhJeKjbMj1buo6s/1qGIfSrZR/AjozvJ03 +JehevfULS3668jOYJZW6BoNhysx6+Hqf5Id8fB4iDWPQhA== +-----END RSA PRIVATE KEY----- diff --git a/tests/integration/targets/aci_tenant/pki/rsa_user.crt b/tests/integration/targets/aci_tenant/pki/rsa_user.crt new file mode 100644 index 000000000..de2223500 --- /dev/null +++ b/tests/integration/targets/aci_tenant/pki/rsa_user.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICODCCAaGgAwIBAgIUNOqiIBh811X/tPWSUgr9rajJ7t4wDQYJKoZIhvcNAQEL +BQAwLTEOMAwGA1UEAwwFQWRtaW4xDjAMBgNVBAoMBWNpc2NvMQswCQYDVQQGEwJV +UzAgFw0yMDEwMjkyMjQ0NTNaGA8yMTIwMTAwNTIyNDQ1M1owLTEOMAwGA1UEAwwF +QWRtaW4xDjAMBgNVBAoMBWNpc2NvMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwjb3/W3x/bPX+bylh2PjXbcFPwpdTPJwqTxCdUinJRKv +HXW7rwRiV9TdoNZZ946RvVM6l2LzUJyaK4wZZHf6WKJ2veL6LIrORA32vN+ofmpn +XcTAUQ1JVyHbriy0GaT+1wYClqImWj8HxiskgpD+pKc+kzgl33xwwwqyuF1N7ikC +AwEAAaNTMFEwHQYDVR0OBBYEFAK18YAZAaPQW7bHvqRwselDeGskMB8GA1UdIwQY +MBaAFAK18YAZAaPQW7bHvqRwselDeGskMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADgYEAgIvzyP0t4CjsmUmgG7QP977c3+Uvbt2wlCwe+rrXlqvuSeaW +l4DaTyv8kYyiqIxgrTVI/G+HbpHgTO2yH57njTIAdRjsJgMU9z0oCazOtVD8KpXj +SKFUtJVbY27BQAnbuDOawX96a0UDY44Ia9NaPuq0/mEcdCKSpQP4ZuvvKVc= +-----END CERTIFICATE----- diff --git a/tests/integration/targets/aci_tenant/pki/rsa_user.key b/tests/integration/targets/aci_tenant/pki/rsa_user.key new file mode 100644 index 000000000..354dbbdbb --- /dev/null +++ b/tests/integration/targets/aci_tenant/pki/rsa_user.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMI29/1t8f2z1/m8 +pYdj4123BT8KXUzycKk8QnVIpyUSrx11u68EYlfU3aDWWfeOkb1TOpdi81CcmiuM +GWR3+liidr3i+iyKzkQN9rzfqH5qZ13EwFENSVch264stBmk/tcGApaiJlo/B8Yr +JIKQ/qSnPpM4Jd98cMMKsrhdTe4pAgMBAAECgYAX8c8BX9zF+rZWA/wkhRwzIa1z +6EM4iWt6cgN/kkWJPJR6fVl2aoP1cDki60qMSveM8AX5RCnbdnNLiypWSLSEogdd +bRWyFeF4ZXvivd+Lds2u5Ni3PiCrIpHfNvid2ERCaKhblQRdhi/dTH9Z+3kGspwc +jpKzWMmGjBpqWjWOQQJBAOB3cS/AxbwJ6Fzvbi6sLiK6Ry8eSIMlce3Yyw89oU+M +DGkIbggICCYKxXYIWtBbyxthdQudKFZYbLpCkLSMBXsCQQDdf5ICNN2R0ptYLhSX +kQ4tiGigi1hq93+25Ov1rI8eIFSYlKNcyA/cvwv5ptlXmy1UAyoAdGCbS47pgCwT +Nz+rAkEAtzHkR5PuDXSMluS2KRNPJ/qdxB/UEGzMGdEYkNy8vX5QVpyRqK5dcCbU +V2ukKm7wSe11KEBgPnA2dKGFFkU85wJAD895Vpr7bdtAp2yyn5cFEg74mO0ZZJlC +DoYMqb6lgJsCLtn9RzQonbMtYaadQPmcpLCNIPctpiggjV5OxxhcfQJBAM1ETm8p +/9beBPTS8cJdWHvCRE149H/ZCUxqjFZriJzFYvi0xor85eK8/3V7xaWtTkK25i3+ +xWk+sA3DYYDPGM8= +-----END PRIVATE KEY----- diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml index 51d392ed8..3a4b6f27e 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml @@ -6,8 +6,9 @@ host: "{{ aci_hostname }}" username: "{{ aci_username }}" password: "{{ aci_password }}" - validate_certs: "{{ validate_certs | default(false) }}" - use_ssl: "{{ use_ssl | default(true) }}" + validate_certs: "{{ aci_validate_certs | default(false) }}" + use_ssl: "{{ aci_use_ssl | default(true) }}" + use_proxy: "{{ aci_use_proxy | default(true) }}" timeout: 5 output_level: debug @@ -19,15 +20,25 @@ - name: Set the connection to httpapi and set session key used in the inventory ansible.builtin.set_fact: + ansible_user: "{{ aci_username }}" + ansible_password: "{{ aci_password }}" ansible_connection: ansible.netcommon.httpapi - ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'certs/admin.key') }}"} + ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'pki/admin.key') }}"} + ansible_httpapi_validate_certs: "{{ aci_validate_certs | default(false) }}" + ansible_httpapi_use_ssl: "{{ aci_use_ssl | default(true) }}" + ansible_httpapi_use_proxy: "{{ aci_use_proxy | default(true) }}" + ansible_httpapi_port: 443 + ansible_command_timeout: 5 + +- name: Run aci_aaa_user_certificate through the plugin + include_tasks: ../../../../../../integration/targets/aci_aaa_user_certificate/tasks/main.yml - name: Add user certificate to be used later in the test cisco.aci.aci_aaa_user_certificate: <<: *aci_info aaa_user: "{{ aci_username }}" name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" + certificate: "{{ lookup('file', 'pki/admin.crt') }}" state: present # XML operation, APIC HTTP Error and Connection reset upon changed parameter tests @@ -136,7 +147,7 @@ - name: Delete Tenant with password and private key in the task (private_key takes precedence) cisco.aci.aci_tenant: <<: *aci_info - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: absent register: op1_task_private_key_delete_tenant @@ -144,7 +155,7 @@ - name: Add Tenant with password and private key in the task (Check for execution on the provided aci_hostname with no attempts at re-connection) cisco.aci.aci_tenant: <<: *aci_info - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: present register: op1_task_private_key_add_tenant @@ -316,7 +327,7 @@ - name: Verify failure when no hosts are active assert: that: - - op7_task_pwd_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op7_task_pwd_delete_tenant.error.text | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none - op7_task_pwd_add_tenant is not changed # Switching of hosts test with the the inventory password @@ -403,14 +414,14 @@ - name: Verify failure when no hosts are active in the inventory assert: that: - - op10_inventory_pwd_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op10_inventory_pwd_delete_tenant.error.text | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none # Switching of hosts test with the private key in the task - name: Delete Tenant with only private key in the task (Check for successful operation on the last host) cisco.aci.aci_tenant: <<: *aci_info host: "{{ last_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: absent register: op11_task_private_key_delete_tenant @@ -419,7 +430,7 @@ cisco.aci.aci_tenant: <<: *aci_info host: "{{ last_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: present register: op11_task_private_key_add_tenant @@ -444,7 +455,7 @@ cisco.aci.aci_tenant: <<: *aci_info host: "{{ second_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: absent register: op12_task_private_key_delete_tenant @@ -453,7 +464,7 @@ cisco.aci.aci_tenant: <<: *aci_info host: "{{ second_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: present register: op12_task_private_key_add_tenant @@ -476,7 +487,7 @@ cisco.aci.aci_tenant: <<: *aci_info host: "{{ no_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: absent register: op13_task_private_key_delete_tenant @@ -486,7 +497,7 @@ cisco.aci.aci_tenant: <<: *aci_info host: "{{ second_host_active }}" - private_key: "{{ lookup('file', 'certs/admin.key') }}" + private_key: "{{ lookup('file', 'pki/admin.key') }}" tenant: ansible_test state: present register: op13_task_private_key_add_tenant @@ -494,14 +505,14 @@ - name: Verify failure when no hosts are active in the task assert: that: - - op13_task_private_key_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op13_task_private_key_delete_tenant.error.text | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none - op13_task_private_key_add_tenant is not changed # Switching of hosts test with the the inventory session key - name: Set list of hosts in the inventory with the last host as active ansible.builtin.set_fact: ansible_host: "{{aci_hostname|generate_random_ips(5,5)}}" - ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'certs/admin.key') }}"} + ansible_httpapi_session_key: {'admin': "{{ lookup('file', 'pki/admin.key') }}"} - name: Delete Tenant with session key in the inventory (Check for successful operation on the last host) cisco.aci.aci_tenant: @@ -582,7 +593,7 @@ - name: Verify failure when no hosts are active in the inventory assert: that: - - op16_inventory_session_key_delete_tenant.response | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none + - op16_inventory_session_key_delete_tenant.error.text | regex_search('No hosts left in the cluster to continue operation! Error on final host [0-9]+(?:\.[0-9]+){3}') is not none # Clean up Environment - name: Delete a tenant using an XML string @@ -597,18 +608,18 @@ <<: *aci_info aaa_user: "{{ aci_username }}" name: admin - certificate: "{{ lookup('file', 'certs/admin.crt') }}" + certificate: "{{ lookup('file', 'pki/admin.crt') }}" state: absent - name: Delete Tenant cisco.aci.aci_tenant: <<: *aci_info + port: 443 tenant: ansible_test state: absent - name: Remove session key used in the inventory ansible.builtin.set_fact: - ansible_connection: local ansible_httpapi_session_key: - name: refresh inventory for other tests in integration diff --git a/tests/integration/targets/aci_tenant/tasks/main.yml b/tests/integration/targets/aci_tenant/tasks/main.yml index 4738e2eb3..b8a7a310f 100644 --- a/tests/integration/targets/aci_tenant/tasks/main.yml +++ b/tests/integration/targets/aci_tenant/tasks/main.yml @@ -62,25 +62,6 @@ check_mode: true register: cm_add_tenant -# ADD TENANT -- name: Add tenant (check_mode) - cisco.aci.aci_tenant: &tenant_present - host: '{{ aci_hostname }}' - username: '{{ aci_username }}' - password: '{{ aci_password }}' - validate_certs: '{{ aci_validate_certs | default(false) }}' - use_ssl: '{{ aci_use_ssl | default(true) }}' - use_proxy: '{{ aci_use_proxy | default(true) }}' - output_level: '{{ aci_output_level | default("info") }}' - tenant: ansible_test - state: present - annotation: ansible_test - owner_key: ansible_key - owner_tag: ansible_tag - output_path: "{{ aci_hostname }}_cm_add_tenant.json" - check_mode: yes - register: cm_add_tenant - - name: Dump content of files debug: msg: "{{ lookup('file', aci_hostname +'_cm_add_tenant.json')}}" diff --git a/tests/integration/targets/aci_tenant_span_dst_group/tasks/main.yml b/tests/integration/targets/aci_tenant_span_dst_group/tasks/main.yml index d7d807ad8..543a25cd5 100644 --- a/tests/integration/targets/aci_tenant_span_dst_group/tasks/main.yml +++ b/tests/integration/targets/aci_tenant_span_dst_group/tasks/main.yml @@ -26,216 +26,230 @@ tenant: ansible_tenant state: absent -- name: Add a new tenant - aci_tenant: - <<: *aci_info - tenant: ansible_tenant - description: Ansible tenant - state: present - -- name: Add span ansible_group - aci_tenant_span_dst_group: - <<: *aci_info - destination_group: ansible_group - description: Test span - destination_ip: 10.0.0.1 - source_ip: 10.0.2.1 - tenant: ansible_tenant - destination_epg: - tenant: Test1 - ap: ap1 - epg: ep1 - version_enforced: false - span_version: version_1 - ttl: 2 - mtu: 1500 - flow_id: 1 - dscp: "CS1" - state: present - register: add_span1 - -- name: Verify add span - assert: - that: - - add_span1 is changed - - add_span1.current.0.spanDestGrp.attributes.name == "ansible_group" - - add_span1.current.0.spanDestGrp.attributes.descr == "Test span" - - add_span1.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group" - - add_span1.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ver == "ver1" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "no" - - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" - - add_span1.current.0.spanDestGrp.attributes.annotation == 'orchestrator:ansible' - -- name: Add span ansible_group again - aci_tenant_span_dst_group: - <<: *aci_info - destination_group: ansible_group - description: Test span - destination_ip: 10.0.0.1 - source_ip: 10.0.2.1 - tenant: ansible_tenant - destination_epg: - tenant: Test1 - ap: ap1 - epg: ep1 - version_enforced: false - span_version: version_1 - ttl: 2 - mtu: 1500 - flow_id: 1 - dscp: "CS1" - state: present - register: add_span1_again - -- name: Verify add span again - assert: - that: - - add_span1_again is not changed - -- name: Change span ansible_group's src ip - aci_tenant_span_dst_group: - <<: *aci_info - destination_group: ansible_group2 - description: Test span - destination_ip: 10.0.0.2 - source_ip: 10.0.2.1 - tenant: ansible_tenant - destination_epg: - tenant: Test1 - ap: ap1 - epg: ep1 - version_enforced: false - span_version: version_1 - ttl: 2 - mtu: 1500 - flow_id: 1 - dscp: CS1 - state: present - register: change_span1_ip - -- name: Change span ansible_group's dscp - aci_tenant_span_dst_group: - <<: *aci_info - destination_group: ansible_group2 - description: Test span - destination_ip: 10.0.0.2 - source_ip: 10.0.2.1 - tenant: ansible_tenant - destination_epg: - tenant: Test1 - ap: ap1 - epg: ep1 - version_enforced: false - span_version: version_1 - ttl: 2 - mtu: 1500 - flow_id: 1 - dscp: VA - state: present - register: change_span1_dscp - -- name: Verify changes in span - assert: - that: - - change_span1_ip.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.2" - - change_span1_dscp.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "VA" - -- name: Add span ansible_group2 - aci_tenant_span_dst_group: - <<: *aci_info - destination_group: ansible_group2 - description: Test span - destination_ip: 10.0.0.1 - source_ip: 10.0.2.1 - tenant: ansible_tenant - destination_epg: - tenant: Test1 - ap: ap1 - epg: ep1 - version_enforced: true - span_version: version_2 - ttl: 2 - mtu: 1500 - flow_id: 1 - dscp: CS1 - state: present - register: add_span2 - -- name: Verify addition of second span - assert: - that: - - add_span2 is changed - - add_span2.current.0.spanDestGrp.attributes.name == "ansible_group2" - - add_span2.current.0.spanDestGrp.attributes.descr == "Test span" - - add_span2.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group2" - - add_span2.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group2" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ver == "ver2" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "yes" - - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" - -- name: Query span ansible_group - aci_tenant_span_dst_group: - <<: *aci_info - tenant: ansible_tenant - destination_group: ansible_group - state: query - register: query_span_ansible_group - -- name: Query all span dest groups - aci_tenant_span_dst_group: - <<: *aci_info - state: query - register: query_all_span - -- name: Verify Query of span - assert: - that: - - query_span_ansible_group is not changed - - query_span_ansible_group.current.0.spanDestGrp.attributes.name == "ansible_group" - - query_span_ansible_group.current.0.spanDestGrp.attributes.descr == "Test span" - - query_span_ansible_group.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "no" - - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" - - query_all_span is not changed - - query_all_span | length >= 2 - -- name: Remove span ansible_group - aci_tenant_span_dst_group: - <<: *aci_info - tenant: ansible_tenant - destination_group: ansible_group - state: absent - register: remove_span1 - -- name: Remove span ansible_group2 - aci_tenant_span_dst_group: - <<: *aci_info - tenant: ansible_tenant - destination_group: ansible_group2 - state: absent - register: remove_span2 - -- name: Verify Remove of span - assert: - that: - - remove_span1 is changed - - remove_span1.current == [] - - remove_span2 is changed - - remove_span2.current == [] +- name: Verify Cloud and Non-Cloud Sites in use. + include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + + - name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add span ansible_group + aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_group + description: Test span + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + destination_epg: + tenant: Test1 + ap: ap1 + epg: ep1 + version_enforced: false + span_version: version_1 + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + state: present + register: add_span1 + + - name: Verify add span + assert: + that: + - add_span1 is changed + - add_span1.current.0.spanDestGrp.attributes.name == "ansible_group" + - add_span1.current.0.spanDestGrp.attributes.descr == "Test span" + - add_span1.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group" + - add_span1.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ver == "ver1" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "no" + - add_span1.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" + - add_span1.current.0.spanDestGrp.attributes.annotation == 'orchestrator:ansible' + + - name: Add span ansible_group again + aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_group + description: Test span + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + destination_epg: + tenant: Test1 + ap: ap1 + epg: ep1 + version_enforced: false + span_version: version_1 + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + state: present + register: add_span1_again + + - name: Verify add span again + assert: + that: + - add_span1_again is not changed + + - name: Change span ansible_group's src ip + aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_group2 + description: Test span + destination_ip: 10.0.0.2 + source_ip: 10.0.2.1 + tenant: ansible_tenant + destination_epg: + tenant: Test1 + ap: ap1 + epg: ep1 + version_enforced: false + span_version: version_1 + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: CS1 + state: present + register: change_span1_ip + + - name: Change span ansible_group's dscp + aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_group2 + description: Test span + destination_ip: 10.0.0.2 + source_ip: 10.0.2.1 + tenant: ansible_tenant + destination_epg: + tenant: Test1 + ap: ap1 + epg: ep1 + version_enforced: false + span_version: version_1 + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: VA + state: present + register: change_span1_dscp + + - name: Verify changes in span + assert: + that: + - change_span1_ip.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.2" + - change_span1_dscp.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "VA" + + - name: Add span ansible_group2 + aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_group2 + description: Test span + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + destination_epg: + tenant: Test1 + ap: ap1 + epg: ep1 + version_enforced: true + span_version: version_2 + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: CS1 + state: present + register: add_span2 + + - name: Verify addition of second span + assert: + that: + - add_span2 is changed + - add_span2.current.0.spanDestGrp.attributes.name == "ansible_group2" + - add_span2.current.0.spanDestGrp.attributes.descr == "Test span" + - add_span2.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group2" + - add_span2.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group2" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ver == "ver2" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "yes" + - add_span2.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" + + - name: Query span ansible_group + aci_tenant_span_dst_group: + <<: *aci_info + tenant: ansible_tenant + destination_group: ansible_group + state: query + register: query_span_ansible_group + + - name: Query all span dest groups + aci_tenant_span_dst_group: + <<: *aci_info + state: query + register: query_all_span + + - name: Verify Query of span + assert: + that: + - query_span_ansible_group is not changed + - query_span_ansible_group.current.0.spanDestGrp.attributes.name == "ansible_group" + - query_span_ansible_group.current.0.spanDestGrp.attributes.descr == "Test span" + - query_span_ansible_group.current.0.spanDestGrp.attributes.dn == "uni/tn-ansible_tenant/destgrp-ansible_group" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.attributes.name == "ansible_group" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.dscp == "CS1" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.srcIpPrefix == "10.0.2.1" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ip == "10.0.0.1" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.mtu == "1500" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.flowId == "1" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.verEnforced == "no" + - query_span_ansible_group.current.0.spanDestGrp.children.0.spanDest.children.0.spanRsDestEpg.attributes.ttl == "2" + - query_all_span is not changed + - query_all_span | length >= 2 + + - name: Remove span ansible_group + aci_tenant_span_dst_group: + <<: *aci_info + tenant: ansible_tenant + destination_group: ansible_group + state: absent + register: remove_span1 + + - name: Remove span ansible_group2 + aci_tenant_span_dst_group: + <<: *aci_info + tenant: ansible_tenant + destination_group: ansible_group2 + state: absent + register: remove_span2 + + - name: Verify Remove of span + assert: + that: + - remove_span1 is changed + - remove_span1.current == [] + - remove_span2 is changed + - remove_span2.current == [] + + # CLEAN ENVIRONMENT + - name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent From 814e43567395b3ce1e6995a1269c96b324ffec07 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 15 May 2023 15:16:27 -0400 Subject: [PATCH 38/49] [ignore_changes] Changes made to the connection test file to preserve original connection type --- plugins/doc_fragments/aci.py | 10 ++--- plugins/httpapi/aci.py | 40 +++++++++++-------- plugins/module_utils/aci.py | 30 +++++++------- plugins/modules/aci_rest.py | 7 +++- .../aci_aaa_user_certificate/tasks/main.yml | 2 +- .../aci_tenant/filter_plugins/generate_ips.py | 2 +- .../aci_tenant/tasks/httpapi_connection.yml | 11 +++-- 7 files changed, 58 insertions(+), 44 deletions(-) diff --git a/plugins/doc_fragments/aci.py b/plugins/doc_fragments/aci.py index 70eaa24dc..94d00b514 100644 --- a/plugins/doc_fragments/aci.py +++ b/plugins/doc_fragments/aci.py @@ -18,7 +18,6 @@ class ModuleDocFragment(object): - IP Address or hostname of APIC resolvable by Ansible control host. - If the value is not specified in the task, the value of environment variable C(ACI_HOST) will be used instead. type: str - required: true aliases: [ hostname ] port: description: @@ -30,6 +29,7 @@ class ModuleDocFragment(object): description: - The username to use for authentication. - If the value is not specified in the task, the value of environment variables C(ACI_USERNAME) or C(ANSIBLE_NET_USERNAME) will be used instead. + - The default value is admin. type: str aliases: [ user ] password: @@ -68,27 +68,27 @@ class ModuleDocFragment(object): description: - The socket level timeout in seconds. - If the value is not specified in the task, the value of environment variable C(ACI_TIMEOUT) will be used instead. + - The default value is 30. type: int - default: 30 use_proxy: description: - If C(false), it will not use a proxy, even if one is defined in an environment variable on the target hosts. - If the value is not specified in the task, the value of environment variable C(ACI_USE_PROXY) will be used instead. + - The default value is true. type: bool - default: true use_ssl: description: - If C(false), an HTTP connection will be used instead of the default HTTPS connection. - If the value is not specified in the task, the value of environment variable C(ACI_USE_SSL) will be used instead. + - The default value is true. type: bool - default: true validate_certs: description: - If C(false), SSL certificates will not be validated. - This should only set to C(false) when used on personally controlled sites using self-signed certificates. - If the value is not specified in the task, the value of environment variable C(ACI_VALIDATE_CERTS) will be used instead. + - The default value is true. type: bool - default: true output_path: description: - Path to a file that will be used to dump the ACI JSON configuration objects generated by the module. diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 5f22e5040..65ae3f384 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -64,6 +64,7 @@ RESET_KEYS = ["username", "password", "port"] CONNECTION_KEYS = RESET_KEYS + ["timeout", "use_proxy", "use_ssl", "validate_certs"] + class HttpApi(HttpApiBase): def __init__(self, *args, **kwargs): super(HttpApi, self).__init__(*args, **kwargs) @@ -148,14 +149,14 @@ def set_parameters(self): self.set_hosts() def set_hosts(self): - if self.params.get("host") is not None: + if self.params.get("host") is not None: hosts = ast.literal_eval(self.params.get("host")) if "[" in self.params.get("host") else self.params.get("host").split(",") - else: + else: if self.inventory_hosts is None: self.inventory_hosts = re.sub(r"[[\]]", "", self.connection.get_option("host")).split(",") hosts = self.inventory_hosts - if self.provided_hosts is None: + if self.provided_hosts is None: self.provided_hosts = deepcopy(hosts) self.connection.queue_message( "debug", "Provided Hosts: {0}".format(self.provided_hosts) @@ -164,8 +165,8 @@ def set_hosts(self): self.current_host = self.backup_hosts.pop(0) self.connection.queue_message( "debug", "Initializing operation on {0}".format(self.current_host) - ) - elif self.provided_hosts != hosts: + ) + elif self.provided_hosts != hosts: self.provided_hosts = deepcopy(hosts) self.connection.queue_message( "debug", "Provided Hosts have changed: {0}".format(self.provided_hosts) @@ -174,15 +175,15 @@ def set_hosts(self): try: self.backup_hosts.pop(self.backup_hosts.index(self.current_host)) self.connection.queue_message( - "debug", "Connected host {0} found in the provided hosts. Continuing with it.".format(self.current_host) - ) + "debug", "Connected host {0} found in the provided hosts. Continuing with it.".format(self.current_host) + ) except Exception: self.current_host = self.backup_hosts.pop(0) self.connection._connected = False self.connection.queue_message( - "debug", "Initializing operation on {0}".format(self.current_host) + "debug", "Initializing operation on {0}".format(self.current_host) ) - self.connection.set_option("host", self.current_host) + self.connection.set_option("host", self.current_host) # One API call is made via each call to send_request from aci.py in module_utils # As long as a host is active in the list the API call will go through @@ -197,13 +198,13 @@ def send_request(self, method, path, data): self.connection._connected = True except Exception as exc_response: self.connection._connected = False - return self._return_info("", method, self.validate_url(self.connection._url+path), str(exc_response)) + return self._return_info("", method, self.validate_url(self.connection._url + path), str(exc_response)) try: if self.connection._connected is False: self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) self.connection.queue_message( - "debug", "Sending {0} request to {1}".format(method, self.connection._url+path) + "debug", "Sending {0} request to {1}".format(method, self.connection._url + path) ) response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message( @@ -214,10 +215,11 @@ def send_request(self, method, path, data): if len(self.backup_hosts) == 0: self.provided_hosts = None self.connection._connected = False - error = dict(code=-1, text="No hosts left in the cluster to continue operation! Error on final host {0}".format(self.connection.get_option("host"))) + error = dict(code=-1, text="No hosts left in the cluster to continue operation! Error on final host {0}" + .format(self.connection.get_option("host"))) if 'path' in dir(exc_response): path = exc_response.path - return self._return_info("", method, self.validate_url(self.connection._url+path), str(exc_response), error=error) + return self._return_info("", method, self.validate_url(self.connection._url + path), str(exc_response), error=error) else: self.current_host = self.backup_hosts.pop(0) self.connection.queue_message( @@ -245,7 +247,7 @@ def handle_httperror(self, exc): def validate_url(self, url): validated_url = re.match(r'^.*?\.json|^.*?\.xml', url).group(0) if self.connection_parameters.get("port") is None: - return validated_url.replace(re.match(r'(https?:\/\/.*)(:\d*)\/?(.*)',url).group(2),"") + return validated_url.replace(re.match(r'(https?:\/\/.*)(:\d*)\/?(.*)', url).group(2), "") else: return validated_url @@ -273,14 +275,17 @@ def _response_to_json(self, response_text): except Exception: return "Invalid JSON response: {0}".format(response_text) - def _return_info(self, response_code, method, path, msg, respond_data=None, error={}): + def _return_info(self, response_code, method, path, msg, respond_data=None, error=None): """Format success/error data and return with consistent format""" info = {} info["status"] = response_code info["method"] = method info["url"] = path info["msg"] = msg - info["error"] = error + if error is not None: + info["error"] = error + else: + info["error"] = {} # Response check to trigger key error if response_data is invalid if respond_data is not None: info["body"] = respond_data @@ -325,7 +330,8 @@ def cert_auth(self, method, path, payload=""): self.connection_parameters["certificate_name"] = os.path.basename(os.path.splitext(self.connection_parameters.get("private_key"))[0]) else: raise ConnectionError( - "Provided private key {0} does not appear to be a private key or provided file does not exist.".format(self.connection_parameters.get("private_key")) + "Provided private key {0} does not appear to be a private key or provided file does not exist.".format( + self.connection_parameters.get("private_key")) ) if self.connection_parameters.get("certificate_name") is None: self.connection_parameters["certificate_name"] = self.connection.get_option("remote_user") diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 3f374adb7..cb4185b44 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -100,7 +100,6 @@ def aci_argument_spec(): return dict( host=dict( type="str", - required=False, aliases=["hostname"], fallback=(env_fallback, ["ACI_HOST"]), ), @@ -347,6 +346,9 @@ def __init__(self, module): # Ensure protocol is set self.define_protocol() + # Set Connection plugin + self.set_connection() + if self.module._debug: self.module.warn("Enable debug output because ANSIBLE_DEBUG was set.") self.params["output_level"] = "debug" @@ -358,7 +360,7 @@ def __init__(self, module): msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") elif self.params.get("password") is not None: self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") - elif self.get_connection() is None: + elif self.connection is None: if self.params.get("password"): # Perform password-based authentication, log on using password self.login() @@ -405,15 +407,10 @@ def define_method(self): state_map = dict(absent="delete", present="post", query="get") self.params["method"] = state_map.get(self.params.get("state")) - def get_connection(self): - if self.connection is not None: - return self.connection - - if self.module._socket_path: + def set_connection(self): + if self.connection is None and self.module._socket_path: self.connection = Connection(self.module._socket_path) - return self.connection - def login(self): """Log in to APIC""" @@ -496,7 +493,8 @@ def cert_auth(self, path=None, payload="", method=None): if self.params.get("certificate_name") is None: self.params["certificate_name"] = os.path.basename(os.path.splitext(self.params.get("private_key"))[0]) else: - self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key or provided file does not exist." % self.params) + self.module.fail_json(msg="Provided private key %(private_key)s does not appear to be a private key or provided file does not exist." + % self.params) if self.params.get('certificate_name') is None: self.params['certificate_name'] = self.params.get('username', 'admin') @@ -1632,21 +1630,23 @@ def api_call(self, method, url, data=None, output=False): >>>>>>> f20fdfe ([ignore_changes] New test file created) ======= def parsed_url_path(self, url): + if not HAS_URLPARSE: + self.fail_json(msg='urlparse is not installed') parse_result = urlparse(url) if parse_result.query == '': return parse_result.path - else: + else: return parse_result.path + '?' + parse_result.query def api_call(self, method, url, data=None, return_response=False): >>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) resp = None - if self.get_connection() is not None: - self.get_connection().set_params(self.params) - info = self.get_connection().send_request(method, self.parsed_url_path(url), data) + if self.connection is not None: + self.connection.set_params(self.params) + info = self.connection.send_request(method, self.parsed_url_path(url), data) self.url = info.get("url") self.error = info.get("error") - self.httpapi_logs.extend(self.get_connection().pop_messages()) + self.httpapi_logs.extend(self.connection.pop_messages()) else: if self.params.get("private_key"): self.cert_auth(path=self.parsed_url_path(url), payload=data, method=method) diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 28cfb80d0..376fe140c 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -55,6 +55,11 @@ together with the C(template) lookup plugin, or use C(template). type: path aliases: [ config_file ] + rsp_subtree_preserve: + description: + - Preserve the response for the provided path. + type: bool + default: false extends_documentation_fragment: - cisco.aci.aci @@ -334,7 +339,7 @@ def main(): method=dict(type="str", default="get", choices=["delete", "get", "post"], aliases=["action"]), src=dict(type="path", aliases=["config_file"]), content=dict(type="raw"), - rsp_subtree_preserve=dict(type=bool, default=False), + rsp_subtree_preserve=dict(type="bool", default=False), ) module = AnsibleModule( diff --git a/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml b/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml index 6ed63c2d1..3140816e0 100644 --- a/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml +++ b/tests/integration/targets/aci_aaa_user_certificate/tasks/main.yml @@ -381,7 +381,7 @@ that: - query_test is not changed - query_test_pk_file is not changed - - query_test_non_existent_key.response == "Provided private key ******** does not appear to be a private key or provided file does not exist." + - '"Provided private key ******** does not appear to be a private key or provided file does not exist." in query_test_non_existent_key.msg' - query_test_pk_content is not changed # Cleanup environment diff --git a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py index 019729752..c9d5dcfdd 100644 --- a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py +++ b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py @@ -1,9 +1,9 @@ -#!/usr/bin/python from ipaddress import ip_network import random RANGE_IPV4 = list(ip_network("192.0.2.0/24").hosts()) + list(ip_network("198.51.100.0/24").hosts()) + list(ip_network("203.0.113.0/24").hosts()) + class FilterModule(object): def filters(self): return { diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml index 3a4b6f27e..05dc1081c 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml @@ -18,6 +18,10 @@ second_host_active: "{{aci_hostname|generate_random_ips(2,5)}}" no_host_active: "{{aci_hostname|generate_random_ips(0,5)}}" +- name: Set the actual connection type specified in the inventory (this var is used at the end of this test) + ansible.builtin.set_fact: + old_connection: "{{ ansible_connection }}" + - name: Set the connection to httpapi and set session key used in the inventory ansible.builtin.set_fact: ansible_user: "{{ aci_username }}" @@ -618,9 +622,8 @@ tenant: ansible_test state: absent -- name: Remove session key used in the inventory +- name: Cleanup facts to continue operation using the inventory file ansible.builtin.set_fact: + ansible_host: "{{ aci_hostname }}" + ansible_connection: "{{ old_connection }}" ansible_httpapi_session_key: - -- name: refresh inventory for other tests in integration - meta: refresh_inventory From d0d9e40c90fc910fc7e19d2a64163f8e4496c586 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 17 May 2023 12:23:51 -0400 Subject: [PATCH 39/49] [ignore_changes] Applied the black format to the code --- plugins/doc_fragments/aci.py | 2 +- plugins/httpapi/aci.py | 64 ++++++++----------- plugins/module_utils/aci.py | 47 +++++++------- plugins/modules/aci_config_rollback.py | 15 ++--- plugins/modules/aci_config_snapshot.py | 7 +- plugins/modules/aci_rest.py | 16 ++--- .../aci_tenant/tasks/httpapi_connection.yml | 28 +++++++- 7 files changed, 95 insertions(+), 84 deletions(-) diff --git a/plugins/doc_fragments/aci.py b/plugins/doc_fragments/aci.py index 94d00b514..e6b18a289 100644 --- a/plugins/doc_fragments/aci.py +++ b/plugins/doc_fragments/aci.py @@ -80,7 +80,7 @@ class ModuleDocFragment(object): description: - If C(false), an HTTP connection will be used instead of the default HTTPS connection. - If the value is not specified in the task, the value of environment variable C(ACI_USE_SSL) will be used instead. - - The default value is true. + - The default value is true when the connection is local. type: bool validate_certs: description: diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 65ae3f384..415fe8ddc 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -119,6 +119,8 @@ def set_parameters(self): connection_parameters = {} for key in CONNECTION_KEYS: value = self.params.get(key) if self.params.get(key) is not None else self.connection.get_option(CONNECTION_MAP.get(key, key)) + if key == "username" and value is None: + value = "admin" self.connection.set_option(CONNECTION_MAP.get(key, key), value) if key == "timeout" and self.params.get(key) is not None: self.connection.set_option("persistent_connect_timeout", value + 30) @@ -126,7 +128,7 @@ def set_parameters(self): connection_parameters[key] = value if self.connection_parameters and value != self.connection_parameters.get(key) and key in RESET_KEYS: self.connection._connected = False - self.connection.queue_message("debug", "Re-setting connection due to change in {0}".format(key)) + self.connection.queue_message("debug", "Re-setting connection due to change in the {0}".format(key)) if self.params.get("private_key") is not None: self.connection.set_option("session_key", None) @@ -138,7 +140,9 @@ def set_parameters(self): else: if self.connection_parameters.get("private_key") is not None: self.connection._connected = False - self.connection.queue_message("debug", "Re-setting connection due to change from private/session key authentication to password authentication") + self.connection.queue_message( + "debug", "Re-setting connection due to change from private/session key authentication to password authentication" + ) self.connection.set_option("session_key", None) connection_parameters["private_key"] = None connection_parameters["certificate_name"] = None @@ -158,31 +162,21 @@ def set_hosts(self): if self.provided_hosts is None: self.provided_hosts = deepcopy(hosts) - self.connection.queue_message( - "debug", "Provided Hosts: {0}".format(self.provided_hosts) - ) + self.connection.queue_message("debug", "Provided Hosts: {0}".format(self.provided_hosts)) self.backup_hosts = deepcopy(hosts) self.current_host = self.backup_hosts.pop(0) - self.connection.queue_message( - "debug", "Initializing operation on {0}".format(self.current_host) - ) + self.connection.queue_message("debug", "Initializing operation on {0}".format(self.current_host)) elif self.provided_hosts != hosts: self.provided_hosts = deepcopy(hosts) - self.connection.queue_message( - "debug", "Provided Hosts have changed: {0}".format(self.provided_hosts) - ) + self.connection.queue_message("debug", "Provided Hosts have changed: {0}".format(self.provided_hosts)) self.backup_hosts = deepcopy(hosts) try: self.backup_hosts.pop(self.backup_hosts.index(self.current_host)) - self.connection.queue_message( - "debug", "Connected host {0} found in the provided hosts. Continuing with it.".format(self.current_host) - ) + self.connection.queue_message("debug", "Connected host {0} found in the provided hosts. Continuing with it.".format(self.current_host)) except Exception: self.current_host = self.backup_hosts.pop(0) self.connection._connected = False - self.connection.queue_message( - "debug", "Initializing operation on {0}".format(self.current_host) - ) + self.connection.queue_message("debug", "Initializing operation on {0}".format(self.current_host)) self.connection.set_option("host", self.current_host) # One API call is made via each call to send_request from aci.py in module_utils @@ -203,9 +197,7 @@ def send_request(self, method, path, data): try: if self.connection._connected is False: self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) - self.connection.queue_message( - "debug", "Sending {0} request to {1}".format(method, self.connection._url + path) - ) + self.connection.queue_message("debug", "Sending {0} request to {1}".format(method, self.connection._url + path)) response, response_data = self.connection.send(path, data, method=method) self.connection.queue_message( "debug", "Received response from {0} for {1} operation with HTTP: {2}".format(self.connection.get_option("host"), method, response.getcode()) @@ -215,16 +207,15 @@ def send_request(self, method, path, data): if len(self.backup_hosts) == 0: self.provided_hosts = None self.connection._connected = False - error = dict(code=-1, text="No hosts left in the cluster to continue operation! Error on final host {0}" - .format(self.connection.get_option("host"))) - if 'path' in dir(exc_response): + error = dict( + code=-1, text="No hosts left in the cluster to continue operation! Error on final host {0}".format(self.connection.get_option("host")) + ) + if "path" in dir(exc_response): path = exc_response.path return self._return_info("", method, self.validate_url(self.connection._url + path), str(exc_response), error=error) else: self.current_host = self.backup_hosts.pop(0) - self.connection.queue_message( - "debug", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host) - ) + self.connection.queue_message("debug", "Switching host from {0} to {1}".format(self.connection.get_option("host"), self.current_host)) self.connection.set_option("host", self.current_host) # recurse through function for retrying the request return self.send_request(method, path, data) @@ -233,21 +224,19 @@ def send_request(self, method, path, data): # Built-in-function def handle_httperror(self, exc): - self.connection.queue_message( - "debug", "Failed to receive response from {0} with {1}".format(self.connection.get_option("host"), exc) - ) + self.connection.queue_message("debug", "Failed to receive response from {0} with {1}".format(self.connection.get_option("host"), exc)) if exc.code == 401: raise ConnectionError(exc) elif exc.code == 403 and self.connection_parameters.get("private_key") is None: self.connection._auth = None - self.login(self.connection.get_option('remote_user'), self.connection.get_option('password')) + self.login(self.connection.get_option("remote_user"), self.connection.get_option("password")) return True return exc def validate_url(self, url): - validated_url = re.match(r'^.*?\.json|^.*?\.xml', url).group(0) + validated_url = re.match(r"^.*?\.json|^.*?\.xml", url).group(0) if self.connection_parameters.get("port") is None: - return validated_url.replace(re.match(r'(https?:\/\/.*)(:\d*)\/?(.*)', url).group(2), "") + return validated_url.replace(re.match(r"(https?:\/\/.*)(:\d*)\/?(.*)", url).group(2), "") else: return validated_url @@ -331,7 +320,8 @@ def cert_auth(self, method, path, payload=""): else: raise ConnectionError( "Provided private key {0} does not appear to be a private key or provided file does not exist.".format( - self.connection_parameters.get("private_key")) + self.connection_parameters.get("private_key") + ) ) if self.connection_parameters.get("certificate_name") is None: self.connection_parameters["certificate_name"] = self.connection.get_option("remote_user") @@ -340,13 +330,11 @@ def cert_auth(self, method, path, payload=""): sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) else: sig_signature = sign(sig_key, sig_request, "sha256") - sig_dn = "uni/userext/user-{0}/usercert-{1}".format( - self.connection.get_option("remote_user"), self.connection_parameters.get("certificate_name") - ) + sig_dn = "uni/userext/user-{0}/usercert-{1}".format(self.connection.get_option("remote_user"), self.connection_parameters.get("certificate_name")) headers["Cookie"] = ( "APIC-Certificate-Algorithm=v1.0; " - + "APIC-Certificate-DN=%s; " % sig_dn + + "APIC-Certificate-DN={0}; ".format(sig_dn) + "APIC-Certificate-Fingerprint=fingerprint; " - + "APIC-Request-Signature=%s" % to_native(base64.b64encode(sig_signature)) + + "APIC-Request-Signature={0}".format(to_native(base64.b64encode(sig_signature))) ) return headers diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index cb4185b44..f604c6b1d 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -1,4 +1,3 @@ - # -*- coding: utf-8 -*- # This code is part of Ansible, but is an independent component @@ -356,8 +355,7 @@ def __init__(self, module): if self.params.get("private_key"): # Perform signature-based authentication, no need to log on separately if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL: - self.module.fail_json( - msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") + self.module.fail_json(msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available") elif self.params.get("password") is not None: self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required") elif self.connection is None: @@ -416,9 +414,9 @@ def login(self): # Perform login request if self.params.get("port") is not None: - url = "%(protocol)s://%(host)s:%(port)s/api/aaaLogin.json" % self.params + url = "{protocol}://{host}:{port}/api/aaaLogin.json".format_map(self.params) else: - url = "%(protocol)s://%(host)s/api/aaaLogin.json" % self.params + url = "{protocol}://{host}/api/aaaLogin.json".format_map(self.params) payload = { "aaaUser": { "attributes": { @@ -436,10 +434,10 @@ def login(self): try: # APIC error self.response_json(auth["body"]) - self.fail_json(msg="Authentication failed: %(code)s %(text)s" % self.error) + self.fail_json(msg="Authentication failed: {code} {text}".format_map(self.error)) except KeyError: # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % auth) + self.fail_json(msg="Connection failed for {url}. {msg}".format_map(auth)) # Retain cookie for later use self.headers["Cookie"] = resp.headers.get("Set-Cookie") @@ -478,7 +476,7 @@ def cert_auth(self, path=None, payload="", method=None): with open(self.params.get("private_key"), permission) as fh: private_key_content = fh.read() except Exception: - self.module.fail_json(msg="Cannot open private key file '%(private_key)s'." % self.params) + self.module.fail_json(msg="Cannot open private key file '{private_key}'.".format_map(self.params)) try: if HAS_CRYPTOGRAPHY: sig_key = serialization.load_pem_private_key( @@ -489,27 +487,28 @@ def cert_auth(self, path=None, payload="", method=None): else: sig_key = load_privatekey(FILETYPE_PEM, private_key_content) except Exception: - self.module.fail_json(msg="Cannot load private key file '%(private_key)s'." % self.params) + self.module.fail_json(msg="Cannot load private key file '{private_key}'.".format_map(self.params)) if self.params.get("certificate_name") is None: self.params["certificate_name"] = os.path.basename(os.path.splitext(self.params.get("private_key"))[0]) else: - self.module.fail_json(msg="Provided private key %(private_key)s does not appear to be a private key or provided file does not exist." - % self.params) + self.module.fail_json( + msg="Provided private key {private_key} does not appear to be a private key or provided file does not exist.".format_map(self.params) + ) - if self.params.get('certificate_name') is None: - self.params['certificate_name'] = self.params.get('username', 'admin') + if self.params.get("certificate_name") is None: + self.params["certificate_name"] = self.params.get("username", "admin") # NOTE: ACI documentation incorrectly adds a space between method and path sig_request = method + path + payload if HAS_CRYPTOGRAPHY: sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256()) else: sig_signature = sign(sig_key, sig_request, "sha256") - sig_dn = "uni/userext/user-%(username)s/usercert-%(certificate_name)s" % self.params + sig_dn = "uni/userext/user-{username}/usercert-{certificate_name}".format_map(self.params) self.headers["Cookie"] = ( "APIC-Certificate-Algorithm=v1.0; " - + "APIC-Certificate-DN=%s; " % sig_dn + + "APIC-Certificate-DN={0}; ".format(sig_dn) + "APIC-Certificate-Fingerprint=fingerprint; " - + "APIC-Request-Signature=%s" % to_native(base64.b64encode(sig_signature)) + + "APIC-Request-Signature={0}".format(to_native(base64.b64encode(sig_signature))) ) def response_json(self, rawoutput): @@ -518,7 +517,7 @@ def response_json(self, rawoutput): jsondata = json.loads(rawoutput) except Exception as e: # Expose RAW output for troubleshooting - self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e) + self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. {0}".format(e)) self.result["raw"] = rawoutput return @@ -540,7 +539,7 @@ def response_xml(self, rawoutput): xmldata = cobra.data(xml) except Exception as e: # Expose RAW output for troubleshooting - self.error = dict(code=-1, text="Unable to parse output as XML, see 'raw' output. %s" % e) + self.error = dict(code=-1, text="Unable to parse output as XML, see 'raw' output. {0}".format(e)) self.result["raw"] = rawoutput return @@ -1631,12 +1630,12 @@ def api_call(self, method, url, data=None, output=False): ======= def parsed_url_path(self, url): if not HAS_URLPARSE: - self.fail_json(msg='urlparse is not installed') + self.fail_json(msg="urlparse is not installed") parse_result = urlparse(url) - if parse_result.query == '': + if parse_result.query == "": return parse_result.path else: - return parse_result.path + '?' + parse_result.query + return parse_result.path + "?" + parse_result.query def api_call(self, method, url, data=None, return_response=False): >>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) @@ -1703,8 +1702,12 @@ def api_call(self, method, url, data=None, return_response=False): try: # APIC error self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) + self.fail_json(msg="APIC Error {code}: {text}".format_map(self.error)) except KeyError: # Connection error +<<<<<<< HEAD self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) >>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) +======= + self.fail_json(msg="Connection failed for {url}. {msg}".format_map(info)) +>>>>>>> a10677f ([ignore_changes] Applied the black format to the code) diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 582c1ecf3..4baa57b4f 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -285,12 +285,11 @@ def main(): aci.post_config() elif state == "preview": - aci.url = "%(protocol)s://%(host)s/mqapi2/snapshots.diff.xml" % module.params - aci.path = "mqapi2/snapshots.diff.xml" + aci.url = "{protocol}://{host}/mqapi2/snapshots.diff.xml".format_map(module.params) aci.filter_string = ( - "?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-%(export_policy)s]/snapshot-%(snapshot)s&" - "s2dn=uni/backupst/snapshots-[uni/fabric/configexp-%(compare_export_policy)s]/snapshot-%(compare_snapshot)s" - ) % module.params + "?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-{export_policy}]/snapshot-{snapshot}&" + "s2dn=uni/backupst/snapshots-[uni/fabric/configexp-{compare_export_policy}]/snapshot-{compare_snapshot}" + ).format_map(module.params) # Generate rollback comparison get_preview(aci) @@ -304,7 +303,7 @@ def get_preview(aci): """ uri = aci.url + aci.filter_string - resp, info = aci.api_call('GET', uri, data=None, return_response=True) + resp, info = aci.api_call("GET", uri, data=None, return_response=True) # Handle APIC response if info.get("status") == 200: @@ -317,9 +316,7 @@ def get_preview(aci): aci.result["raw"] = resp.read() except AttributeError: aci.result["raw"] = info.get("body") - aci.fail_json( - msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error - ) + aci.fail_json(msg="Request failed: {code} {text} (see 'raw' output)".format_map(aci.error)) def xml_to_json(aci, response_data): diff --git a/plugins/modules/aci_config_snapshot.py b/plugins/modules/aci_config_snapshot.py index b147cefe3..c141ab5f1 100644 --- a/plugins/modules/aci_config_snapshot.py +++ b/plugins/modules/aci_config_snapshot.py @@ -306,10 +306,10 @@ def main(): # Query for job information and add to results path = "/api/node/mo/uni/backupst/jobs-[uni/fabric/configexp-{0}].json".format(export_policy) if "port" in aci.params and aci.params.get("port") is not None: - port_url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") + port_url = "{protocol}://{host}:{port}/".format_map(aci.params) + path.lstrip("/") else: - port_url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") - resp, info = aci.api_call('GET', port_url, data=None, return_response=True) + port_url = "{protocol}://{host}/".format_map(aci.params) + path.lstrip("/") + resp, info = aci.api_call("GET", port_url, data=None, return_response=True) try: aci.imdata = json.loads(resp.read())["imdata"] except AttributeError: @@ -318,7 +318,6 @@ def main(): aci.result["job_details"] = aci.imdata[0].get("configJobCont", {}) # Reset state and url to display correct in output and trigger get_existing() function with correct url aci.url = reset_url - aci.params["state"] = "present" else: # Prefix the proper url to export_policy diff --git a/plugins/modules/aci_rest.py b/plugins/modules/aci_rest.py index 376fe140c..d69051f8c 100644 --- a/plugins/modules/aci_rest.py +++ b/plugins/modules/aci_rest.py @@ -358,7 +358,7 @@ def main(): if os.path.isfile(src): file_exists = True else: - module.fail_json(msg="Cannot find/access src '%s'" % src) + module.fail_json(msg="Cannot find/access src '{0}'".format(src)) # Find request type if path.find(".xml") != -1: @@ -392,7 +392,7 @@ def main(): # Validate YAML/JSON string payload = json.dumps(yaml.safe_load(payload)) except Exception as e: - module.fail_json(msg="Failed to parse provided JSON/YAML payload: %s" % to_text(e), exception=to_text(e), payload=payload) + module.fail_json(msg="Failed to parse provided JSON/YAML payload: {0}".format(to_text(e)), exception=to_text(e), payload=payload) elif rest_type == "xml" and HAS_LXML_ETREE: if content and isinstance(content, dict) and HAS_XMLJSON_COBRA: # Validate inline YAML/JSON @@ -402,13 +402,13 @@ def main(): # Validate XML string payload = etree.tostring(etree.fromstring(payload), encoding="unicode") except Exception as e: - module.fail_json(msg="Failed to parse provided XML payload: %s" % to_text(e), payload=payload) + module.fail_json(msg="Failed to parse provided XML payload: {0}".format(to_text(e)), payload=payload) # Perform actual request using auth cookie (Same as aci.request(), but also supports XML) if "port" in aci.params and aci.params.get("port") is not None: - aci.url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/") + aci.url = "{protocol}://{host}:{port}/".format_map(aci.params) + path.lstrip("/") else: - aci.url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/") + aci.url = "{protocol}://{host}/".format_map(aci.params) + path.lstrip("/") if aci.params.get("method") != "get" and not rsp_subtree_preserve: aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"}) @@ -422,15 +422,15 @@ def main(): try: # APIC error aci.response_type(info["body"], rest_type) - aci.fail_json(msg="APIC Error %(code)s: %(text)s" % aci.error) + aci.fail_json(msg="APIC Error {code}: {text}".format_map(aci.error)) except KeyError: # Connection error - aci.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) + aci.fail_json(msg="Connection failed for {url}. {msg}".format_map(info)) try: aci.response_type(resp.read(), rest_type) except AttributeError: - aci.response_type(info.get('body'), rest_type) + aci.response_type(info.get("body"), rest_type) aci.result["status"] = aci.status aci.result["imdata"] = aci.imdata diff --git a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml index 05dc1081c..214f55fe2 100644 --- a/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml +++ b/tests/integration/targets/aci_tenant/tasks/httpapi_connection.yml @@ -74,19 +74,43 @@ ignore_errors: true register: wrong_credentials +- name: Set the username to null + ansible.builtin.set_fact: + ansible_user: + +- name: Delete Tenant with no username in the task or inventory (Check that username is set to the default value) + cisco.aci.aci_tenant: + host: "{{ aci_hostname }}" + password: "{{ aci_password }}" + validate_certs: "{{ aci_validate_certs | default(false) }}" + use_ssl: "{{ aci_use_ssl | default(true) }}" + use_proxy: "{{ aci_use_proxy | default(true) }}" + timeout: 5 + output_level: debug + tenant: ansible_test + state: absent + ignore_errors: true + register: no_username + +- name: Revert the username to its original value + ansible.builtin.set_fact: + ansible_user: "{{ aci_username }}" + - name: Flatten the registered instances ansible.builtin.set_fact: ap_non_existent_tenant_flattened: "{{ ap_non_existent_tenant.httpapi_logs | flatten }}" wrong_credentials_flattened: "{{ wrong_credentials.httpapi_logs | flatten }}" + no_username_flattened: "{{ no_username.httpapi_logs | flatten }}" - name: Verify XML operation and HTTP error returned by APIC assert: that: - tenant_xml_plugin.status == 200 - '"Received response from {{ aci_hostname }} for POST operation with HTTP: 400" in ap_non_existent_tenant_flattened' - - '"Re-setting connection due to change in username" in wrong_credentials_flattened' - - '"Re-setting connection due to change in password" in wrong_credentials_flattened' + - '"Re-setting connection due to change in the username" in wrong_credentials_flattened' + - '"Re-setting connection due to change in the password" in wrong_credentials_flattened' - '"Connection to {{ aci_hostname }} has failed: HTTP Error 401: Unauthorized" in wrong_credentials_flattened' + - '"Establishing login for admin to {{ aci_hostname }}" in no_username_flattened' # Simulate HTTP 403 error test - name: Delete Tenant with only password in the task (Check for 403) From ac3c2e619637f2e42fbfaaf8165c65e2b6718849 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 17 May 2023 15:28:13 -0400 Subject: [PATCH 40/49] [ignore_changes] Applied galaxy importer changes --- plugins/modules/aci_config_rollback.py | 2 -- .../targets/aci_tenant/filter_plugins/generate_ips.py | 11 +++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/modules/aci_config_rollback.py b/plugins/modules/aci_config_rollback.py index 4baa57b4f..fced5944c 100644 --- a/plugins/modules/aci_config_rollback.py +++ b/plugins/modules/aci_config_rollback.py @@ -197,8 +197,6 @@ from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_bytes -from ansible.module_utils.urls import fetch_url -from ansible.module_utils.connection import Connection # Optional, only used for rollback preview try: diff --git a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py index c9d5dcfdd..130fbb786 100644 --- a/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py +++ b/tests/integration/targets/aci_tenant/filter_plugins/generate_ips.py @@ -1,3 +1,10 @@ +# Copyright (c) 2023 Cisco and/or its affiliates. +# Copyright: (c) 2023, Shreyas Srish (@shrsr) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + from ipaddress import ip_network import random @@ -7,7 +14,7 @@ class FilterModule(object): def filters(self): return { - 'generate_random_ips': self.generate_random_ips, + "generate_random_ips": self.generate_random_ips, } def generate_random_ips(self, given_ip, insert_given_ip_at, number_of_ips): @@ -18,4 +25,4 @@ def generate_random_ips(self, given_ip, insert_given_ip_at, number_of_ips): else: ips += str((random.choice(RANGE_IPV4))) ips += "," - return ips.rstrip(',') + return ips.rstrip(",") From e20ce34442314a5186ce20431433cd8d0c354912 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 17 May 2023 15:33:50 -0400 Subject: [PATCH 41/49] [ignore_changes] Applied black format to functions in aci.py --- plugins/module_utils/aci.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index f604c6b1d..1bf8e10dd 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -625,7 +625,6 @@ def _deep_url_path_builder(self, obj): ) def _deep_url_parent_object(self, parent_objects, parent_class): - for parent_object in parent_objects: if parent_object.get("aci_class") is parent_class: return parent_object @@ -774,7 +773,6 @@ def construct_url( child_classes=None, config_only=True, ): - """ This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC. @@ -1504,7 +1502,6 @@ def exit_json(self, filter_existing=None, **kwargs): self.module.exit_json(**self.result) def fail_json(self, msg, **kwargs): - # Return error information, if we have it if self.error.get("code") is not None and self.error.get("text") is not None: self.result["error"] = self.error From baa04238c40f081158bc0c3c17c6e3b458ef3396 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Wed, 17 May 2023 15:40:09 -0400 Subject: [PATCH 42/49] [ignore_changes] Applied dependency netcommon --- galaxy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/galaxy.yml b/galaxy.yml index 4ec17c75a..0ef4a09b0 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -25,6 +25,8 @@ tags: - collection - networking - sdn +dependencies: + "ansible.netcommon": "*" repository: https://github.com/CiscoDevNet/ansible-aci documentation: https://docs.ansible.com/ansible/latest/scenario_guides/guide_aci.html homepage: https://github.com/CiscoDevNet/ansible-aci From cedfac126257c572285f40ea852fa367118ed788 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 18 May 2023 15:07:59 -0400 Subject: [PATCH 43/49] [ignore_changes] Applied changes to the bd_subnet test to check APIC versions --- .../targets/aci_bd_subnet/tasks/main.yml | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/tests/integration/targets/aci_bd_subnet/tasks/main.yml b/tests/integration/targets/aci_bd_subnet/tasks/main.yml index f9833cf75..12ff83cba 100644 --- a/tests/integration/targets/aci_bd_subnet/tasks/main.yml +++ b/tests/integration/targets/aci_bd_subnet/tasks/main.yml @@ -8,6 +8,24 @@ msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +- name: Query system information + aci_system: + <<: *aci_info + id: 1 + state: query + register: version + - name: Verify Cloud and Non-Cloud Sites in use. include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml @@ -60,7 +78,7 @@ <<: *aci_subnet_present register: create_subnet - - name: create new subnet - creation works + - name: create new subnet - creation works (with no ip_data_plane_learning - APIC version < 5.0) cisco.aci.aci_bd_subnet: &aci_subnet2_present <<: *aci_subnet2_absent state: present @@ -68,8 +86,20 @@ scope: [private, shared] route_profile: default route_profile_l3_out: default - ip_data_plane_learning: disabled register: create_subnet2 + when: version.current.0.topSystem.attributes.version is version('5', '<') + + - name: create new subnet - creation works (with ip_data_plane_learning - APIC version >= 5.0) + cisco.aci.aci_bd_subnet: + <<: *aci_subnet2_present + state: present + descr: Ansible Test + scope: [private, shared] + route_profile: default + route_profile_l3_out: default + ip_data_plane_learning: disabled + register: create_subnet2_5 + when: version.current.0.topSystem.attributes.version is version('5', '>=') - name: create subnet again - idempotency works cisco.aci.aci_bd_subnet: @@ -97,7 +127,7 @@ register: create_incomplete_data ignore_errors: true - - name: asserts for subnet creation tasks + - name: assert for subnet creation tasks assert: that: - create_check_mode is changed @@ -106,14 +136,7 @@ - create_check_mode.sent.fvSubnet.attributes.name == create_subnet.sent.fvSubnet.attributes.name == 'anstest' - create_subnet is changed - create_subnet.current.0.fvSubnet.attributes.annotation == 'orchestrator:ansible' - - create_subnet.current.0.fvSubnet.attributes.ipDPLearning == 'enabled' - create_subnet.previous == [] - - create_subnet2 is changed - - create_subnet2.current.0.fvSubnet.attributes.ipDPLearning == 'disabled' - - create_subnet2.sent == create_subnet2.proposed - - create_subnet2.sent.fvSubnet.attributes.scope == "private,shared" - - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnL3extOutName == 'default' - - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnRtctrlProfileName == 'default' - create_idempotency is not changed - create_idempotency.previous != [] - modify_subnet is changed @@ -126,6 +149,28 @@ - create_incomplete_data is failed - 'create_incomplete_data.msg == "state is present but all of the following are missing: bd"' + - name: assert for subnet for task with version < 5 + assert: + that: + - create_subnet2 is changed + - create_subnet2.sent == create_subnet2.proposed + - create_subnet2.sent.fvSubnet.attributes.scope == "private,shared" + - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnL3extOutName == 'default' + - create_subnet2.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnRtctrlProfileName == 'default' + when: version.current.0.topSystem.attributes.version is version('5', '<') + + - name: assert for subnet ip_data_learning for task with version >=5 + assert: + that: + - create_subnet.current.0.fvSubnet.attributes.ipDPLearning == 'enabled' + - create_subnet2_5 is changed + - create_subnet2_5.current.0.fvSubnet.attributes.ipDPLearning == 'disabled' + - create_subnet2_5.sent == create_subnet2_5.proposed + - create_subnet2_5.sent.fvSubnet.attributes.scope == "private,shared" + - create_subnet2_5.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnL3extOutName == 'default' + - create_subnet2_5.sent.fvSubnet.children.0.fvRsBDSubnetToProfile.attributes.tnRtctrlProfileName == 'default' + when: version.current.0.topSystem.attributes.version is version('5', '>=') + - name: get all subnets cisco.aci.aci_bd_subnet: &aci_query <<: *aci_tenant_present From 01133df46270678c8e2b90348afb6029115863b9 Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 16 Jul 2023 08:54:45 -0400 Subject: [PATCH 44/49] [ignore_change] Manual change to aci.py to incorporate recent changes in other PRs --- plugins/module_utils/aci.py | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 1bf8e10dd..a4489fa34 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -14,10 +14,15 @@ # Copyright: (c) 2020, Lionel Hercot (@lhercot) # Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) <<<<<<< HEAD +<<<<<<< HEAD # Copyright: (c) 2023, Gaspard Micol (@gmicol) ======= # Copyright: (c) 2020, Shreyas Srish (@shrsr) >>>>>>> 02c53f6 (Check Sanity) +======= +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# Copyright: (c) 2023, Shreyas Srish (@shrsr) +>>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) # All rights reserved. # Redistribution and use in source and binary forms, with or without modification, @@ -305,6 +310,20 @@ def destination_epg_spec(): ), ) +<<<<<<< HEAD +======= +def ospf_spec(): + return dict( + area_cost=dict(type="int"), + area_ctrl=dict(type="list", elements="str", choices=["redistribute", "summary", "suppress-fa", "unspecified"]), + area_id=dict(type="str"), + area_type=dict(type="str", choices=["nssa", "regular", "stub"]), + description=dict(type="str", aliases=["descr"]), + multipod_internal=dict(type="str", choices=["no", "yes"]), + name_alias=dict(type="str"), + ) + +>>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) class ACIModule(object): def __init__(self, module): @@ -1393,6 +1412,9 @@ def post_config(self, parent_class=None): <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) url = self.url if parent_class is not None: if self.params.get("port") is not None: @@ -1400,6 +1422,7 @@ def post_config(self, parent_class=None): else: url = "{protocol}://{host}/{path}".format(path=self.parent_path, **self.module.params) self.config = {parent_class: {"attributes": {}, "children": [self.config]}} +<<<<<<< HEAD if self.params.get("private_key"): self.cert_auth(method="POST", payload=json.dumps(self.config)) @@ -1450,6 +1473,9 @@ def post_config(self, parent_class=None): ======= self.api_call("POST", self.url, json.dumps(self.config), return_response=False) >>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) +======= + self.api_call("POST", url, json.dumps(self.config), return_response=False) +>>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) else: self.result["changed"] = True self.method = "POST" @@ -1564,6 +1590,22 @@ def dump_json(self): with open(output_path, "a") as output_file: if self.result.get("changed") is True: json.dump([mo], output_file) + + def delete_config_request(self, path): + self._config_request(path, "absent") + self.result["changed"] = True + + def get_config_request(self, path): + self._config_request(path, "query") + return self.imdata + + def _config_request(self, path, state): + reset_url = self.url + reset_state = self.params["state"] + self.params["state"] = state + self.request(path) + self.url = reset_url + self.params["state"] = reset_state <<<<<<< HEAD <<<<<<< HEAD @@ -1640,7 +1682,6 @@ def api_call(self, method, url, data=None, return_response=False): if self.connection is not None: self.connection.set_params(self.params) info = self.connection.send_request(method, self.parsed_url_path(url), data) - self.url = info.get("url") self.error = info.get("error") self.httpapi_logs.extend(self.connection.pop_messages()) else: From 53e7136736a75c3367b7fd87fd652087ef60aecd Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 16 Jul 2023 09:12:11 -0400 Subject: [PATCH 45/49] [ignore_change] Changed position of ospf_spec in aci.py --- plugins/module_utils/aci.py | 157 ------------------------------------ 1 file changed, 157 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index a4489fa34..896b22891 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -13,16 +13,8 @@ # Copyright: (c) 2019, Rob Huelga (@RobW3LGA) # Copyright: (c) 2020, Lionel Hercot (@lhercot) # Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) -<<<<<<< HEAD -<<<<<<< HEAD -# Copyright: (c) 2023, Gaspard Micol (@gmicol) -======= -# Copyright: (c) 2020, Shreyas Srish (@shrsr) ->>>>>>> 02c53f6 (Check Sanity) -======= # Copyright: (c) 2023, Gaspard Micol (@gmicol) # Copyright: (c) 2023, Shreyas Srish (@shrsr) ->>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) # All rights reserved. # Redistribution and use in source and binary forms, with or without modification, @@ -310,8 +302,6 @@ def destination_epg_spec(): ), ) -<<<<<<< HEAD -======= def ospf_spec(): return dict( area_cost=dict(type="int"), @@ -323,7 +313,6 @@ def ospf_spec(): name_alias=dict(type="str"), ) ->>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) class ACIModule(object): def __init__(self, module): @@ -1406,15 +1395,6 @@ def post_config(self, parent_class=None): return elif not self.module.check_mode: # Sign and encode request as to APIC's wishes -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) url = self.url if parent_class is not None: if self.params.get("port") is not None: @@ -1422,60 +1402,7 @@ def post_config(self, parent_class=None): else: url = "{protocol}://{host}/{path}".format(path=self.parent_path, **self.module.params) self.config = {parent_class: {"attributes": {}, "children": [self.config]}} -<<<<<<< HEAD - if self.params.get("private_key"): - self.cert_auth(method="POST", payload=json.dumps(self.config)) - - resp, info = fetch_url( - self.module, - url, - data=json.dumps(self.config), - headers=self.headers, - method="POST", - timeout=self.params.get("timeout"), - use_proxy=self.params.get("use_proxy"), - ) - - self.response = info.get("msg") - self.status = info.get("status") - self.method = "POST" - - # Handle APIC response - if info.get("status") == 200: - self.result["changed"] = True - self.response_json(resp.read()) - else: - try: - # APIC error - self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) -======= - self.call("POST") ->>>>>>> da2af7d (retry w/o conflict) -======= - self.call("POST") -======= - self.api_call('POST') ->>>>>>> eeef0dc ([ignore] Change location of persistent storage file) ->>>>>>> d17daa6 ([ignore] Change location of persistent storage file) -======= - self.api_call("POST") ->>>>>>> a77aefa ([ignore] Addition of queue messages) -======= - self.api_call("POST", self.path, self.url, json.dumps(self.config), output=False) ->>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) -======= - self.api_call("POST", self.url, json.dumps(self.config), output=False) ->>>>>>> f20fdfe ([ignore_changes] New test file created) -======= - self.api_call("POST", self.url, json.dumps(self.config), return_response=False) ->>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) -======= self.api_call("POST", url, json.dumps(self.config), return_response=False) ->>>>>>> d23cc89 ([ignore_change] Manual change to aci.py to incorporate recent changes in other PRs) else: self.result["changed"] = True self.method = "POST" @@ -1607,66 +1534,6 @@ def _config_request(self, path, state): self.url = reset_url self.params["state"] = reset_state -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - def delete_config_request(self, path): - self._config_request(path, "absent") - self.result["changed"] = True - - def get_config_request(self, path): - self._config_request(path, "query") - return self.imdata - - def _config_request(self, path, state): - reset_url = self.url - reset_state = self.params["state"] - self.params["state"] = state - self.request(path) - self.url = reset_url - self.params["state"] = reset_state - - -def ospf_spec(): - return dict( - area_cost=dict(type="int"), - area_ctrl=dict(type="list", elements="str", choices=["redistribute", "summary", "suppress-fa", "unspecified"]), - area_id=dict(type="str"), - area_type=dict(type="str", choices=["nssa", "regular", "stub"]), - description=dict(type="str", aliases=["descr"]), - multipod_internal=dict(type="str", choices=["no", "yes"]), - name_alias=dict(type="str"), - ) -======= - def call(self, method): -======= - def api_call(self, method): -<<<<<<< HEAD ->>>>>>> d17daa6 ([ignore] Change location of persistent storage file) - if method == 'GET': -======= - if method == "GET": ->>>>>>> 5d0ead5 ([minor_change] Fix comments and formatting) - call_path = self.path + self.filter_string - call_url = self.url + self.filter_string - data = None - elif method == "POST": - call_path = self.path - call_url = self.url - data = json.dumps(self.config) - elif method == "DELETE": - call_path = self.path - call_url = self.url - data = None -======= - def api_call(self, method, path, url, data=None, output=False): ->>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) -======= - def api_call(self, method, url, data=None, output=False): ->>>>>>> f20fdfe ([ignore_changes] New test file created) -======= def parsed_url_path(self, url): if not HAS_URLPARSE: self.fail_json(msg="urlparse is not installed") @@ -1677,7 +1544,6 @@ def parsed_url_path(self, url): return parse_result.path + "?" + parse_result.query def api_call(self, method, url, data=None, return_response=False): ->>>>>>> a774f00 ([ignore_changes] Changes made to test files for intergration tests) resp = None if self.connection is not None: self.connection.set_params(self.params) @@ -1704,24 +1570,6 @@ def api_call(self, method, url, data=None, return_response=False): if return_response: return resp, info else: -<<<<<<< HEAD - try: - # APIC error - self.response_json(info["body"]) - self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error) - except KeyError: - # Connection error -<<<<<<< HEAD - self.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info) -<<<<<<< HEAD - ->>>>>>> da2af7d (retry w/o conflict) -======= ->>>>>>> 02c53f6 (Check Sanity) -======= - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) ->>>>>>> 5d0ead5 ([minor_change] Fix comments and formatting) -======= # Handle APIC response if info.get("status") == 200: if method == "POST" or method == "DELETE": @@ -1743,9 +1591,4 @@ def api_call(self, method, url, data=None, return_response=False): self.fail_json(msg="APIC Error {code}: {text}".format_map(self.error)) except KeyError: # Connection error -<<<<<<< HEAD - self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info) ->>>>>>> 88b1ff5 ([minor_change] Removed different functions to make requests which can now be done by using just one function) -======= self.fail_json(msg="Connection failed for {url}. {msg}".format_map(info)) ->>>>>>> a10677f ([ignore_changes] Applied the black format to the code) From 5cf9a47accab561c8f00f50cee8c43c0e4fe990d Mon Sep 17 00:00:00 2001 From: Shreyas Date: Sun, 16 Jul 2023 22:22:33 -0400 Subject: [PATCH 46/49] [ignore_change] Changed the call from request function to api_call in certain modules --- plugins/httpapi/aci.py | 15 ++++++------ plugins/module_utils/aci.py | 24 +------------------ plugins/modules/aci_access_span_src_group.py | 2 +- .../modules/aci_access_span_src_group_src.py | 8 +++---- .../modules/aci_fabric_span_src_group_src.py | 4 ++-- tests/integration/inventory.networking | 6 ++--- 6 files changed, 18 insertions(+), 41 deletions(-) diff --git a/plugins/httpapi/aci.py b/plugins/httpapi/aci.py index 415fe8ddc..5105223cf 100644 --- a/plugins/httpapi/aci.py +++ b/plugins/httpapi/aci.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Cisco and/or its affiliates. +# Copyright (c) 2020 Cisco and/or its affiliates. # Copyright: (c) 2020, Shreyas Srish (@shrsr) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) # @@ -26,7 +26,7 @@ - Shreyas Srish (@shrsr) short_description: Ansible ACI HTTPAPI Plugin. description: - - This ACI plugin provides the HTTPAPI transport methods needed to initiate + - This ACI plugin provides the HTTPAPI methods needed to initiate a connection to the APIC, send API requests and process the response from the controller. """ @@ -113,7 +113,7 @@ def logout(self): msg = "Error on attempt to logout from APIC. {0}".format(exc_logout) raise ConnectionError(self._return_info("", method, path, msg)) self.connection._auth = None - self._verify_response(response, method, path, response_data) + self._verify_response(response, method, response_data) def set_parameters(self): connection_parameters = {} @@ -122,7 +122,7 @@ def set_parameters(self): if key == "username" and value is None: value = "admin" self.connection.set_option(CONNECTION_MAP.get(key, key), value) - if key == "timeout" and self.params.get(key) is not None: + if key == "timeout" and self.connection.get_option("persistent_connect_timeout") <= value: self.connection.set_option("persistent_connect_timeout", value + 30) connection_parameters[key] = value @@ -220,7 +220,7 @@ def send_request(self, method, path, data): # recurse through function for retrying the request return self.send_request(method, path, data) # return statement executed upon each successful response from the request function - return self._verify_response(response, method, path, response_data) + return self._verify_response(response, method, response_data) # Built-in-function def handle_httperror(self, exc): @@ -240,17 +240,16 @@ def validate_url(self, url): else: return validated_url - def _verify_response(self, response, method, path, response_data): + def _verify_response(self, response, method, response_data): """Process the return code and response object from APIC""" response_value = self._get_response_value(response_data) response_code = response.getcode() - path = self.validate_url(response.url) # Response check to remain consistent with fetch_url's response if str(response) == "HTTP Error 400: Bad Request": msg = "{0}".format(response) else: msg = "{0} ({1} bytes)".format(response.msg, len(response_value)) - return self._return_info(response_code, method, path, msg, respond_data=response_value) + return self._return_info(response_code, method, "", msg, respond_data=response_value) def _get_response_value(self, response_data): """Extract string data from response_data returned from APIC""" diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 896b22891..7a274d498 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -302,6 +302,7 @@ def destination_epg_spec(): ), ) + def ospf_spec(): return dict( area_cost=dict(type="int"), @@ -406,13 +407,6 @@ def define_protocol(self): # Set protocol for further use self.params["protocol"] = "https" if self.params.get("use_ssl", True) else "http" - def define_method(self): - """Set method based on state parameter""" - - # Set method for further use - state_map = dict(absent="delete", present="post", query="get") - self.params["method"] = state_map.get(self.params.get("state")) - def set_connection(self): if self.connection is None and self.module._socket_path: self.connection = Connection(self.module._socket_path) @@ -1517,22 +1511,6 @@ def dump_json(self): with open(output_path, "a") as output_file: if self.result.get("changed") is True: json.dump([mo], output_file) - - def delete_config_request(self, path): - self._config_request(path, "absent") - self.result["changed"] = True - - def get_config_request(self, path): - self._config_request(path, "query") - return self.imdata - - def _config_request(self, path, state): - reset_url = self.url - reset_state = self.params["state"] - self.params["state"] = state - self.request(path) - self.url = reset_url - self.params["state"] = reset_state def parsed_url_path(self, url): if not HAS_URLPARSE: diff --git a/plugins/modules/aci_access_span_src_group.py b/plugins/modules/aci_access_span_src_group.py index 350b4f9ca..fb6f6a35b 100644 --- a/plugins/modules/aci_access_span_src_group.py +++ b/plugins/modules/aci_access_span_src_group.py @@ -293,7 +293,7 @@ def main(): # } # } # ) - aci.delete_config_request("/api/mo/uni/infra/srcgrp-{0}/rssrcGrpToFilterGrp.json".format(source_group)) + aci.api_call("DELETE", "/api/mo/uni/infra/srcgrp-{0}/rssrcGrpToFilterGrp.json".format(source_group)) elif child.get("spanSpanLbl") and child.get("spanSpanLbl").get("attributes").get("name") != destination_group: child_configs.append( { diff --git a/plugins/modules/aci_access_span_src_group_src.py b/plugins/modules/aci_access_span_src_group_src.py index 2419f8aff..f8ff4349e 100644 --- a/plugins/modules/aci_access_span_src_group_src.py +++ b/plugins/modules/aci_access_span_src_group_src.py @@ -388,7 +388,7 @@ def main(): # Commented validate code to avoid making additional API request which is handled by APIC error # Keeping for informational purposes # Validate drop_packets are set on parent correctly - # if aci.get_config_request("{0}/rssrcGrpToFilterGrp.json".format(source_group_path)) != [] and drop_packets: + # if aci.api_call("GET", "{0}/rssrcGrpToFilterGrp.json".format(source_group_path)) != [] and drop_packets: # module.fail_json(msg="It is not allowed to configure 'drop_packets: true' when a filter group is configured on the source group.") source_path = "/api/mo/uni/infra/srcgrp-{0}/src-{1}".format(source_group, source) @@ -408,13 +408,13 @@ def main(): # } # } # ) - aci.delete_config_request("{0}/rssrcToFilterGrp.json".format(source_path)) + aci.api_call("DELETE", "{0}/rssrcToFilterGrp.json".format(source_path)) elif child.get("spanRsSrcToEpg") and child.get("spanRsSrcToEpg").get("attributes").get("tDn") != epg_dn: # Appending to child_config list not possible because of APIC Error 103: child (Rn) of class spanRsSrcToEpg is already attached. - aci.delete_config_request("{0}/rssrcToEpg.json".format(source_path)) + aci.api_call("DELETE", "{0}/rssrcToEpg.json".format(source_path)) elif child.get("spanRsSrcToL3extOut") and child.get("spanRsSrcToL3extOut").get("attributes").get("tDn") != l3ext_out_dn: # Appending to child_config list not possible because of APIC Error 103: child (Rn) of class spanRsSrcToL3extOut is already attached. - aci.delete_config_request("{0}/rssrcToL3extOut.json".format(source_path)) + aci.api_call("DELETE", "{0}/rssrcToL3extOut.json".format(source_path)) aci.payload( aci_class="spanSrc", diff --git a/plugins/modules/aci_fabric_span_src_group_src.py b/plugins/modules/aci_fabric_span_src_group_src.py index b4afca730..b4b722e41 100644 --- a/plugins/modules/aci_fabric_span_src_group_src.py +++ b/plugins/modules/aci_fabric_span_src_group_src.py @@ -385,10 +385,10 @@ def main(): for child in aci.existing[0].get("spanSrc", {}).get("children", {}): if child.get("spanRsSrcToCtx") and child.get("spanRsSrcToCtx").get("attributes").get("tDn") != vrf_dn: # Appending to child_config list not possible because of APIC Error 103: child (Rn) of class spanRsSrcToCtx is already attached. - aci.delete_config_request("{0}/rssrcToCtx.json".format(source_path)) + aci.api_call("DELETE", "{0}/rssrcToCtx.json".format(source_path)) elif child.get("spanRsSrcToBD") and child.get("spanRsSrcToBD").get("attributes").get("tDn") != bd_dn: # Appending to child_config list not possible because of APIC Error 103: child (Rn) of class spanRsSrcToBD is already attached. - aci.delete_config_request("{0}/rssrcToBD.json".format(source_path)) + aci.api_call("DELETE", "{0}/rssrcToBD.json".format(source_path)) aci.payload( aci_class="spanSrc", diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 0c1c59744..0e9c2e882 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -3,12 +3,12 @@ cn-dmz-apic-m1-02-v42 ansible_host=173.36.219.68 aci_hostname=173.36.219.68 cn-dmz-apic-m1-03-v52 ansible_host=173.36.219.69 aci_hostname=173.36.219.69 cn-dmz-apic-m1-04-v60 ansible_host=173.36.219.70 aci_hostname=173.36.219.70 cn-dmz-apic-m1-07-v32 ansible_host=173.36.219.73 aci_hostname=173.36.219.73 -aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a -azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +#aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a +#azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=azure region=westus region_2=westus2 vnet_gateway=true [aci:vars] aci_username=ansible_github_ci aci_password="sJ94G92#8dq2hx*K4qh" ansible_network_os=cisco.aci.aci -ansible_connection=ansible.netcommon.httpapi +ansible_connection=local ansible_python_interpreter=/usr/bin/python3.9 From d438e76df61cf17cde0b10e9a529fc84eb3516dc Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 17 Jul 2023 07:38:41 -0400 Subject: [PATCH 47/49] [ignore_change] Removed comments from the inventory --- tests/integration/inventory.networking | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 0e9c2e882..357f58758 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -3,8 +3,8 @@ cn-dmz-apic-m1-02-v42 ansible_host=173.36.219.68 aci_hostname=173.36.219.68 cn-dmz-apic-m1-03-v52 ansible_host=173.36.219.69 aci_hostname=173.36.219.69 cn-dmz-apic-m1-04-v60 ansible_host=173.36.219.70 aci_hostname=173.36.219.70 cn-dmz-apic-m1-07-v32 ansible_host=173.36.219.73 aci_hostname=173.36.219.73 -#aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a -#azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a +azure_cloud ansible_host=20.245.236.136 aci_hostname=20.245.236.136 cloud_type=azure region=westus region_2=westus2 vnet_gateway=true [aci:vars] aci_username=ansible_github_ci From 9f4ab6fd79cf453bfcda35fcb72e6c4dc1c75e3a Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 17 Jul 2023 19:07:03 -0400 Subject: [PATCH 48/49] [ignore_change] Changed the constant file to remove ssh to port number mapping --- plugins/module_utils/constants.py | 2 +- tests/integration/targets/aci_filter_entry/tasks/main.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 8788bd32a..77c3217e6 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -1,6 +1,6 @@ VALID_IP_PROTOCOLS = ["eigrp", "egp", "icmp", "icmpv6", "igmp", "igp", "l2tp", "ospfigp", "pim", "tcp", "udp", "unspecified"] -FILTER_PORT_MAPPING = {"443": "https", "25": "smtp", "80": "http", "53": "dns", "22": "ssh", "110": "pop3", "554": "rtsp", "20": "ftpData", "ftp": "ftpData"} +FILTER_PORT_MAPPING = {"443": "https", "25": "smtp", "80": "http", "53": "dns", "110": "pop3", "554": "rtsp", "20": "ftpData", "ftp": "ftpData"} VALID_ETHER_TYPES = ["arp", "fcoe", "ip", "ipv4", "ipv6", "mac_security", "mpls_ucast", "trill", "unspecified"] diff --git a/tests/integration/targets/aci_filter_entry/tasks/main.yml b/tests/integration/targets/aci_filter_entry/tasks/main.yml index 5461617c8..2f1bcdc53 100644 --- a/tests/integration/targets/aci_filter_entry/tasks/main.yml +++ b/tests/integration/targets/aci_filter_entry/tasks/main.yml @@ -78,8 +78,8 @@ ether_type: ip ip_protocol: tcp source_port: 20 - source_port_start: 22 - source_port_end: 22 + source_port_start: 80 + source_port_end: 80 register: nt_source_port ignore_errors: true @@ -119,7 +119,7 @@ ether_type: ip ip_protocol: tcp source_port_start: 20 - source_port_end: 22 + source_port_end: 80 tcp_flags: - acknowledgment - finish @@ -194,7 +194,7 @@ - source_port_values is changed - source_port_values.current.0.vzEntry.attributes.name == source_port_values.sent.vzEntry.attributes.name == "source_port_values" - source_port_values.current.0.vzEntry.attributes.sFromPort == source_port_values.sent.vzEntry.attributes.sFromPort == "ftpData" - - source_port_values.current.0.vzEntry.attributes.sToPort == source_port_values.sent.vzEntry.attributes.sToPort == "ssh" + - source_port_values.current.0.vzEntry.attributes.sToPort == source_port_values.sent.vzEntry.attributes.sToPort == "http" - source_port_values.current.0.vzEntry.attributes.tcpRules == source_port_values.sent.vzEntry.attributes.tcpRules == "ack,fin" - source_port_values.current.0.vzEntry.attributes.applyToFrag == "no" - source_port_values.current.0.vzEntry.attributes.arpOpc == "unspecified" From 0bbd2b38865ec3967f28690ece73f504a5e9fbea Mon Sep 17 00:00:00 2001 From: Shreyas Date: Thu, 20 Jul 2023 16:59:24 -0400 Subject: [PATCH 49/49] [ignore] Added collection name to a task module in the bd_subnet test file --- tests/integration/targets/aci_bd_subnet/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/aci_bd_subnet/tasks/main.yml b/tests/integration/targets/aci_bd_subnet/tasks/main.yml index 12ff83cba..8e7a32b81 100644 --- a/tests/integration/targets/aci_bd_subnet/tasks/main.yml +++ b/tests/integration/targets/aci_bd_subnet/tasks/main.yml @@ -20,7 +20,7 @@ output_level: debug - name: Query system information - aci_system: + cisco.aci.aci_system: <<: *aci_info id: 1 state: query