From 417da6c1012a75085837fc0040234c7766534af4 Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Wed, 11 Sep 2024 01:00:56 +0000 Subject: [PATCH 1/8] Merging devel changes - 2024-09-11T01:00:46Z --- .../f5os/changelogs/.plugin-cache.yaml | 166 ------------------ .../f5networks/f5os/galaxy.yml | 2 +- .../f5os/plugins/module_utils/version.py | 2 +- 3 files changed, 2 insertions(+), 168 deletions(-) delete mode 100644 ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml diff --git a/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml b/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml deleted file mode 100644 index 4844bae..0000000 --- a/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml +++ /dev/null @@ -1,166 +0,0 @@ -objects: - role: {} -plugins: - become: {} - cache: {} - callback: {} - cliconf: {} - connection: {} - filter: {} - httpapi: - f5os: - description: HttpApi Plugin for F5OS devices - name: f5os - version_added: 1.0.0 - inventory: {} - lookup: {} - module: - f5os_allowed_ips: - description: Manage allowed IPs using openAPI on F5OS based systems - name: f5os_allowed_ips - namespace: '' - version_added: 1.9.0 - f5os_auth: - description: Manage authentication settings - name: f5os_auth - namespace: '' - version_added: 1.10.0 - f5os_config_backup: - description: Manage F5OS config backups. - name: f5os_config_backup - namespace: '' - version_added: 1.2.0 - f5os_device_info: - description: Collect information from F5OS devices - name: f5os_device_info - namespace: '' - version_added: 1.0.0 - f5os_dns: - description: Manage DNS on F5OS Devices - name: f5os_dns - namespace: '' - version_added: 1.8.0 - f5os_interface: - description: Manage network interfaces on F5OS based systems - name: f5os_interface - namespace: '' - version_added: 1.0.0 - f5os_lag: - description: Manage LAG interfaces on F5OS based systems - name: f5os_lag - namespace: '' - version_added: 1.0.0 - f5os_license: - description: Manage F5OS license activation and deactivation. - name: f5os_license - namespace: '' - version_added: 1.10.0 - f5os_lldp_config: - description: Manage LLDP config - name: f5os_lldp_config - namespace: '' - version_added: 1.8.0 - f5os_logging: - description: Manage logging settings - name: f5os_logging - namespace: '' - version_added: 1.10.0 - f5os_ntp_server: - description: Manage NTP servers on F5OS based systems - name: f5os_ntp_server - namespace: '' - version_added: 1.8.0 - f5os_primarykey: - description: Manage F5OS Devices Primary-key Setting. - name: f5os_primarykey - namespace: '' - version_added: 1.11.0 - f5os_qkview: - description: Manage Generation of qkview file - name: f5os_qkview - namespace: '' - version_added: 1.0.0 - f5os_snmp: - description: Manage SNMP Communities, Users, and Targets using openAPI on F5OS - based systems - name: f5os_snmp - namespace: '' - version_added: 1.9.0 - f5os_stp_config: - description: Manage STP config - name: f5os_stp_config - namespace: '' - version_added: 1.8.0 - f5os_system: - description: Manage generic system settings - name: f5os_system - namespace: '' - version_added: 1.10.0 - f5os_system_image_import: - description: Manage F5OS System image import. - name: f5os_system_image_import - namespace: '' - version_added: 1.11.0 - f5os_system_image_install: - description: Manage F5OS system software installation. - name: f5os_system_image_install - namespace: '' - version_added: 1.11.0 - f5os_tenant: - description: Manage F5OS tenants - name: f5os_tenant - namespace: '' - version_added: 1.0.0 - f5os_tenant_image: - description: Manage F5OS tenant images - name: f5os_tenant_image - namespace: '' - version_added: 1.0.0 - f5os_tenant_wait: - description: Wait for a F5OS tenant condition before continuing - name: f5os_tenant_wait - namespace: '' - version_added: 1.0.0 - f5os_tls_cert_key: - description: Manage TLS certificate and key on F5OS devices. - name: f5os_tls_cert_key - namespace: '' - version_added: 1.11.0 - f5os_user: - description: Manage Users and roles on F5OS based systems - name: f5os_user - namespace: '' - version_added: 1.9.0 - f5os_vlan: - description: Manage VLANs on F5OS based systems - name: f5os_vlan - namespace: '' - version_added: 1.0.0 - velos_partition: - description: Manage VELOS chassis partitions - name: velos_partition - namespace: '' - version_added: 1.0.0 - velos_partition_change_password: - description: Provides access to VELOS chassis partition user authentication - methods - name: velos_partition_change_password - namespace: '' - version_added: 1.0.0 - velos_partition_image: - description: Manage VELOS chassis partition images - name: velos_partition_image - namespace: '' - version_added: 1.0.0 - velos_partition_wait: - description: Wait for a VELOS chassis partition to match a condition before - continuing - name: velos_partition_wait - namespace: '' - version_added: 1.0.0 - netconf: {} - shell: {} - strategy: {} - test: {} - vars: {} -version: 1.11.0 diff --git a/ansible_collections/f5networks/f5os/galaxy.yml b/ansible_collections/f5networks/f5os/galaxy.yml index 1504cdb..8d8f4a9 100644 --- a/ansible_collections/f5networks/f5os/galaxy.yml +++ b/ansible_collections/f5networks/f5os/galaxy.yml @@ -30,4 +30,4 @@ tags: - networking - rseries - velos -version: 1.11.0 +version: 1.12.0-devel diff --git a/ansible_collections/f5networks/f5os/plugins/module_utils/version.py b/ansible_collections/f5networks/f5os/plugins/module_utils/version.py index ec54826..1446445 100644 --- a/ansible_collections/f5networks/f5os/plugins/module_utils/version.py +++ b/ansible_collections/f5networks/f5os/plugins/module_utils/version.py @@ -4,4 +4,4 @@ # GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # This collection version needs to be updated at each release -CURRENT_COLL_VERSION = "1.11.0" +CURRENT_COLL_VERSION = "1.12.0-devel" From 7c8342b74b9d4f1ff93039b06974e30d5ad32bc4 Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Fri, 27 Sep 2024 01:00:52 +0000 Subject: [PATCH 2/8] Merging devel changes - 2024-09-27T01:00:43Z --- README.md | 2 +- ansible_collections/f5networks/f5os/README.md | 2 +- .../f5os/changelogs/fragments/lag_bugfix.yaml | 3 + .../tenant_virtual_disk_size_param.yaml | 3 + .../f5os/plugins/module_utils/compare.py | 2 +- .../f5os/plugins/modules/f5os_lag.py | 29 ++++++++++ .../f5os/plugins/modules/f5os_tenant.py | 58 +++++++++++++++++-- .../modules/network/f5/test_f5os_tenant.py | 3 +- 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml create mode 100644 ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml diff --git a/README.md b/README.md index f5370c5..220a0aa 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ being included in this project. [repoinstall]: https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#installing-a-collection-from-a-git-repository [dailybuild]: https://f5-ansible.s3.amazonaws.com/collections/f5networks-f5os-devel.tar.gz [ansible_issues]: https://github.com/F5Networks/f5-ansible-f5os/issues -[License]: https://github.com/f5devcentral/f5-ansible-bigip/blob/master/COPYING +[License]: https://www.gnu.org/licenses/gpl-3.0.txt [ansiblehelp]: http://clouddocs.f5.com/products/orchestration/ansible/devel/ [execenv]: https://docs.ansible.com/automation-controller/latest/html/userguide/execution_environments.html [f5execenv]: http://clouddocs.f5.com/products/orchestration/ansible/devel/usage/exec-env.html diff --git a/ansible_collections/f5networks/f5os/README.md b/ansible_collections/f5networks/f5os/README.md index f5370c5..220a0aa 100644 --- a/ansible_collections/f5networks/f5os/README.md +++ b/ansible_collections/f5networks/f5os/README.md @@ -109,7 +109,7 @@ being included in this project. [repoinstall]: https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#installing-a-collection-from-a-git-repository [dailybuild]: https://f5-ansible.s3.amazonaws.com/collections/f5networks-f5os-devel.tar.gz [ansible_issues]: https://github.com/F5Networks/f5-ansible-f5os/issues -[License]: https://github.com/f5devcentral/f5-ansible-bigip/blob/master/COPYING +[License]: https://www.gnu.org/licenses/gpl-3.0.txt [ansiblehelp]: http://clouddocs.f5.com/products/orchestration/ansible/devel/ [execenv]: https://docs.ansible.com/automation-controller/latest/html/userguide/execution_environments.html [f5execenv]: http://clouddocs.f5.com/products/orchestration/ansible/devel/usage/exec-env.html diff --git a/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml b/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml new file mode 100644 index 0000000..9efcfc3 --- /dev/null +++ b/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - f5os_lag - fixed a bug that used to occur while adding trunk or native vlans diff --git a/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml b/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml new file mode 100644 index 0000000..5a845f1 --- /dev/null +++ b/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - f5os_tenant - added a new parameter, virtual_disk_size, to set the virtual disk size of the tenant diff --git a/ansible_collections/f5networks/f5os/plugins/module_utils/compare.py b/ansible_collections/f5networks/f5os/plugins/module_utils/compare.py index 7bd3929..ce42883 100644 --- a/ansible_collections/f5networks/f5os/plugins/module_utils/compare.py +++ b/ansible_collections/f5networks/f5os/plugins/module_utils/compare.py @@ -104,7 +104,7 @@ def nested_diff(want, have, invalid): if k not in have: return True else: - if type(want[k]) is dict: + if isinstance(want[k], dict): if nested_diff(want[k], have[k], invalid): return True else: diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_lag.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_lag.py index 49ffe46..2838348 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_lag.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_lag.py @@ -124,6 +124,32 @@ name: "Arista" trunk_vlans: [444, 555] state: absent + +- name: Create a FAST and PASSIVE lacp LAG interface + f5os_lag: + name: "Arista" + lag_type: "lacp" + mode: "passive" + interval: "fast" + native_vlan: 666 + trunk_vlans: [444, 555] + config_members: + - "1.0" + - "2.0" + state: present + +- name: Create a SLOW and ACTIVE lacp LAG interface + f5os_lag: + name: "Arista" + lag_type: "lacp" + mode: "active" + interval: "slow" + native_vlan: 666 + trunk_vlans: [444, 555] + config_members: + - "1.0" + - "2.0" + state: present ''' RETURN = r''' @@ -500,6 +526,9 @@ def create_on_device(self): "lag-type": params['lag_type'], "f5-if-aggregate:distribution-hash": "src-dst-ipport", }, + "openconfig-vlan:switched-vlan": { + "config": {}, + }, } } payload = { diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_tenant.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_tenant.py index 3a70969..5bda7c4 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_tenant.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_tenant.py @@ -92,6 +92,13 @@ choices: - enabled - disabled + virtual_disk_size: + description: + - The size of the virtual disk in GB. + - To update this attribute the tenant must be in either C(configured) + or C(provisioned) state. + type: int + version_added: "1.12.0" running_state: description: - Desired C(running_state) of the tenant. @@ -132,6 +139,7 @@ cpu_cores: 2 memory: 4096 cryptos: disabled + virtual_disk_size: 85 running_state: configured - name: Deploy tenant 'foo' @@ -221,7 +229,8 @@ class Parameters(AnsibleF5Parameters): 'gateway': 'mgmt_gateway', 'running-state': 'running_state', 'vcpu-cores-per-node': 'cpu_cores', - 'cpu-cores': 'cpu_cores' + 'cpu-cores': 'cpu_cores', + 'storage': 'virtual_disk_size', } api_attributes = [ @@ -235,6 +244,7 @@ class Parameters(AnsibleF5Parameters): 'memory', 'cryptos', 'running-state', + 'storage', ] returnables = [ @@ -247,7 +257,8 @@ class Parameters(AnsibleF5Parameters): 'cpu_cores', 'memory', 'cryptos', - 'running_state' + 'running_state', + 'virtual_disk_size', ] updatables = [ @@ -260,7 +271,8 @@ class Parameters(AnsibleF5Parameters): 'cpu_cores', 'memory', 'cryptos', - 'running_state' + 'running_state', + 'virtual_disk_size', ] @@ -280,6 +292,13 @@ def memory(self): except (TypeError, ValueError): return None + @property + def virtual_disk_size(self): + try: + return self._values.get('virtual_disk_size') + except (TypeError, ValueError): + return None + class ModuleParameters(Parameters): @property @@ -359,6 +378,12 @@ def vlans(self): ) return result + @property + def virtual_disk_size(self): + if self._values['virtual_disk_size'] is None: + return None + return {'size': self._values['virtual_disk_size']} + class Changes(Parameters): def to_return(self): # pragma: no cover @@ -401,6 +426,18 @@ def __default(self, param): except AttributeError: return attr1 + @property + def virtual_disk_size(self): + want = self.want.virtual_disk_size + have = self.have.virtual_disk_size + if want is None: + return None + if have is None: + return want + if want['size'] != have['size']: + return {'virtual_disk_size': want} + return None + class ModuleManager(object): def __init__(self, *args, **kwargs): @@ -532,12 +569,24 @@ def create_on_device(self): def update_on_device(self): params = self.changes.api_params() - for k, v in params.items(): + keys = list(params.keys()) + + if 'running-state' in keys: + if params['running-state'] == 'deployed': + keys.remove('running-state') + keys.append('running-state') + elif params['running-state'] in ['configured', 'provisioned']: + keys.remove('running-state') + keys.insert(0, 'running-state') + + for k in keys: + v = params[k] uri = f"/f5-tenants:tenants/tenant={self.want.name}/config/{k}" payload = {k: v} response = self.client.put(uri, data=payload) if response['code'] not in [200, 201, 202, 204]: raise F5ModuleError("Failed to update tenant {0}, {1} to {2}".format(self.want.name, k, v)) + return True def remove_from_device(self): @@ -571,6 +620,7 @@ def __init__(self): choices=[1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22] ), memory=dict(type='int'), + virtual_disk_size=dict(type='int'), cryptos=dict( choices=['enabled', 'disabled'] ), diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_tenant.py b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_tenant.py index 8dbbc05..78b4cb4 100644 --- a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_tenant.py +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_tenant.py @@ -226,6 +226,7 @@ def test_tenant_create(self, *args): cpu_cores=2, memory=7680, cryptos='enabled', + virtual_disk_size=80, running_state='configured', )) @@ -237,7 +238,7 @@ def test_tenant_create(self, *args): {'name': 'foo', 'config': { 'image': 'BIGIP-14.1.4.1-0.0.4.ALL-VELOS.qcow2.zip.bundle', 'nodes': [1], 'mgmt-ip': '10.144.140.151', 'gateway': '10.144.140.254', 'vlans': [444], 'prefix-length': 24, - 'vcpu-cores-per-node': 2, 'memory': 7680, 'cryptos': 'enabled', 'running-state': 'configured'}}]} + 'vcpu-cores-per-node': 2, 'storage': {'size': 80}, 'memory': 7680, 'cryptos': 'enabled', 'running-state': 'configured'}}]} # Override methods to force specific logic in the module to happen mm = ModuleManager(module=module) From e95c317d96363ca581443be8d4069866764ddeb5 Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Tue, 8 Oct 2024 01:00:54 +0000 Subject: [PATCH 3/8] Merging devel changes - 2024-10-08T01:00:45Z --- .../modules/f5os_system_image_install.py | 165 ++++++++++++++---- 1 file changed, 127 insertions(+), 38 deletions(-) diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_system_image_install.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_system_image_install.py index 1047fbc..7faa3ba 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_system_image_install.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_system_image_install.py @@ -15,6 +15,10 @@ - Manage F5OS system software installation. version_added: "1.11.0" options: + partition_name: + description: + - Partition Name for which ISO image version is to be installed or upgraded. + type: str image_version: description: - Image/software version to be installed on the F5OS device. @@ -52,6 +56,20 @@ - name: check status of Image Install f5os_system_image_install: image_version: "1.8.0-13846" + state: install + timeout: 600 + +- name: Update Partition Image Version + f5os_system_image_install: + partition_name: test100GbEoptics + image_version: "1.6.2-30244" + state: install + timeout: 600 + +- name: check status of Image Install + f5os_system_image_install: + partition_name: test100GbEoptics + image_version: "1.6.2-30244" state: present timeout: 600 ''' @@ -145,6 +163,7 @@ def __init__(self, *args, **kwargs): self.want = ModuleParameters(params=self.module.params) self.changes = UsableChanges() self.image_is_valid = False + self.partition_exists = False def _set_changed_options(self): changed = {} @@ -210,48 +229,99 @@ def create(self): self._set_changed_options() if self.module.check_mode: # pragma: no cover return True - self.install_software_image() + if self.want.partition_name is not None: + self.update_partition_image() + else: + self.install_software_image() return True def exists(self): - if self.install_status_complete(): - pass - # try: - # result = self.install_status_complete() - # return True - uri = "/openconfig-system:system/f5-system-image:image/state/install" - response = self.client.get(uri) - if response['code'] == 404 and self.client.platform == 'Velos Controller': - uri = "/openconfig-system:system/f5-system-controller-image:image" + if self.want.partition_name is not None: + if self.want.state == "present": + return self.install_status_complete() + else: + exists, version = self.check_partition() + self.partition_exists = exists + if exists and version == self.want.image_version: + return True + return False + else: + if self.install_status_complete(): + pass + # try: + # result = self.install_status_complete() + # return True + uri = "/openconfig-system:system/f5-system-image:image/state/install" response = self.client.get(uri) + if response['code'] == 404 and self.client.platform == 'Velos Controller': + uri = "/openconfig-system:system/f5-system-controller-image:image" + response = self.client.get(uri) + if response['code'] not in [200, 201, 202]: + raise F5ModuleError(response['contents']) + for key in response['contents']['f5-system-controller-image:image']['state']['controllers']['controller']: + if key['install-status'] == "success" and key['os-version'] == self.want.image_version: + return True + return False + if response['code'] == 404 and self.client.platform == 'Velos Partition': + uri = "/openconfig-platform:components" + response = self.client.get(uri) + platform_data = response['contents']['openconfig-platform:components']['component'][0] + for key in platform_data['f5-platform:software']['state']['software-components']['software-component']: + if key['state']['version'] != self.want.image_version: + return False + return True + if response['code'] in [200, 201, 202] and self.client.platform == 'rSeries Platform': + if response['contents']['f5-system-image:install']['install-os-version'] == self.want.image_version and \ + response['contents']['f5-system-image:install']['install-status'] == 'success': + return True if response['code'] not in [200, 201, 202]: raise F5ModuleError(response['contents']) - for key in response['contents']['f5-system-controller-image:image']['state']['controllers']['controller']: - if key['install-status'] == "success" and key['os-version'] == self.want.image_version: - return True + # { + # "f5-system-image:install": { + # "install-os-version": "1.8.0-13819", + # "install-service-version": "1.8.0-13819", + # "install-status": "success" + # } + # } return False - if response['code'] == 404 and self.client.platform == 'Velos Partition': - uri = "/openconfig-platform:components" - response = self.client.get(uri) - platform_data = response['contents']['openconfig-platform:components']['component'][0] - for key in platform_data['f5-platform:software']['state']['software-components']['software-component']: - if key['state']['version'] != self.want.image_version: - return False - return True - if response['code'] in [200, 201, 202] and self.client.platform == 'rSeries Platform': - if response['contents']['f5-system-image:install']['install-os-version'] == self.want.image_version and \ - response['contents']['f5-system-image:install']['install-status'] == 'success': - return True + + def check_partition(self): + uri = "/f5-system-partition:partitions?with-defaults=report-all" + response = self.client.get(uri) if response['code'] not in [200, 201, 202]: raise F5ModuleError(response['contents']) - # { - # "f5-system-image:install": { - # "install-os-version": "1.8.0-13819", - # "install-service-version": "1.8.0-13819", - # "install-status": "success" - # } - # } - return False + if 'f5-system-partition:partitions' in response['contents']: + if 'partition' in response['contents']['f5-system-partition:partitions']: + if len(response['contents']['f5-system-partition:partitions']['partition']) > 0: + for partition in response['contents']['f5-system-partition:partitions']['partition']: + if partition['name'] == self.want.partition_name: + return True, partition['config']['iso-version'] + return False, '' + + def update_partition_image(self): + if not self.partition_exists: + raise F5ModuleError("Partition does not exists.") + partition_image_exists = False + uri = '/f5-system-image:image/partition/config/iso' + response = self.client.get(uri) + if response['code'] not in [200, 201, 202]: + raise F5ModuleError(response['contents']) + if 'f5-system-image:iso' in response['contents']: + if 'iso' in response['contents']['f5-system-image:iso'] and len(response['contents']['f5-system-image:iso']['iso']) > 0: + for iso_version in response['contents']['f5-system-image:iso']['iso']: + if iso_version['version'] == self.want.image_version: + partition_image_exists = True + break + if not partition_image_exists: + raise F5ModuleError(f"Partition Image with ISO version {self.want.image_version} does not exists.") + uri = f'/f5-system-partition:partitions/partition={self.want.partition_name}/set-version' + payload = { + "f5-system-partition:iso-version": self.want.image_version + } + response = self.client.post(uri, data=payload) + if response['code'] not in [200, 201, 202]: + raise F5ModuleError(response['contents']) + return True def install_software_image(self): params = self.changes.api_params() @@ -311,11 +381,29 @@ def install_status_complete(self): def is_still_installing(self): try: - uri = "api" - response = self.client.get(uri, scope="/") - if response['code'] not in [200, 201, 202]: - raise F5ModuleError(response['contents']) - return False + if self.want.partition_name: + uri = "/f5-system-partition:partitions?with-defaults=report-all" + response = self.client.get(uri) + if response['code'] not in [200, 201, 202]: + raise F5ModuleError(response['contents']) + if 'f5-system-partition:partitions' in response['contents']: + if 'partition' in response['contents']['f5-system-partition:partitions']: + if len(response['contents']['f5-system-partition:partitions']['partition']) > 0: + for partition in response['contents']['f5-system-partition:partitions']['partition']: + if partition['name'] == self.want.partition_name and 'state' in partition and 'install-status' in partition['state']: + if partition['state']['install-status'] == ['in-progress', 'switching-role', 'pending']: + return True + elif partition['state']['install-status'] == "success": + return False + else: + raise F5ModuleError('Installation Failed with status' + partition['state']['install-status']) + + else: + uri = "api" + response = self.client.get(uri, scope="/") + if response['code'] not in [200, 201, 202]: + raise F5ModuleError(response['contents']) + return False except Exception as e: if e.__class__.__name__ == 'ConnectionError': return True @@ -326,6 +414,7 @@ class ArgumentSpec(object): def __init__(self): self.supports_check_mode = True argument_spec = dict( + partition_name=dict(type='str'), image_version=dict(type='str', required=True), timeout=dict( type='int', From ebb1942210ab8044b9df5430e7688f8152b7742c Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Tue, 15 Oct 2024 01:00:50 +0000 Subject: [PATCH 4/8] Merging devel changes - 2024-10-15T01:00:41Z --- .../f5networks/f5os/plugins/modules/f5os_logging.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py index b3388cd..d388681 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py @@ -759,9 +759,9 @@ def update_on_device(self): if 'remote_forwarding' in params and params['remote_forwarding'] is not None: uri = f'{base_uri}/f5-openconfig-system-logging:host-logs/config' payload = { - 'config': dict() + 'f5-openconfig-system-logging:config' : dict() } - conf = payload['config'] + conf = payload['f5-openconfig-system-logging:config'] conf['remote-forwarding'] = { 'enabled': params['remote_forwarding']['enabled'] } @@ -771,7 +771,7 @@ def update_on_device(self): } for log in params['remote_forwarding']['logs']: log_conf = { - 'facility': f'openconfig-system-loggin:{log["facility"].upper()}', + 'facility': f'openconfig-system-logging:{log["facility"].upper()}', 'severity': log['severity'].upper() } conf['selectors']['selector'].append(log_conf) From 87892abfd1105609031d18c06f7e0bd25500d91f Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Fri, 18 Oct 2024 01:00:56 +0000 Subject: [PATCH 5/8] Merging devel changes - 2024-10-18T01:00:44Z --- .../f5networks/f5os/plugins/modules/f5os_dns.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_dns.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_dns.py index 3525d62..8298e62 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_dns.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_dns.py @@ -40,6 +40,10 @@ default: present author: - Ravinder Reddy (@chinthalapalli) +notes: + - This Modules will only make patch calls to add the DNS servers and domains. + - It does not support the deletion of difference DNS servers and domains from the existing list while updating,it just adds new entries. + - When state is C(absent) it will B(delete) the DNS servers and domains from the user provided list. ''' EXAMPLES = r''' From 0f1d80c80d14cb46c22f0d8b61625ddd2cc2d189 Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Sat, 19 Oct 2024 01:00:53 +0000 Subject: [PATCH 6/8] Merging devel changes - 2024-10-19T01:00:43Z --- .../f5os/plugins/modules/f5os_logging.py | 2 +- .../f5os/plugins/modules/f5os_system.py | 22 ++- .../network/f5/fixtures/system_settings.json | 16 ++ .../modules/network/f5/test_f5os_system.py | 149 ++++++++++++++++++ 4 files changed, 180 insertions(+), 9 deletions(-) create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py index d388681..b7a6892 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py @@ -759,7 +759,7 @@ def update_on_device(self): if 'remote_forwarding' in params and params['remote_forwarding'] is not None: uri = f'{base_uri}/f5-openconfig-system-logging:host-logs/config' payload = { - 'f5-openconfig-system-logging:config' : dict() + 'f5-openconfig-system-logging:config': dict() } conf = payload['f5-openconfig-system-logging:config'] conf['remote-forwarding'] = { diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_system.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_system.py index 0aea854..b15cb5d 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_system.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_system.py @@ -702,6 +702,7 @@ def update_on_device(self): response = self.client.patch(uri, data=payload) if response['code'] not in [200, 201, 202, 204]: raise F5ModuleError(response['contents']) + # time.sleep(65) return True def remove_from_device(self): @@ -800,31 +801,36 @@ def read_current_from_device(self): # Motd, login_banner, hostname uri = "/openconfig-system:system/config" response = self.client.get(uri) - if response['code'] not in [200, 201, 202]: + if response['code'] not in [200, 201, 202, 204]: raise F5ModuleError(response['contents']['openconfig-system:config']) + if response['code'] in [200]: + params['config'] = response['contents']['openconfig-system:config'] # Clock clock_uri = "/openconfig-system:system/clock" clock_response = self.client.get(clock_uri) - if clock_response['code'] not in [200, 201, 202]: + if clock_response['code'] not in [200, 201, 202, 204]: raise F5ModuleError(clock_response['contents']['openconfig-system:clock']) + if response['code'] in [200]: + params['clock'] = clock_response['contents']['openconfig-system:clock'] # Ciphers ciphers_uri = '/openconfig-system:system/f5-security-ciphers:security/services/service' + # /services/service' ciphers_response = self.client.get(ciphers_uri) - if ciphers_response['code'] not in [200, 201, 202]: + if ciphers_response['code'] not in [200, 201, 202, 204]: raise F5ModuleError(ciphers_response['contents']['f5-security-ciphers:service']) + if ciphers_response['code'] in [200]: + params['ciphers'] = ciphers_response['contents']['f5-security-ciphers:service'] # Settings settings_uri = '/openconfig-system:system/f5-system-settings:settings' settings_response = self.client.get(settings_uri) - if settings_response['code'] not in [200, 201, 202]: + if settings_response['code'] not in [200, 201, 202, 204]: raise F5ModuleError(settings_response['contents']['f5-system-settings:settings']) + if settings_response['code'] in [200]: + params['settings'] = settings_response['contents']['f5-system-settings:settings'] - params['config'] = response['contents']['openconfig-system:config'] - params['clock'] = clock_response['contents']['openconfig-system:clock'] - params['ciphers'] = ciphers_response['contents']['f5-security-ciphers:service'] - params['settings'] = settings_response['contents']['f5-system-settings:settings'] return ApiParameters(params=params) diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json new file mode 100644 index 0000000..cb6ebb2 --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json @@ -0,0 +1,16 @@ +{ + "code": 200, + "contents": { + "openconfig-system:config": { + "hostname": "appliance-1.chassis.local", + "login-banner": "pmlab-r5600", + "motd-banner": "pmlab-ravi-r5600", + "current-datetime": "2024-10-09 10:01:53+00:00", + "f5-system:base-mac": "00:94:a1:69:5d:00", + "f5-system:mac-pool-size": 256 + }, + "openconfig-system:clock": {}, + "f5-security-ciphers:service": {}, + "f5-system-settings:settings": {} + } +} \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py new file mode 100644 index 0000000..0f0850e --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, F5 Networks Inc. +# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import os + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.f5networks.f5os.plugins.modules import f5os_system +from ansible_collections.f5networks.f5os.plugins.modules.f5os_system import ( + ArgumentSpec, ModuleManager +) +from ansible_collections.f5networks.f5os.plugins.module_utils.common import F5ModuleError + +from ansible_collections.f5networks.f5os.tests.compat import unittest +from ansible_collections.f5networks.f5os.tests.compat.mock import ( + Mock, patch +) +from ansible_collections.f5networks.f5os.tests.modules.utils import ( + set_module_args, exit_json, fail_json, AnsibleFailJson, AnsibleExitJson +) + + +fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') +fixture_data = {} + + +def load_fixture(name): + path = os.path.join(fixture_path, name) + + if path in fixture_data: + return fixture_data[path] + + with open(path) as f: + data = f.read() + + try: + data = json.loads(data) + except Exception: + pass + + fixture_data[path] = data + return data + + +class TestParameters(unittest.TestCase): + pass + + +class TestManager(unittest.TestCase): + def setUp(self): + self.spec = ArgumentSpec() + self.mock_module_helper = patch.multiple(AnsibleModule, + exit_json=exit_json, + fail_json=fail_json) + self.mock_module_helper.start() + self.p1 = patch('ansible_collections.f5networks.f5os.plugins.modules.f5os_system.F5Client') + self.m1 = self.p1.start() + self.m1.return_value = Mock() + self.p2 = patch('ansible_collections.f5networks.f5os.plugins.modules.f5os_system.send_teem') + self.m2 = self.p2.start() + self.m2.return_value = True + + def tearDown(self): + self.p1.stop() + self.p2.stop() + self.mock_module_helper.stop() + + def test_change_hostname_setting(self, *args): + set_module_args( + dict( + state='present', + hostname='foobar', + login_banner='foofoo' + ) + ) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.exists = Mock(return_value=True) + mm.client.get = Mock(return_value=load_fixture('system_settings.json')) + mm.client.patch = Mock(return_value=dict(code=200)) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.patch.call_count, 1) + self.assertEqual(mm.client.get.call_count, 4) + + def test_remove_hostname_setting(self, *args): + set_module_args( + dict( + state='absent', + hostname='foobar', + login_banner='foofoo' + ) + ) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.exists = Mock(side_effect=[True, False]) + mm.client.delete = Mock(return_value=dict(code=204)) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 2) + + @patch.object(f5os_system, 'Connection') + @patch.object(f5os_system.ModuleManager, 'exec_module', Mock(return_value={'changed': False})) + def test_main_function_success(self, *args): + set_module_args(dict( + hostname='foobar', + state='present', + )) + + with self.assertRaises(AnsibleExitJson) as result: + f5os_system.main() + + self.assertFalse(result.exception.args[0]['changed']) + + @patch.object(f5os_system, 'Connection') + @patch.object(f5os_system.ModuleManager, 'exec_module', + Mock(side_effect=F5ModuleError('This module has failed.')) + ) + def test_main_function_failed(self, *args): + set_module_args(dict( + hostname='foobar', + state='absent', + )) + + with self.assertRaises(AnsibleFailJson) as result: + f5os_system.main() + + self.assertTrue(result.exception.args[0]['failed']) + self.assertIn('This module has failed', result.exception.args[0]['msg']) From aeff8d44cdb82220874616f06d7f15b1a85c4d7e Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Tue, 22 Oct 2024 01:00:55 +0000 Subject: [PATCH 7/8] Merging devel changes - 2024-10-22T01:00:44Z --- .../f5os/plugins/modules/f5os_logging.py | 52 +- .../f5os/plugins/modules/f5os_tls_cert_key.py | 8 +- .../fixtures/remote_forwarding_get_resp.json | 38 ++ .../network/f5/fixtures/system_settings.json | 45 +- .../f5/fixtures/system_settings_ciphers.json | 3 + .../f5/fixtures/system_settings_clock.json | 13 + .../f5/fixtures/system_settings_hostname.json | 9 + .../modules/network/f5/test_f5os_auth.py | 462 ++++++++++++++++++ .../modules/network/f5/test_f5os_system.py | 176 ++++++- 9 files changed, 755 insertions(+), 51 deletions(-) create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/remote_forwarding_get_resp.json create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_ciphers.json create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_clock.json create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_hostname.json create mode 100644 ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_auth.py diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py index b7a6892..e8e7890 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_logging.py @@ -52,8 +52,11 @@ facility: description: Filter logs on facility local0 or authpriv. type: str + choices: + - local0 + - authpriv severity: - description: Specify the minimum seceverity to be forwarded to this server + description: Specify the minimum severity to be forwarded to this server type: str choices: - debug @@ -80,7 +83,7 @@ description: Filter logs on facility. type: str severity: - description: Specify the minimum seceverity to be forwarded to remote servers + description: Specify the minimum severity to be forwarded to remote servers type: str choices: - debug @@ -536,9 +539,8 @@ def exists(self, query=None): if hasattr(self.want, 'servers') and self.want.servers is not None: for server in self.want.servers: - uri = f'{base_uri}/remote-servers/remote-server="{server["address"]}"' + uri = f'{base_uri}/remote-servers/remote-server={server["address"]}' response = self.client.get(uri) - if response['code'] == 200: if query in ['any', 'still']: return True @@ -684,11 +686,7 @@ def create_on_device(self): } } if 'authentication' in server and server['authentication'] is not None: - server_conf['config'] = { - 'f5-openconfig-system-logging:authentication': { - 'enabled': server['authentication'] - } - } + server_conf['config'].update({'f5-openconfig-system-logging:authentication': {'enabled': server['authentication']}}) if 'logs' in server and server['logs'] is not None: server_conf['selectors'] = { 'selector': list() @@ -704,7 +702,6 @@ def create_on_device(self): } server_conf['selectors']['selector'].append(log_conf) server_list.append(server_conf) - response = self.client.post(uri, data=payload) if response['code'] == 409: # This object exists already, so override it @@ -731,7 +728,6 @@ def create_on_device(self): def update_on_device(self): params = self.changes.api_params() base_uri = '/openconfig-system:system/logging' - if 'tls' in params and params['tls'] is not None: uri = f'{base_uri}/f5-openconfig-system-logging:tls' payload = { @@ -741,7 +737,6 @@ def update_on_device(self): response = self.client.put(uri, data=payload) if response['code'] not in [200, 204]: raise F5ModuleError(response['contents']) - if 'ca_bundles' in params and params['ca_bundles'] is not None: for bundle in params['ca_bundles']: uri = f'{base_uri}/f5-openconfig-system-logging:tls/ca-bundles/ca-bundle="{bundle["name"]}"' @@ -789,16 +784,12 @@ def update_on_device(self): response = self.client.put(uri, data=payload) if response['code'] not in [200, 204]: raise F5ModuleError(response['contents']) - if 'servers' in params and params['servers'] is not None: uri = f'{base_uri}/remote-servers' - payload = { - 'openconfig-system:remote-servers': { - 'remote-server': list() - } - } - server_list = payload['openconfig-system:remote-servers']['remote-server'] + for server in params['servers']: + payload = {'remote-server': list()} + server_conf = { 'host': server['address'], 'config': { @@ -809,16 +800,13 @@ def update_on_device(self): } if 'authentication' in server and server['authentication'] is not None: - server_conf['config'] = { - 'f5-openconfig-system-logging:authentication': { - 'enabled': server['authentication'] - } - } + server_conf['config']['f5-openconfig-system-logging:authentication'] = dict() + server_conf['config']['f5-openconfig-system-logging:authentication'] = {'enabled': server['authentication']} if 'logs' in server and server['logs'] is not None: - server_conf['selectors'] = { - 'selector': list() - } + server_conf['selectors'] = dict() + server_conf['selectors'] = {'selector': list()} + for log in server['logs']: log_conf = { 'facility': f'f5-system-logging-types:{log["facility"].upper()}', @@ -829,12 +817,11 @@ def update_on_device(self): } } server_conf['selectors']['selector'].append(log_conf) - server_list.append(server_conf) - response = self.client.put(uri, data=payload) + payload['remote-server'].append(server_conf) + response = self.client.put(f'{uri}/remote-server={server["address"]}', data=payload) if response['code'] not in [200, 204]: raise F5ModuleError(response['contents']) - if 'include_hostname' in params and params['include_hostname'] is not None: uri = f'{base_uri}/f5-openconfig-system-logging:config' payload = { @@ -969,7 +956,10 @@ def __init__(self): type='list', elements='dict', options=dict( - facility=dict(type='str'), + facility=dict( + type='str', + choices=['local0', 'authpriv'] + ), severity=dict( type='str', choices=ArgumentSpec.severities diff --git a/ansible_collections/f5networks/f5os/plugins/modules/f5os_tls_cert_key.py b/ansible_collections/f5networks/f5os/plugins/modules/f5os_tls_cert_key.py index 7ff9590..b6a2ca3 100644 --- a/ansible_collections/f5networks/f5os/plugins/modules/f5os_tls_cert_key.py +++ b/ansible_collections/f5networks/f5os/plugins/modules/f5os_tls_cert_key.py @@ -64,9 +64,9 @@ type: str choices: - rsa - - encrypted rsa + - encrypted-rsa - ecdsa - - encrypted ecdsa + - encrypted-ecdsa key_size: description: - This specifies the length of the key. @@ -643,9 +643,9 @@ def __init__(self): key_type=dict( choices=[ "rsa", - "encrypted rsa", + "encrypted-rsa", "ecdsa", - "encrypted ecdsa", + "encrypted-ecdsa", ] ), key_size=dict( diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/remote_forwarding_get_resp.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/remote_forwarding_get_resp.json new file mode 100644 index 0000000..943af54 --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/remote_forwarding_get_resp.json @@ -0,0 +1,38 @@ +{ + "f5-openconfig-system-logging:host-logs": { + "config": { + "remote-forwarding": { + "enabled": true + }, + "selectors": { + "selector": [ + { + "facility": "openconfig-system-logging:LOCAL0", + "severity": "NOTICE" + }, + { + "facility": "openconfig-system-logging:AUTHPRIV", + "severity": "EMERGENCY" + }, + { + "facility": "openconfig-system-logging:AUTH", + "severity": "EMERGENCY" + } + ] + }, + "files": { + "file": [ + { + "name": "anaconda" + }, + { + "name": "ansible.log" + }, + { + "name": "boot.log" + } + ] + } + } + } +} \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json index cb6ebb2..cf6d3fb 100644 --- a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings.json @@ -1,16 +1,37 @@ { - "code": 200, - "contents": { - "openconfig-system:config": { - "hostname": "appliance-1.chassis.local", - "login-banner": "pmlab-r5600", - "motd-banner": "pmlab-ravi-r5600", - "current-datetime": "2024-10-09 10:01:53+00:00", - "f5-system:base-mac": "00:94:a1:69:5d:00", - "f5-system:mac-pool-size": 256 + "f5-system-settings:settings": { + "config": { + "f5-system-settings-pgwarn:portgroup-confirmation-warning": "on" }, - "openconfig-system:clock": {}, - "f5-security-ciphers:service": {}, - "f5-system-settings:settings": {} + "state": { + "sshd-idle-timeout": "0", + "f5-system-settings-pgwarn:portgroup-confirmation-warning": "on" + }, + "f5-system-settings-dag:dag": { + "config": { + "gtp-u": { + "teid-hash": { + "enabled": false + } + } + }, + "state": { + "gtp-u": { + "teid-hash": { + "enabled": false + } + } + } + }, + "f5-gui-advisory:gui": { + "advisory": { + "config": { + "enabled": false + }, + "state": { + "enabled": false + } + } + } } } \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_ciphers.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_ciphers.json new file mode 100644 index 0000000..032cf13 --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_ciphers.json @@ -0,0 +1,3 @@ +{ + "f5-security-ciphers:service": {} +} \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_clock.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_clock.json new file mode 100644 index 0000000..0551d1a --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_clock.json @@ -0,0 +1,13 @@ +{ + "openconfig-system:clock": { + "config": { + "timezone-name": "Etc/UTC" + }, + "state": { + "timezone-name": "Etc/UTC", + "f5-system-clock:appliance": { + "date-time": "2024-10-18 10:27:08+00:00" + } + } + } +} \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_hostname.json b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_hostname.json new file mode 100644 index 0000000..c2ffcd2 --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/fixtures/system_settings_hostname.json @@ -0,0 +1,9 @@ +{ + "openconfig-system:config": { + "login-banner": "pmlab-r5600", + "motd-banner": "pmlab-ravi-r5600" + }, + "openconfig-system:clock": {}, + "f5-security-ciphers:service": {}, + "f5-system-settings:settings": {} +} \ No newline at end of file diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_auth.py b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_auth.py new file mode 100644 index 0000000..3eef260 --- /dev/null +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_auth.py @@ -0,0 +1,462 @@ +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, F5 Networks Inc. +# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import os + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.f5networks.f5os.plugins.modules import f5os_auth +from ansible_collections.f5networks.f5os.plugins.modules.f5os_auth import ( + ArgumentSpec, ModuleManager +) +from ansible_collections.f5networks.f5os.plugins.module_utils.common import F5ModuleError + +from ansible_collections.f5networks.f5os.tests.compat import unittest +from ansible_collections.f5networks.f5os.tests.compat.mock import ( + Mock, patch +) +from ansible_collections.f5networks.f5os.tests.modules.utils import ( + set_module_args, exit_json, fail_json, AnsibleFailJson, AnsibleExitJson +) + + +fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') +fixture_data = {} + + +def load_fixture(name): + path = os.path.join(fixture_path, name) + + if path in fixture_data: + return fixture_data[path] + + with open(path) as f: + data = f.read() + + try: + data = json.loads(data) + except Exception: + pass + + fixture_data[path] = data + return data + + +class TestParameters(unittest.TestCase): + pass + + +class TestManager(unittest.TestCase): + def setUp(self): + self.spec = ArgumentSpec() + self.mock_module_helper = patch.multiple(AnsibleModule, + exit_json=exit_json, + fail_json=fail_json) + self.mock_module_helper.start() + self.p1 = patch('ansible_collections.f5networks.f5os.plugins.modules.f5os_auth.F5Client') + self.m1 = self.p1.start() + self.m1.return_value = Mock() + self.p2 = patch('ansible_collections.f5networks.f5os.plugins.modules.f5os_auth.send_teem') + self.m2 = self.p2.start() + self.m2.return_value = True + + def tearDown(self): + self.p1.stop() + self.p2.stop() + self.mock_module_helper.stop() + + def test_create_tacacs_server_auth(self): + set_module_args(dict( + servergroups=[ + dict( + name='tacacs_server', + protocol='tacacs', + servers=[ + {'address': '1.2.3.4', 'secret': 'supersecret', 'port': 49}, + {'address': '1.2.3.5', 'secret': 'secret', 'port': 49} + ] + ), + dict( + name='radius_server', + protocol='radius', + servers=[ + {'address': '10.2.3.4', 'secret': 'TOPSECRET', 'port': 1812, 'timeout': 3}, + {'address': '10.2.3.5', 'secret': 'TOPSECRET', 'port': 1812, 'timeout': 3} + ] + ), + dict( + name='ldap_server', + protocol='ldap', + servers=[ + {'address': '11.22.33.44', 'port': 389}, + {'address': '21.22.33.44', 'port': 636, 'security': 'tls'}, + ] + ), + dict( + name='ocsp_server', + protocol='ocsp', + servers=[ + {'address': '21.34.56.33', 'port': 80}, + {'address': '10.198.168.11', 'port': 80}, + ] + ) + ] + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.client.get = Mock(return_value=dict(code=404)) + mm.client.post = Mock(return_value=dict(code=200)) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.post.call_count, 4) + self.assertEqual(mm.client.get.call_count, 1) + + def test_update_tacacs_server_auth(self): + set_module_args(dict( + servergroups=[ + dict( + name='tacacs_server', + protocol='tacacs', + servers=[ + {'address': '1.2.3.4', 'secret': 'supersecret', 'port': 49}, + {'address': '1.2.3.5', 'secret': 'secret', 'port': 49} + ] + ), + dict( + name='radius_server', + protocol='radius', + servers=[ + {'address': '10.2.3.4', 'secret': 'TOPSECRET', 'port': 1812, 'timeout': 3}, + {'address': '10.2.3.5', 'secret': 'TOPSECRET', 'port': 1812, 'timeout': 3} + ] + ), + dict( + name='ldap_server', + protocol='ldap', + servers=[ + {'address': '11.22.33.44', 'port': 389}, + {'address': '21.22.33.44', 'port': 636, 'security': 'tls'}, + ] + ), + dict( + name='ocsp_server', + protocol='ocsp', + servers=[ + {'address': '21.34.56.33', 'port': 80}, + {'address': '10.198.168.11', 'port': 80}, + ] + ) + ] + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.all_exist = Mock(return_value=True) + mm.client.get = Mock(return_value=dict(code=404, conetents={})) + mm.client.put = Mock(return_value=dict(code=200)) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.put.call_count, 4) + self.assertEqual(mm.client.get.call_count, 4) + + def test_remove_server_groups(self): + set_module_args(dict( + servergroups=[dict(name='tacacs_server')], + state='absent', + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.client.get = Mock(side_effect=[{'code': 200}, {'code': 404}]) + mm.client.delete = Mock(return_value={'code': 204}) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 1) + self.assertEqual(mm.client.get.call_count, 2) + + def test_create_remote_roles_auth(self): + set_module_args(dict( + remote_roles=[ + dict(rolename='admin', remote_gid=10, ldap_group='admins'), + dict(rolename='resource-admin', remote_gid=20, ldap_group='resource-admins') + ] + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.client.get = Mock(return_value={'code': 200, 'contents': {'f5-system-aaa:role': [{'config': ''}]}}) + mm.client.patch = Mock(return_value={'code': 200}) + + result = mm.exec_module() + self.assertTrue(result['changed']) + self.assertEqual(mm.client.patch.call_count, 2) + self.assertEqual(mm.client.get.call_count, 1) + + def test_update_remote_roles_auth(self): + set_module_args(dict( + remote_roles=[ + dict(rolename='admin', remote_gid=10, ldap_group='admins'), + dict(rolename='resource-admin', remote_gid=20, ldap_group='resource-admins') + ] + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.exists = Mock(return_value=True) + mm.client.get = Mock( + return_value={ + 'code': 200, + 'contents': { + 'f5-system-aaa:role': [ + {'config': {'remote-gid': 9, 'ldap-group': 'non-admin', 'rolename': 'admin'}}, + {'config': {'remote-gid': 7, 'ldap-group': 'non-admin', 'rolename': 'resource-admin'}} + ] + } + } + ) + + mm.client.patch = Mock(return_value={'code': 200}) + + result = mm.exec_module() + self.assertTrue(result['changed']) + self.assertEqual(mm.client.patch.call_count, 2) + + def test_remove_remote_roles_auth(self): + set_module_args(dict( + remote_roles=[ + dict(rolename='admin', remote_gid=10, ldap_group='admins'), + dict(rolename='resource-admin', remote_gid=20, ldap_group='resource-admins') + ], + state='absent' + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.client.get = Mock(side_effect=[ + {'code': 200, 'contents': {'f5-system-aaa:role': [{'config': {'ldap-group': ''}}]}}, + {'code': 200, 'contents': {'f5-system-aaa:role': [{'config': ''}]}}, + {'code': 200, 'contents': {'f5-system-aaa:role': [{'config': ''}]}}, + ]) + mm.client.delete = Mock(return_value={'code': 204}) + + result = mm.exec_module() + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 4) + self.assertEqual(mm.client.get.call_count, 3) + + def test_create_password_policy(self): + set_module_args(dict( + password_policy=dict( + apply_to_root=True, + max_age=10, + max_class_repeat=3, + max_letter_repeat=3, + max_login_failures=3, + max_retries=3, + max_sequence_repeat=3, + min_differences=3, + min_length=8, + min_lower=1, + min_number=1, + min_special=1, + min_upper=1, + reject_username=True, + root_lockout=True, + root_unlock_time=10, + unlock_time=10, + ) + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.client.get = Mock(side_effect=[ + {'code': 200}, + {'code': 200, 'contents': {'f5-openconfig-aaa-password-policy:password-policy': {'config': {}}}} + ]) + mm.client.put = Mock(return_value={'code': 200}) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.put.call_count, 1) + self.assertEqual(mm.client.get.call_count, 2) + + def test_remove_password_policy(self): + set_module_args(dict( + password_policy=dict( + apply_to_root=True, + max_age=10, + max_class_repeat=3, + max_letter_repeat=3, + max_login_failures=3, + max_retries=3, + max_sequence_repeat=3, + min_differences=3, + min_length=8, + min_lower=1, + min_number=1, + min_special=1, + min_upper=1, + reject_username=True, + root_lockout=True, + root_unlock_time=10, + unlock_time=10, + ), + state='absent' + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.client.get = Mock(return_value={'code': 200}) + mm.client.delete = Mock(return_value={'code': 204}) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 1) + self.assertEqual(mm.client.get.call_count, 2) + + def test_create_auth_order(self): + set_module_args(dict( + auth_order=['radius', 'tacacs', 'ldap', 'local'], + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.client.get = Mock(return_value={'code': 404}) + mm.client.put = Mock(return_value={'code': 200}) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.put.call_count, 1) + self.assertEqual(mm.client.get.call_count, 1) + + def test_update_auth_order(self): + set_module_args(dict( + auth_order=['radius', 'tacacs', 'ldap', 'local'], + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.exists = Mock(return_value=True) + mm.client.get = Mock( + return_value={ + 'code': 200, + 'contents': { + 'openconfig-system:authentication-method': ['openconfig-aaa-types:RADIUS_ALL', 'openconfig-aaa-types:TACACS_ALL'] + } + } + ) + mm.client.put = Mock(return_value={'code': 200}) + + result = mm.exec_module() + self.assertTrue(result['changed']) + self.assertEqual(mm.client.put.call_count, 1) + self.assertEqual(mm.client.get.call_count, 1) + + def test_remove_auth_order(self): + set_module_args(dict( + auth_order=['radius', 'tacacs', 'ldap', 'local'], + state='absent' + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.exists = Mock(return_value=True) + mm.client.delete = Mock(return_value={'code': 204}) + mm.still_exists = Mock(return_value=False) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 1) + + @patch.object(f5os_auth, 'Connection') + @patch.object(f5os_auth.ModuleManager, 'exec_module', Mock(return_value={'changed': False})) + def test_main_function_success(self, *args): + set_module_args(dict( + auth_order=['radius', 'tacacs', 'ldap', 'local'], + )) + + with self.assertRaises(AnsibleExitJson) as result: + f5os_auth.main() + + self.assertFalse(result.exception.args[0]['changed']) + + @patch.object(f5os_auth, 'Connection') + @patch.object(f5os_auth.ModuleManager, 'exec_module', + Mock(side_effect=F5ModuleError('This module has failed.')) + ) + def test_main_function_failed(self, *args): + set_module_args(dict( + auth_order=['radius', 'tacacs', 'ldap', 'local'], + )) + + with self.assertRaises(AnsibleFailJson) as result: + f5os_auth.main() + + self.assertTrue(result.exception.args[0]['failed']) + self.assertIn('This module has failed', result.exception.args[0]['msg']) diff --git a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py index 0f0850e..91cd6cb 100644 --- a/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py +++ b/ansible_collections/f5networks/f5os/tests/modules/network/f5/test_f5os_system.py @@ -13,7 +13,7 @@ from ansible_collections.f5networks.f5os.plugins.modules import f5os_system from ansible_collections.f5networks.f5os.plugins.modules.f5os_system import ( - ArgumentSpec, ModuleManager + ApiParameters, ArgumentSpec, ModuleManager ) from ansible_collections.f5networks.f5os.plugins.module_utils.common import F5ModuleError @@ -49,7 +49,45 @@ def load_fixture(name): class TestParameters(unittest.TestCase): - pass + def test_api_paramaeters_ciphers(self): + args2 = { + 'ciphers': [ + { + 'name': 'sshd', + 'config': { + 'ciphers': ['aes256-ctr', 'aes256-gcm@openssh.com'], + 'kexalgorithms': ['ecdh-sha2-nistp384', 'ecdh-sha2-nistp521'], + 'macs': ['hmac-sha1', 'hmac-sha1-96'], + 'host-key-algorithms': ['ssh-rsa', 'ssh-ecdsa'], + } + }, + ] + } + + p = ApiParameters(params=args2) + + self.assertEqual(p.sshd_ciphers, ['aes256-ctr', 'aes256-gcm@openssh.com']) + self.assertEqual(p.sshd_kex_alg, ['ecdh-sha2-nistp384', 'ecdh-sha2-nistp521']) + self.assertEqual(p.sshd_mac_alg, ['hmac-sha1', 'hmac-sha1-96']) + self.assertEqual(p.sshd_hkey_alg, ['ssh-ecdsa', 'ssh-rsa']) + + def test_api_parameters_none_values(self): + args = {} + + p = ApiParameters(params=args) + + self.assertIsNone(p.hostname) + self.assertIsNone(p.motd) + self.assertIsNone(p.login_banner) + self.assertIsNone(p.timezone) + self.assertIsNone(p.cli_timeout) + self.assertIsNone(p.httpd_ciphersuite) + self.assertIsNone(p.sshd_idle_timeout) + self.assertIsNone(p.sshd_ciphers) + self.assertIsNone(p.sshd_kex_alg) + self.assertIsNone(p.sshd_mac_alg) + self.assertIsNone(p.sshd_hkey_alg) + self.assertIsNone(p.gui_advisory) class TestManager(unittest.TestCase): @@ -71,7 +109,92 @@ def tearDown(self): self.p2.stop() self.mock_module_helper.stop() - def test_change_hostname_setting(self, *args): + def test_create_system_settings(self): + set_module_args( + dict( + state='present', + hostname='foobar', + motd='Todays weather is great!', + login_banner='foofoo', + timezone='America/New_York', + cli_timeout=300, + sshd_idle_timeout=1800, + httpd_ciphersuite='ECDHE-RSA-AES256-GCM-SHA384', + sshd_ciphers=['aes256-ctr', 'aes256-gcm@openssh.com'], + sshd_kex_alg=['ecdh-sha2-nistp384', 'ecdh-sha2-nistp521'], + sshd_mac_alg=['hmac-sha1', 'hmac-sha1-96'], + sshd_hkey_alg=['ssh-rsa'] + ) + ) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + mm.client.get = Mock(side_effect=[ + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 404}, + {'code': 200, 'contents': load_fixture('system_settings_hostname.json')}, + {'code': 200, 'contents': load_fixture('system_settings_clock.json')}, + {'code': 200, 'contents': load_fixture('system_settings_ciphers.json')}, + {'code': 200, 'contents': load_fixture('system_settings.json')}, + ]) + + mm.client.patch = Mock(return_value=dict(code=200)) + mm.client.put = Mock(return_value=dict(code=200)) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.put.call_count, 5) + self.assertEqual(mm.client.patch.call_count, 2) + + def test_remove_system_settings(self): + set_module_args( + dict( + state='absent', + hostname='foobar', + motd='Todays weather is great!', + login_banner='foofoo', + timezone='America/New_York', + cli_timeout=300, + sshd_idle_timeout=1800, + httpd_ciphersuite='ECDHE-RSA-AES256-GCM-SHA384', + sshd_ciphers=['aes256-ctr', 'aes256-gcm@openssh.com'], + sshd_kex_alg=['ecdh-sha2-nistp384', 'ecdh-sha2-nistp521'], + sshd_mac_alg=['hmac-sha1', 'hmac-sha1-96'], + sshd_hkey_alg=['ssh-rsa'] + ) + ) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.client.get = Mock(return_value=dict(code=200)) + mm.client.delete = Mock(return_value=dict(code=204)) + mm.still_exists = Mock(return_value=False) + + result = mm.exec_module() + + self.assertTrue(result['changed']) + self.assertEqual(mm.client.delete.call_count, 11) + + def test_update_hostname_setting(self, *args): set_module_args( dict( state='present', @@ -87,7 +210,7 @@ def test_change_hostname_setting(self, *args): mm = ModuleManager(module=module) mm.exists = Mock(return_value=True) - mm.client.get = Mock(return_value=load_fixture('system_settings.json')) + mm.client.get = Mock(return_value={'code': 200, 'contents': load_fixture('system_settings_hostname.json')}) mm.client.patch = Mock(return_value=dict(code=200)) result = mm.exec_module() @@ -147,3 +270,48 @@ def test_main_function_failed(self, *args): self.assertTrue(result.exception.args[0]['failed']) self.assertIn('This module has failed', result.exception.args[0]['msg']) + + def test_device_call_functions(self): + set_module_args( + dict( + state='absent', + hostname='foobar', + motd='Todays weather is great!', + login_banner='foofoo', + timezone='America/New_York', + cli_timeout=300, + sshd_idle_timeout=1800, + httpd_ciphersuite='ECDHE-RSA-AES256-GCM-SHA384', + sshd_ciphers=['aes256-ctr', 'aes256-gcm@openssh.com'], + sshd_kex_alg=['ecdh-sha2-nistp384', 'ecdh-sha2-nistp521'], + sshd_mac_alg=['hmac-sha1', 'hmac-sha1-96'], + sshd_hkey_alg=['ssh-rsa'] + ) + ) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + mm = ModuleManager(module=module) + + mm.any_exists = Mock(return_value=False) + res1 = mm.absent() + self.assertFalse(res1) + + mm._update_changed_options = Mock(return_value=False) + res2 = mm.should_update() + self.assertFalse(res2) + + mm.read_current_from_device = Mock() + mm.should_update = Mock(return_value=False) + res3 = mm.update() + self.assertFalse(res3) + + mm.remove_from_device = Mock() + mm.still_exists = Mock(return_value=True) + + with self.assertRaises(F5ModuleError) as err1: + mm.remove() + self.assertIn('Failed to delete the resource.', err1.exception.args[0]) From 4da0202a25aa06d36a346a412843d465bb91484f Mon Sep 17 00:00:00 2001 From: rupadhyay Date: Thu, 24 Oct 2024 11:35:56 +0000 Subject: [PATCH 8/8] Version 1.12.0 release --- .../f5networks/f5os/CHANGELOG.rst | 13 ++ .../f5os/changelogs/.plugin-cache.yaml | 166 ++++++++++++++++++ .../f5networks/f5os/changelogs/changelog.yaml | 11 ++ .../f5os/changelogs/fragments/lag_bugfix.yaml | 3 - .../tenant_virtual_disk_size_param.yaml | 3 - .../f5networks/f5os/galaxy.yml | 2 +- .../f5os/plugins/module_utils/version.py | 2 +- .../f5networks/f5os/tests/compat/mock.py | 6 +- .../f5networks/f5os/tests/mock/loader.py | 2 +- .../f5networks/f5os/tests/utils/common.py | 2 +- 10 files changed, 196 insertions(+), 14 deletions(-) create mode 100644 ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml delete mode 100644 ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml delete mode 100644 ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml diff --git a/ansible_collections/f5networks/f5os/CHANGELOG.rst b/ansible_collections/f5networks/f5os/CHANGELOG.rst index b17066e..df934d1 100644 --- a/ansible_collections/f5networks/f5os/CHANGELOG.rst +++ b/ansible_collections/f5networks/f5os/CHANGELOG.rst @@ -4,6 +4,19 @@ F5Networks.F5OS Release Notes .. contents:: Topics +v1.12.0 +======= + +Minor Changes +------------- + +- f5os_tenant - added a new parameter, virtual_disk_size, to set the virtual disk size of the tenant + +Bugfixes +-------- + +- f5os_lag - fixed a bug that used to occur while adding trunk or native vlans + v1.11.0 ======= diff --git a/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml b/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml new file mode 100644 index 0000000..db8bbc3 --- /dev/null +++ b/ansible_collections/f5networks/f5os/changelogs/.plugin-cache.yaml @@ -0,0 +1,166 @@ +objects: + role: {} +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + filter: {} + httpapi: + f5os: + description: HttpApi Plugin for F5OS devices + name: f5os + version_added: 1.0.0 + inventory: {} + lookup: {} + module: + f5os_allowed_ips: + description: Manage allowed IPs using openAPI on F5OS based systems + name: f5os_allowed_ips + namespace: '' + version_added: 1.9.0 + f5os_auth: + description: Manage authentication settings + name: f5os_auth + namespace: '' + version_added: 1.10.0 + f5os_config_backup: + description: Manage F5OS config backups. + name: f5os_config_backup + namespace: '' + version_added: 1.2.0 + f5os_device_info: + description: Collect information from F5OS devices + name: f5os_device_info + namespace: '' + version_added: 1.0.0 + f5os_dns: + description: Manage DNS on F5OS Devices + name: f5os_dns + namespace: '' + version_added: 1.8.0 + f5os_interface: + description: Manage network interfaces on F5OS based systems + name: f5os_interface + namespace: '' + version_added: 1.0.0 + f5os_lag: + description: Manage LAG interfaces on F5OS based systems + name: f5os_lag + namespace: '' + version_added: 1.0.0 + f5os_license: + description: Manage F5OS license activation and deactivation. + name: f5os_license + namespace: '' + version_added: 1.10.0 + f5os_lldp_config: + description: Manage LLDP config + name: f5os_lldp_config + namespace: '' + version_added: 1.8.0 + f5os_logging: + description: Manage logging settings + name: f5os_logging + namespace: '' + version_added: 1.10.0 + f5os_ntp_server: + description: Manage NTP servers on F5OS based systems + name: f5os_ntp_server + namespace: '' + version_added: 1.8.0 + f5os_primarykey: + description: Manage F5OS Devices Primary-key Setting. + name: f5os_primarykey + namespace: '' + version_added: 1.11.0 + f5os_qkview: + description: Manage Generation of qkview file + name: f5os_qkview + namespace: '' + version_added: 1.0.0 + f5os_snmp: + description: Manage SNMP Communities, Users, and Targets using openAPI on F5OS + based systems + name: f5os_snmp + namespace: '' + version_added: 1.9.0 + f5os_stp_config: + description: Manage STP config + name: f5os_stp_config + namespace: '' + version_added: 1.8.0 + f5os_system: + description: Manage generic system settings + name: f5os_system + namespace: '' + version_added: 1.10.0 + f5os_system_image_import: + description: Manage F5OS System image import. + name: f5os_system_image_import + namespace: '' + version_added: 1.11.0 + f5os_system_image_install: + description: Manage F5OS system software installation. + name: f5os_system_image_install + namespace: '' + version_added: 1.11.0 + f5os_tenant: + description: Manage F5OS tenants + name: f5os_tenant + namespace: '' + version_added: 1.0.0 + f5os_tenant_image: + description: Manage F5OS tenant images + name: f5os_tenant_image + namespace: '' + version_added: 1.0.0 + f5os_tenant_wait: + description: Wait for a F5OS tenant condition before continuing + name: f5os_tenant_wait + namespace: '' + version_added: 1.0.0 + f5os_tls_cert_key: + description: Manage TLS certificate and key on F5OS devices. + name: f5os_tls_cert_key + namespace: '' + version_added: 1.11.0 + f5os_user: + description: Manage Users and roles on F5OS based systems + name: f5os_user + namespace: '' + version_added: 1.9.0 + f5os_vlan: + description: Manage VLANs on F5OS based systems + name: f5os_vlan + namespace: '' + version_added: 1.0.0 + velos_partition: + description: Manage VELOS chassis partitions + name: velos_partition + namespace: '' + version_added: 1.0.0 + velos_partition_change_password: + description: Provides access to VELOS chassis partition user authentication + methods + name: velos_partition_change_password + namespace: '' + version_added: 1.0.0 + velos_partition_image: + description: Manage VELOS chassis partition images + name: velos_partition_image + namespace: '' + version_added: 1.0.0 + velos_partition_wait: + description: Wait for a VELOS chassis partition to match a condition before + continuing + name: velos_partition_wait + namespace: '' + version_added: 1.0.0 + netconf: {} + shell: {} + strategy: {} + test: {} + vars: {} +version: 1.12.0 diff --git a/ansible_collections/f5networks/f5os/changelogs/changelog.yaml b/ansible_collections/f5networks/f5os/changelogs/changelog.yaml index 69af6bf..578b4c5 100644 --- a/ansible_collections/f5networks/f5os/changelogs/changelog.yaml +++ b/ansible_collections/f5networks/f5os/changelogs/changelog.yaml @@ -118,6 +118,17 @@ releases: name: f5os_tls_cert_key namespace: '' release_date: '2024-09-10' + 1.12.0: + changes: + bugfixes: + - f5os_lag - fixed a bug that used to occur while adding trunk or native vlans + minor_changes: + - f5os_tenant - added a new parameter, virtual_disk_size, to set the virtual + disk size of the tenant + fragments: + - lag_bugfix.yaml + - tenant_virtual_disk_size_param.yaml + release_date: '2024-10-24' 1.2.0: modules: - description: Manage F5OS config backups. diff --git a/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml b/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml deleted file mode 100644 index 9efcfc3..0000000 --- a/ansible_collections/f5networks/f5os/changelogs/fragments/lag_bugfix.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - f5os_lag - fixed a bug that used to occur while adding trunk or native vlans diff --git a/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml b/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml deleted file mode 100644 index 5a845f1..0000000 --- a/ansible_collections/f5networks/f5os/changelogs/fragments/tenant_virtual_disk_size_param.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: - - f5os_tenant - added a new parameter, virtual_disk_size, to set the virtual disk size of the tenant diff --git a/ansible_collections/f5networks/f5os/galaxy.yml b/ansible_collections/f5networks/f5os/galaxy.yml index 8d8f4a9..cd21205 100644 --- a/ansible_collections/f5networks/f5os/galaxy.yml +++ b/ansible_collections/f5networks/f5os/galaxy.yml @@ -30,4 +30,4 @@ tags: - networking - rseries - velos -version: 1.12.0-devel +version: 1.12.0 diff --git a/ansible_collections/f5networks/f5os/plugins/module_utils/version.py b/ansible_collections/f5networks/f5os/plugins/module_utils/version.py index 1446445..4321aa1 100644 --- a/ansible_collections/f5networks/f5os/plugins/module_utils/version.py +++ b/ansible_collections/f5networks/f5os/plugins/module_utils/version.py @@ -4,4 +4,4 @@ # GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # This collection version needs to be updated at each release -CURRENT_COLL_VERSION = "1.12.0-devel" +CURRENT_COLL_VERSION = "1.12.0" diff --git a/ansible_collections/f5networks/f5os/tests/compat/mock.py b/ansible_collections/f5networks/f5os/tests/compat/mock.py index 0972cd2..c7d0183 100644 --- a/ansible_collections/f5networks/f5os/tests/compat/mock.py +++ b/ansible_collections/f5networks/f5os/tests/compat/mock.py @@ -64,8 +64,7 @@ def _iterate_read_data(read_data): # newline that our naive format() added data_as_list[-1] = data_as_list[-1][:-1] - for line in data_as_list: - yield line + yield from data_as_list def mock_open(mock=None, read_data=''): """ @@ -93,8 +92,7 @@ def _readline_side_effect(): if handle.readline.return_value is not None: while True: yield handle.readline.return_value - for line in _data: - yield line + yield from _data global file_spec if file_spec is None: diff --git a/ansible_collections/f5networks/f5os/tests/mock/loader.py b/ansible_collections/f5networks/f5os/tests/mock/loader.py index c41a6ea..edeac45 100644 --- a/ansible_collections/f5networks/f5os/tests/mock/loader.py +++ b/ansible_collections/f5networks/f5os/tests/mock/loader.py @@ -30,7 +30,7 @@ class DictDataLoader(DataLoader): def __init__(self, file_mapping=None): file_mapping = {} if file_mapping is None else file_mapping - assert type(file_mapping) is dict + assert isinstance(file_mapping, dict) super(DictDataLoader, self).__init__() diff --git a/ansible_collections/f5networks/f5os/tests/utils/common.py b/ansible_collections/f5networks/f5os/tests/utils/common.py index 27e4cb1..5809d5b 100644 --- a/ansible_collections/f5networks/f5os/tests/utils/common.py +++ b/ansible_collections/f5networks/f5os/tests/utils/common.py @@ -20,6 +20,6 @@ def connection_response(response, status=200, headers=None): if headers is None: headers = BASE_HEADERS response_mock.getheaders.return_value = headers.items() - response_text = json.dumps(response) if type(response) is dict else response + response_text = json.dumps(response) if isinstance(response, dict) else response response_data = BytesIO(response_text.encode() if response_text else ''.encode()) return response_mock, response_data