From 696814e296cd80cbde134659b61b7e30424d1bc3 Mon Sep 17 00:00:00 2001 From: Giulio Fidente Date: Mon, 9 Dec 2019 12:15:08 +0100 Subject: [PATCH] Add tripleo-lvmfilter role to restrict visible block devices for LVM2 The lvmfilter role creates an LVM filter looking at which physical devices are in use by active logical volumes. Change-Id: I9781007559e074f2b102f6f90c1aed6def1b02be Related-Bug: 1855704 --- doc/source/roles/role-tripleo_lvmfilter.rst | 6 ++ .../modules/lvm2_physical_devices_facts.py | 102 ++++++++++++++++++ .../roles/tripleo_lvmfilter/defaults/main.yml | 23 ++++ .../roles/tripleo_lvmfilter/handlers/main.yml | 20 ++++ .../roles/tripleo_lvmfilter/meta/main.yml | 37 +++++++ .../molecule/default/converge.yml | 21 ++++ .../molecule/default/molecule.yml | 49 +++++++++ .../molecule/default/prepare.yml | 24 +++++ .../roles/tripleo_lvmfilter/tasks/main.yml | 67 ++++++++++++ .../test_lvm2_physical_devices_facts.py | 34 ++++++ zuul.d/molecule.yaml | 10 ++ 11 files changed, 393 insertions(+) create mode 100644 doc/source/roles/role-tripleo_lvmfilter.rst create mode 100644 tripleo_ansible/ansible_plugins/modules/lvm2_physical_devices_facts.py create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/defaults/main.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/handlers/main.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/meta/main.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/converge.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/molecule.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/prepare.yml create mode 100644 tripleo_ansible/roles/tripleo_lvmfilter/tasks/main.yml create mode 100644 tripleo_ansible/tests/modules/test_lvm2_physical_devices_facts.py diff --git a/doc/source/roles/role-tripleo_lvmfilter.rst b/doc/source/roles/role-tripleo_lvmfilter.rst new file mode 100644 index 000000000..f5dfd2a81 --- /dev/null +++ b/doc/source/roles/role-tripleo_lvmfilter.rst @@ -0,0 +1,6 @@ +======================== +Role - tripleo_lvmfilter +======================== + +.. ansibleautoplugin:: + :role: tripleo_ansible/roles/tripleo_lvmfilter diff --git a/tripleo_ansible/ansible_plugins/modules/lvm2_physical_devices_facts.py b/tripleo_ansible/ansible_plugins/modules/lvm2_physical_devices_facts.py new file mode 100644 index 000000000..897d67c64 --- /dev/null +++ b/tripleo_ansible/ansible_plugins/modules/lvm2_physical_devices_facts.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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. +__metaclass__ = type + +DOCUMENTATION = """ +module: lvm2_physical_devices_facts +short_description: Gather list of block devices in use by LVM2 +version_added: '1.0.0' +description: Gather list of block devices in use by LVM2 as PVs +author: + - "Giulio Fidente (@gfidente)" +""" + +EXAMPLES = """ +- name: Get list of LVM2 PVs + lvm2_physical_devices_facts: +""" + +RETURN = """ +ansible_facts: + description: List of PVs in use + returned: always + type: dict + contains: + lvm2_active_pvs: + description: List of LVM2 volumes hosting active LVs + type: list + returned: always but it might be empty + sample: ['/dev/sdb2'] +""" + +from ansible.module_utils.basic import AnsibleModule + + +def get_vgs_with_active_lvs(module): + command = ['lvs', '--noheadings', '--options', 'vg_name', '--select', 'lv_active=active'] + rc, out, err = module.run_command(command) + if rc != 0: + module.fail_json(msg="Failed to run LVM2 lvs command", err=err) + if not out: + return [] + vgs = list(set(out.split())) + return vgs + + +def get_pvs_in_use_by_active_vg(module, active_vg): + command = ['vgs', '--noheadings', '--options', 'pv_name', active_vg] + rc, out, err = module.run_command(command) + if rc != 0: + module.fail_json(msg="Failed to run LVM2 vgs command for %s" % (active_vg), err=err) + if not out: + return [] + pvs = list(set(out.split())) + return pvs + + +def run_module(): + module_args = {} + + result = dict( + changed=False, + ansible_facts=dict(), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + if module.check_mode: + module.exit_json(**result) + + active_vgs = get_vgs_with_active_lvs(module) + active_pvs = [] + for vg in active_vgs: + active_pvs.extend(get_pvs_in_use_by_active_vg(module, vg)) + pvs = {'lvm2_active_pvs': list(set(active_pvs))} + result['ansible_facts'] = pvs + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/defaults/main.yml b/tripleo_ansible/roles/tripleo_lvmfilter/defaults/main.yml new file mode 100644 index 000000000..6e00dc832 --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/defaults/main.yml @@ -0,0 +1,23 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +# All variables intended for modification should be placed in this file. + +# All variables within this role should have a prefix of "tripleo_tripleo_lvmfilter" +tripleo_tripleo_lvmfilter_enabled: false +tripleo_tripleo_lvmfilter_devices_allowlist: [] +tripleo_tripleo_lvmfilter_devices_denylist: [] diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/handlers/main.yml b/tripleo_ansible/roles/tripleo_lvmfilter/handlers/main.yml new file mode 100644 index 000000000..60cb71e9c --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/handlers/main.yml @@ -0,0 +1,20 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +- name: Refresh LVM caches + become: true + command: vgscan diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/meta/main.yml b/tripleo_ansible/roles/tripleo_lvmfilter/meta/main.yml new file mode 100644 index 000000000..e8e668ebf --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/meta/main.yml @@ -0,0 +1,37 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +galaxy_info: + author: OpenStack + description: TripleO OpenStack Role -- tripleo_lvmfilter + company: Red Hat + license: Apache-2.0 + min_ansible_version: 2.7 + # + # Provide a list of supported platforms, and for each platform a list of versions. + # If you don't wish to enumerate all versions for a particular platform, use 'all'. + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + platforms: + - name: CentOS + versions: + - 7 + - 8 + + galaxy_tags: + - tripleo diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/converge.yml b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/converge.yml new file mode 100644 index 000000000..42f0a0721 --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/converge.yml @@ -0,0 +1,21 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +- name: Converge + hosts: all + roles: + - role: "tripleo_lvmfilter" diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/molecule.yml b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/molecule.yml new file mode 100644 index 000000000..e528551da --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/molecule.yml @@ -0,0 +1,49 @@ +--- +driver: + name: podman + +log: true + +platforms: + - name: ubi8 + hostname: ubi8 + image: ubi8/ubi-init + registry: + url: registry.access.redhat.com + dockerfile: Dockerfile + pkg_extras: python*setuptools + volumes: + - /etc/ci/mirror_info.sh:/etc/ci/mirror_info.sh:ro + - /etc/pki/rpm-gpg:/etc/pki/rpm-gpg + - /opt/yum.repos.d:/etc/yum.repos.d:rw + privileged: true + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + ulimits: &ulimit + - host + +provisioner: + inventory: + hosts: + all: + hosts: + ubi8: + ansible_python_interpreter: /usr/bin/python3 + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - check + - verify + - destroy + +verifier: + name: testinfra diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/prepare.yml b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/prepare.yml new file mode 100644 index 000000000..392a71653 --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/molecule/default/prepare.yml @@ -0,0 +1,24 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +- name: Prepare + hosts: all + vars: + test_deps_extra_packages: + - lvm2 + roles: + - role: test_deps diff --git a/tripleo_ansible/roles/tripleo_lvmfilter/tasks/main.yml b/tripleo_ansible/roles/tripleo_lvmfilter/tasks/main.yml new file mode 100644 index 000000000..62c8db9f1 --- /dev/null +++ b/tripleo_ansible/roles/tripleo_lvmfilter/tasks/main.yml @@ -0,0 +1,67 @@ +--- +# Copyright 2020 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + + +- name: gather package facts + package_facts: + manager: auto +- name: gather allowed block devices list + when: "'lvm2' in ansible_facts.packages" + block: + - name: collect in-use lvm2 devices list + become: true + lvm2_physical_devices_facts: + - name: set allowed_devices + set_fact: + allowed_devices: "{{ (ansible_facts['lvm2_active_pvs'] | default([]) | list) + | intersect(ansible_devices.keys()) + + (tripleo_tripleo_lvmfilter_devices_allowlist | default([])) + | unique }}" + - name: create lvm.conf with global_filter + when: + - allowed_devices is defined + - (allowed_devices | length) > 0 + block: + - name: build lvm2 allow list + set_fact: + lvm2_allow_list: "\"{{ allowed_devices | map('regex_replace', '(.+)', 'a|\\1|') + | join('\",\"') }}\"" + - name: build lvm2 deny list + set_fact: + lvm2_deny_list: "\"{{ tripleo_tripleo_lvmfilter_devices_denylist | default([]) + | map('regex_replace', '(.+)', 'r|\\1|') | join('\",\"') }}\"" + - name: build lvm2 filter + set_fact: + filter: "{{ lvm2_allow_list + ',' + lvm2_deny_list }}" + - name: regenerate lvm config + become: true + command: > + lvmconfig -f /tmp/tripleo_lvmfilter.conf + --mergedconfig --withgeneralpreamble --withspaces --withsummary --withcomments --ignorelocal --showdeprecated + --config devices/global_filter='[{{ filter }}]' + - name: copy new lvm.conf in place + become: true + copy: + remote_src: true + src: /tmp/tripleo_lvmfilter.conf + dest: /etc/lvm/lvm.conf + owner: root + group: root + mode: '0644' + backup: true + when: tripleo_tripleo_lvmfilter_enabled + notify: + - Refresh LVM caches diff --git a/tripleo_ansible/tests/modules/test_lvm2_physical_devices_facts.py b/tripleo_ansible/tests/modules/test_lvm2_physical_devices_facts.py new file mode 100644 index 000000000..b0caad033 --- /dev/null +++ b/tripleo_ansible/tests/modules/test_lvm2_physical_devices_facts.py @@ -0,0 +1,34 @@ +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# 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 collections import Counter +from unittest import mock + +from tripleo_ansible.ansible_plugins.modules import lvm2_physical_devices_facts as lvm2 +from tripleo_ansible.tests import base as tests_base + + +class TestLvm2PhysicalDevicesFacts(tests_base.TestCase): + + def test_get_pvs(self): + mock_module = mock.Mock() + + mock_module.run_command.return_value = (0, ' myvgname\n myvgname\n', '') + result = lvm2.get_vgs_with_active_lvs(mock_module) + self.assertEqual(['myvgname'], result) + + mock_module.run_command.return_value = (0, ' /dev/sdb1\n /dev/sdb2\n', '') + result = lvm2.get_vgs_with_active_lvs(mock_module) + self.assertEqual(Counter(['/dev/sdb1', '/dev/sdb2']), Counter(result)) diff --git a/zuul.d/molecule.yaml b/zuul.d/molecule.yaml index f3ffada6c..c6dc715c6 100644 --- a/zuul.d/molecule.yaml +++ b/zuul.d/molecule.yaml @@ -30,6 +30,7 @@ - tripleo-ansible-centos-8-molecule-tripleo_image_serve - tripleo-ansible-centos-8-molecule-tripleo_kernel - tripleo-ansible-centos-8-molecule-tripleo_keystone_resources + - tripleo-ansible-centos-8-molecule-tripleo_lvmfilter - tripleo-ansible-centos-8-molecule-tripleo_module_load - tripleo-ansible-centos-8-molecule-tripleo_network_config - tripleo-ansible-centos-8-molecule-tripleo_nodes_validation @@ -81,6 +82,7 @@ - tripleo-ansible-centos-8-molecule-tripleo_image_serve - tripleo-ansible-centos-8-molecule-tripleo_kernel - tripleo-ansible-centos-8-molecule-tripleo_keystone_resources + - tripleo-ansible-centos-8-molecule-tripleo_lvmfilter - tripleo-ansible-centos-8-molecule-tripleo_module_load - tripleo-ansible-centos-8-molecule-tripleo_network_config - tripleo-ansible-centos-8-molecule-tripleo_nodes_validation @@ -132,6 +134,7 @@ - tripleo-ansible-centos-8-molecule-tripleo_image_serve - tripleo-ansible-centos-8-molecule-tripleo_kernel - tripleo-ansible-centos-8-molecule-tripleo_keystone_resources + - tripleo-ansible-centos-8-molecule-tripleo_lvmfilter - tripleo-ansible-centos-8-molecule-tripleo_module_load - tripleo-ansible-centos-8-molecule-tripleo_network_config - tripleo-ansible-centos-8-molecule-tripleo_nodes_validation @@ -350,6 +353,13 @@ parent: tripleo-ansible-centos-8-base vars: tripleo_role_name: tripleo_keystone_resources +- job: + files: + - ^tripleo_ansible/roles/tripleo_lvmfilter/.* + name: tripleo-ansible-centos-8-molecule-tripleo_lvmfilter + parent: tripleo-ansible-centos-8-base + vars: + tripleo_role_name: tripleo_lvmfilter - job: files: - ^tripleo_ansible/roles/tripleo_module_load/.*