From 2d0656f072193e25f2898bd2c5e08dec7c3afe1f Mon Sep 17 00:00:00 2001 From: Will Szumski Date: Tue, 23 Aug 2022 10:36:05 +0000 Subject: [PATCH] Allow networks to be updated Neutron will allow you to update certain properties of the network after creation. This change modifies the ansible code to perform an update call if it detects that any updatable properties have been changed. If you attempt to change a property that cannot be updated, the module will fail. This gives you confidence that the ansible configuration matches the state of the network in OpenStack. If we did not fail in this way, you might think you have updated the network, but in reality those changes would be silently ignored. Prior to this change, the only way to update properties of a network was to delete it and then recreate. Story: 2010024 Task: 45262 Change-Id: I4af2b50f207f349b58c63e0a4e92816ada0847fd --- ci/roles/network/defaults/main.yml | 1 + ci/roles/network/tasks/main.yml | 63 ++++++++++++++++++++++++++++++ plugins/modules/network.py | 62 +++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 16 deletions(-) diff --git a/ci/roles/network/defaults/main.yml b/ci/roles/network/defaults/main.yml index 8b592206..321ccd97 100644 --- a/ci/roles/network/defaults/main.yml +++ b/ci/roles/network/defaults/main.yml @@ -31,5 +31,6 @@ mtu: 1250 network_external: false network_name: shade_network network_name_newparams: newparams_network +network_name_updates: update_network network_shared: false port_security_enabled: false diff --git a/ci/roles/network/tasks/main.yml b/ci/roles/network/tasks/main.yml index 60078f16..32c4191f 100644 --- a/ci/roles/network/tasks/main.yml +++ b/ci/roles/network/tasks/main.yml @@ -105,3 +105,66 @@ assert: that: - result_nonet.networks == [] + +- name: Create network - updates + openstack.cloud.network: + cloud: "{{ cloud }}" + name: "{{ network_name_updates }}" + state: present + shared: "{{ network_shared }}" + external: "{{ network_external }}" + mtu: "{{ mtu }}" + port_security_enabled: "{{ port_security_enabled }}" + register: result_create_nw_for_updates + +- name: Update network - update failure + openstack.cloud.network: + cloud: "{{ cloud }}" + name: "{{ network_name_updates }}" + state: present + shared: "{{ network_shared }}" + external: "{{ network_external }}" + mtu: "{{ mtu }}" + port_security_enabled: "{{ port_security_enabled }}" + # You cannot update this property. + provider_physical_network: cannot_be_updated + ignore_errors: true + register: result_nw_update_failure + +- name: Verify networks info - update fail + assert: + that: + - result_nw_update_failure is failed + +- name: Update network - update success + openstack.cloud.network: + cloud: "{{ cloud }}" + name: "{{ network_name_updates }}" + state: present + shared: "{{ network_shared }}" + external: "{{ network_external }}" + # NOTE: This property should be updated + mtu: "{{ mtu - 50 }}" + # NOTE: This property should be updated + port_security_enabled: "{{ not port_security_enabled }}" + register: result_nw_update_success + +- name: Gather networks info - updates + openstack.cloud.networks_info: + cloud: "{{ cloud }}" + name: "{{ network_name_updates }}" + register: result_network_updates_info + +- name: Verify networks info - update success + assert: + that: + - result_nw_update_success is changed + - result_network_updates_info.networks.0.name == network_name_updates + - result_network_updates_info.networks.0.mtu == mtu - 50 + - result_network_updates_info.networks.0['is_port_security_enabled'] == (not port_security_enabled) + +- name: Delete network - updates + openstack.cloud.network: + cloud: "{{ cloud }}" + name: "{{ network_name_updates }}" + state: absent diff --git a/plugins/modules/network.py b/plugins/modules/network.py index 65ecba57..af50f17a 100644 --- a/plugins/modules/network.py +++ b/plugins/modules/network.py @@ -234,27 +234,57 @@ class NetworkModule(OpenStackModule): net = self.conn.network.find_network(name, **net_kwargs) if state == 'present': - if not net: - if provider_physical_network: - kwargs['provider_physical_network'] = provider_physical_network - if provider_network_type: - kwargs['provider_network_type'] = provider_network_type - if provider_segmentation_id: - kwargs['provider_segmentation_id'] = provider_segmentation_id + if provider_physical_network: + kwargs['provider_physical_network'] = provider_physical_network + if provider_network_type: + kwargs['provider_network_type'] = provider_network_type + if provider_segmentation_id: + kwargs['provider_segmentation_id'] = provider_segmentation_id - if project_id is not None: - kwargs['project_id'] = project_id - net = self.conn.network.create_network(name=name, - shared=shared, - admin_state_up=admin_state_up, - is_router_external=external, - **kwargs) + if project_id is not None: + kwargs['project_id'] = project_id + + kwargs["shared"] = shared + kwargs["admin_state_up"] = admin_state_up + kwargs["is_router_external"] = external + + if not net: + net = self.conn.network.create_network(name=name, **kwargs) changed = True - net = net.to_dict(computed=False) else: changed = False - self.exit(changed=changed, network=net, id=net['id']) + update_kwargs = {} + # Check we are not trying to update an properties that cannot be modified + non_updatables = [ + "provider_network_type", + "provider_physical_network", + ] + for arg in non_updatables: + if arg in kwargs and kwargs[arg] != net[arg]: + self.fail_json( + msg="The following parameters cannot be updated: " + "%s. You will need to use state: absent and " + "recreate." % ', '.join(non_updatables) + ) + + # Filter args to update call to the ones that have been modifed + # and are updatable. Adapted from: + # https://github.com/openstack/openstacksdk/blob/1ce15c9a8758b4d978eb5239bae100ddc13c8875/openstack/cloud/_network.py#L559-L561 + for arg in ["shared", "admin_state_up", "is_router_external", + "mtu", "port_security_enabled", "dns_domain", + "provider_segmentation_id"]: + if arg in kwargs and kwargs[arg] != net[arg]: + update_kwargs[arg] = kwargs[arg] + + if update_kwargs: + net = self.conn.network.update_network( + net.id, **update_kwargs + ) + changed = True + + net = net.to_dict(computed=False) + self.exit(changed=changed, network=net, id=net['id']) elif state == 'absent': if not net: self.exit(changed=False)