diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml new file mode 100644 index 0000000..a95f5dd --- /dev/null +++ b/.github/workflows/molecule.yml @@ -0,0 +1,51 @@ +--- +# This is a basic workflow to help you get started with Actions +name: Molecule + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + pull_request: + branches: + - master + - tags/* + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + lint: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: install lint prerequisite + run: | + sudo apt-get update + sudo apt -y install python3-setuptools ansible-lint vagrant + + - name: Install molecule + run: | + sudo apt update + sudo apt -y install python3-setuptools python3 python3-pip + sudo pip3 install wheel + sudo pip3 install molecule testinfra yamllint ansible-lint flake8 molecule-vagrant + + - name: molecule lint + run: | + molecule lint + + requirements: + # The type of runner that the job will run on + runs-on: ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - name: install prereq + run: | + ansible-galaxy role install -r requirements.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81dc40e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +**/__pychache__ diff --git a/.travis.yml b/.travis.yml index dcafe13..e8389f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,27 @@ --- -language: python -python: "2.7" - -# Use the new container infrastructure -sudo: false - -# Install ansible -addons: - apt: - packages: - - python-pip - -install: - # Install ansible - - pip install ansible ansible-lint - - # Check ansible version - - ansible --version - # Create ansible.cfg with correct roles_path - - printf '[defaults]\nroles_path=../' >ansible.cfg +dist: bionic - # Compensate for repo name being different to the role - - ln -s $(pwd) ../stackhpc.libvirt-host - -script: - # Run Ansible lint against the role - - ansible-lint tasks/main.yml - - # Basic role syntax check - - ansible-playbook tests/test.yml -i tests/inventory --syntax-check - -notifications: - webhooks: https://galaxy.ansible.com/api/v1/notifications/ +language: python +python: + - "3.8" + +before_install: + - | + sudo apt -y install bridge-utils dnsmasq-base ebtables libvirt-bin libvirt-dev qemu-kvm qemu-utils ruby-dev + + - | + wget https://releases.hashicorp.com/vagrant/2.2.7/vagrant_2.2.7_x86_64.deb + sudo apt -y install ./vagrant_2.2.7_x86_64.deb + sudo vagrant plugin install vagrant-libvirt + + - | + sudo apt-get -y purge python3-openssl && sudo apt-get -y autoremove + sudo apt-get update && sudo apt-get install -y ca-certificates curl gcc iproute2 python3 python3-dev sudo + curl -skL https://bootstrap.pypa.io/get-pip.py | sudo -H python3 + sudo pip3 install wheel + sudo pip3 install netaddr python-vagrant yamllint testinfra flake8 + sudo pip3 install ansible ansible-lint + sudo pip3 install -I molecule molecule-vagrant + +script: travis_wait 60 sudo molecule test --scenario-name kvm diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8827676 --- /dev/null +++ b/.yamllint @@ -0,0 +1,33 @@ +--- +# Based on ansible-lint config +extends: default + +rules: + braces: + max-spaces-inside: 1 + level: error + brackets: + max-spaces-inside: 1 + level: error + colons: + max-spaces-after: -1 + level: error + commas: + max-spaces-after: -1 + level: error + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + max: 3 + level: error + hyphens: + level: error + indentation: disable + key-duplicates: enable + line-length: disable + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable + truthy: disable diff --git a/README.md b/README.md index de88ef0..f0b1ab2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ should be a dict containing the following items: - `source` The name of the volume group. (only when type is `logical`) - `pvs` A list of physical volumes the volume group consists of. (only when type is `logical`) +- `pvs` A list of physical volumes the volume group consists of. (only when + type is `logical`). N.B. if specified, the volume group will be created on + top of the PVs, otherwise the logical volume should have been created before. `libvirt_host_networks` is a list of networks to define and start. Each item should be a dict containing the following items: @@ -84,7 +87,7 @@ installed. Dependencies ------------ -None +* [The LVM role](https://github.com/mrlesmithjr/ansible-manage-lvm) You can install it using `ansible-galaxy install -r requirements.yml --roles-p ../community` Example Playbook ---------------- @@ -94,6 +97,11 @@ Example Playbook hosts: all roles: - role: stackhpc.libvirt-host + lvm_groups: # see according properties on [The LVM role](https://github.com/mrlesmithjr/ansible-manage-lvm) + - vgname: libvirtvg + disks: + - /dev/sdb1 + create: true libvirt_host_pools: - name: my-pool type: dir @@ -104,10 +112,7 @@ Example Playbook group: my-group - name: lvm_pool type: logical - source: vg1 - target: /dev/vg1 - pvs: - - /dev/sda3 + source: libvirtvg libvirt_host_networks: - name: br-example mode: bridge diff --git a/handlers/main.yml b/handlers/main.yml index 8193fde..da56086 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -4,4 +4,23 @@ service: name: libvirtd state: restarted - become: true + become: True + listen: restart libvirt + +- name: Ensure libvirt storage pools are active + virt_pool: + name: "{{ item.name }}" + state: active + uri: "{{ libvirt_host_uri | default(omit, true) }}" + with_items: "{{ libvirt_host_pools }}" + become: True + listen: Ensure libvirt storage pools are active + +- name: Ensure libvirt networks are active + virt_net: + name: "{{ item.name }}" + state: active + uri: "{{ libvirt_host_uri | default(omit, true) }}" + with_items: "{{ libvirt_host_networks }}" + become: True + listen: Ensure libvirt networks are active diff --git a/molecule/default/Vagrantfile b/molecule/default/Vagrantfile new file mode 100644 index 0000000..313756f --- /dev/null +++ b/molecule/default/Vagrantfile @@ -0,0 +1,18 @@ +Vagrant.configure(2) do |config| + config.vm.provider :libvirt do |libvirt| + libvirt.memory = "512" + libvirt.cpus = 1 + libvirt.driver = "kvm" + libvirt.graphics_type = 'none' + end + + config.vm.box = "fedora/31-cloud-base" + config.vm.synced_folder ".", "/vagrant", disabled: true + + config.vm.define "myvm" do |myvm| +# myvm.vm.network :public_network, +# :dev => "virbr0", +# :mode => "bridge", +# :type => "bridge" + end +end \ No newline at end of file diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..1d032e9 --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,64 @@ +--- +- name: Converge + hosts: all + vars: + manage_lvm: true + lvm_groups: + - vgname: libvirt_vg + disks: + - /dev/sdb1 + create: true + users_group_list: + - name: libvirt + systemusers_user_list: + - name: libvirt + group: libvirt + groups: wheel + libvirt_host_pools: + - name: libvirtpool + type: lvm2 + source: libvirt_vg + libvirt_host_networks: + - name: ansible-virtualization-bridge + mode: bridge + bridge: bridge0 + tasks: + - name: "Include ansible-role-libvirt-host" + include_role: + name: "ansible-role-libvirt-host" + + - name: "Post converge - Install vagrant" + package: + name: + - qemu + - libvirt + - ruby-devel + - gcc + - qemu-kvm + - libxml2-devel + - libxslt-devel + - libguestfs-tools-c + - vagrant + - daemonize + state: present + become: true + + - name: Copy vagrant file + copy: + src: Vagrantfile + dest: /home/vagrant/Vagrantfile + owner: vagrant + group: vagrant + mode: '0644' + + - name: Execute Vagrant as daemon + command: + cmd: "daemonize -e /home/vagrant/myvmerr.log -o /home/vagrant/myvm.log -c /home/vagrant /usr/bin/vagrant up --provider=libvirt" + chdir: /home/vagrant + creates: /home/vagrant/myvm.log + + - name: Wait until the string "auth" is in the vagrant log + wait_for: + path: /home/vagrant/myvm.log + search_regex: auth + timeout: 1000 \ No newline at end of file diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..b439512 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,39 @@ +--- +dependency: + name: galaxy + options: + role-file: requirements.yml + roles-path: ../community +driver: + name: vagrant + provider: + name: virtualbox + +lint: yamllint . && flake8 && ansible-lint --exclude=meta +platforms: + - name: Fedora-Molecule-Virtualization + box: fedora/31-cloud-base + memory: 8000 + cpus: 4 + provider_raw_config_args: + - "customize ['modifyvm', :id, '--nested-hw-virt', 'on']" + provider_override_args: + - "persistent_storage.enabled = true" + - "persistent_storage.location = 'molecule-virtualization.vdi'" + - "persistent_storage.size = 100" + - "persistent_storage.mount = false" + - "persistent_storage.diskdevice = '/dev/sdb'" + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: yes + pre_build_image: yes +provisioner: + name: ansible + env: + ANSIBLE_ROLES_PATH: ../../..:../../../community +verifier: + name: testinfra + env: + PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning" + options: + v: 1 diff --git a/molecule/default/tests/__pycache__/conftest.cpython-38-pytest-5.4.1.pyc b/molecule/default/tests/__pycache__/conftest.cpython-38-pytest-5.4.1.pyc new file mode 100644 index 0000000..0468951 Binary files /dev/null and b/molecule/default/tests/__pycache__/conftest.cpython-38-pytest-5.4.1.pyc differ diff --git a/molecule/default/tests/__pycache__/test_default.cpython-38-pytest-5.4.1.pyc b/molecule/default/tests/__pycache__/test_default.cpython-38-pytest-5.4.1.pyc new file mode 100644 index 0000000..82ee39e Binary files /dev/null and b/molecule/default/tests/__pycache__/test_default.cpython-38-pytest-5.4.1.pyc differ diff --git a/molecule/default/tests/conftest.py b/molecule/default/tests/conftest.py new file mode 100644 index 0000000..7bd2743 --- /dev/null +++ b/molecule/default/tests/conftest.py @@ -0,0 +1,21 @@ +"""PyTest Fixtures.""" +from __future__ import absolute_import +import os +import pytest + + +def pytest_runtest_setup(item): + """Run tests only when under molecule with testinfra installed.""" + try: + import testinfra + except ImportError: + pytest.skip("Test requires testinfra", allow_module_level=True) + if "MOLECULE_INVENTORY_FILE" in os.environ: + pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] + ).get_hosts("all") + else: + pytest.skip( + "Test should run only from inside molecule.", + allow_module_level=True + ) diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py new file mode 100644 index 0000000..902d821 --- /dev/null +++ b/molecule/default/tests/test_default.py @@ -0,0 +1,41 @@ +"""Role testing files using testinfra.""" + + +def test_vagrant_machine_is_fully_up(host): + command = """cat myvm.log | grep -c 'myvm: SSH auth method: private key'""" + cmd = host.run(command) + assert '1' in cmd.stdout + + +def test_vagrant_machine_is_running(host): + command = r"""vagrant status | egrep -c 'myvm\s*running\s\(libvirt\)'""" + cmd = host.run(command) + assert '1' in cmd.stdout + + +def test_pool_is_started(host): + command = r""" sudo virsh pool-list --all | + cut -d " " -f 5 | tail -n +3 | head -n +1 | egrep -c '^active$'""" + cmd = host.run(command) + assert '1' in cmd.stdout + + +def test_pool_is_autostarted(host): + command = r""" sudo virsh pool-list --all | + cut -d " " -f 8 | tail -n +3 | head -n +1 | egrep -c '^yes$'""" + cmd = host.run(command) + assert '1' in cmd.stdout + + +def test_net_is_started(host): + command = r""" sudo virsh net-list --all | + cut -d " " -f 5 | tail -n +3 | head -n +1 | egrep -c '^active$'""" + cmd = host.run(command) + assert '1' in cmd.stdout + + +def test_net_is_autostarted(host): + command = r""" sudo virsh net-list --all | + cut -d " " -f 8 | tail -n +3 | head -n +1 | egrep -c '^yes$'""" + cmd = host.run(command) + assert '1' in cmd.stdout diff --git a/molecule/kvm/Vagrantfile b/molecule/kvm/Vagrantfile new file mode 100644 index 0000000..313756f --- /dev/null +++ b/molecule/kvm/Vagrantfile @@ -0,0 +1,18 @@ +Vagrant.configure(2) do |config| + config.vm.provider :libvirt do |libvirt| + libvirt.memory = "512" + libvirt.cpus = 1 + libvirt.driver = "kvm" + libvirt.graphics_type = 'none' + end + + config.vm.box = "fedora/31-cloud-base" + config.vm.synced_folder ".", "/vagrant", disabled: true + + config.vm.define "myvm" do |myvm| +# myvm.vm.network :public_network, +# :dev => "virbr0", +# :mode => "bridge", +# :type => "bridge" + end +end \ No newline at end of file diff --git a/molecule/kvm/converge.yml b/molecule/kvm/converge.yml new file mode 100644 index 0000000..60243af --- /dev/null +++ b/molecule/kvm/converge.yml @@ -0,0 +1,64 @@ +--- +- name: Converge + hosts: all + vars: + manage_lvm: true + lvm_groups: + - vgname: libvirt_vg + disks: + - /dev/vdb + create: true + users_group_list: + - name: libvirt + systemusers_user_list: + - name: libvirt + group: libvirt + groups: wheel + libvirt_host_pools: + - name: libvirtpool + type: lvm2 + source: libvirt_vg + libvirt_host_networks: + - name: ansible-virtualization-bridge + mode: bridge + bridge: bridge0 + tasks: + - name: "Include ansible-role-libvirt-host" + include_role: + name: "ansible-role-libvirt-host" + + - name: "Post converge - Install vagrant" + package: + name: + - qemu + - libvirt + - ruby-devel + - gcc + - qemu-kvm + - libxml2-devel + - libxslt-devel + - libguestfs-tools-c + - vagrant + - daemonize + state: present + become: true + + - name: Copy vagrant file + copy: + src: Vagrantfile + dest: /home/vagrant/Vagrantfile + owner: vagrant + group: vagrant + mode: '0644' + + - name: Execute Vagrant as daemon + command: + cmd: "daemonize -e /home/vagrant/myvmerr.log -o /home/vagrant/myvm.log -c /home/vagrant /usr/bin/vagrant up --provider=libvirt" + chdir: /home/vagrant + creates: /home/vagrant/myvm.log + + - name: Wait until the string "auth" is in the vagrant log + wait_for: + path: /home/vagrant/myvm.log + search_regex: auth + timeout: 1800 \ No newline at end of file diff --git a/molecule/kvm/molecule.yml b/molecule/kvm/molecule.yml new file mode 100644 index 0000000..7c5dd16 --- /dev/null +++ b/molecule/kvm/molecule.yml @@ -0,0 +1,36 @@ +--- +dependency: + name: galaxy + options: + role-file: requirements.yml + roles-path: ../community +driver: + name: vagrant + provider: + name: libvirt +lint: yamllint . && flake8 && ansible-lint --exclude=meta +platforms: + - name: Fedora-Molecule-virtualization + box: fedora/31-cloud-base + cpus: 2 + memory: 2048 + provider_raw_config_args: + - "storage :file, :size => '1G', :device => 'vdb'" +provisioner: + name: ansible + config_options: + defaults: + forks: 20 + ssh_connection: + pipelining: true + ssh_args: -o ControlMaster=auto -o ControlPersist=600s + env: + ANSIBLE_ROLES_PATH: ../../..:../../../community +verifier: + name: testinfra + env: + PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning" + options: + v: 1 +scenario: + name: kvm diff --git a/molecule/kvm/tests b/molecule/kvm/tests new file mode 120000 index 0000000..5456f39 --- /dev/null +++ b/molecule/kvm/tests @@ -0,0 +1 @@ +../default/tests \ No newline at end of file diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..32eef5e --- /dev/null +++ b/requirements.yml @@ -0,0 +1,5 @@ +--- +roles: + - name: tcharl.ansible_manage_lvm + source: https://galaxy.ansible.com + path: ../community diff --git a/tasks/config.yml b/tasks/config.yml index 52fc2f3..c4a6519 100644 --- a/tasks/config.yml +++ b/tasks/config.yml @@ -7,7 +7,7 @@ insertafter: '^#unix_sock_dir =' regexp: '^unix_sock_dir =' line: unix_sock_dir = "{{ libvirt_host_socket_dir }}" - become: true + become: True when: libvirt_host_socket_dir | length > 0 notify: restart libvirt @@ -18,25 +18,23 @@ owner: root group: root mode: 0755 - become: true + become: True when: libvirt_host_socket_dir | length > 0 - name: Process lineinfile rules lineinfile: "{{ rule.args }}" - become: true + become: True loop: "{{ libvirt_host_lineinfile_extra_rules | default([]) }}" loop_control: loop_var: rule when: rule.condition - notify: - - restart libvirt + notify: restart libvirt - name: Flush handlers meta: flush_handlers - name: Ensure the libvirt daemon is started and enabled - service: + systemd: name: libvirtd state: started - enabled: yes become: True diff --git a/tasks/networks.yml b/tasks/networks.yml index 3c74095..cc1aeb3 100644 --- a/tasks/networks.yml +++ b/tasks/networks.yml @@ -7,14 +7,7 @@ uri: "{{ libvirt_host_uri | default(omit, true) }}" with_items: "{{ libvirt_host_networks }}" become: True - -- name: Ensure libvirt networks are active - virt_net: - name: "{{ item.name }}" - state: active - uri: "{{ libvirt_host_uri | default(omit, true) }}" - with_items: "{{ libvirt_host_networks }}" - become: True + notify: Ensure libvirt networks are active - name: Ensure libvirt networks are started on boot virt_net: diff --git a/tasks/pools.yml b/tasks/pools.yml index d582d9f..70d0c2a 100644 --- a/tasks/pools.yml +++ b/tasks/pools.yml @@ -1,4 +1,12 @@ --- +- name: Include lvm role to create VG + include_role: + name: tcharl.ansible_manage_lvm + when: > + manage_lvm is defined and + manage_lvm and + libvirt_host_pools | map(attribute='type') | select('match', '^lvm2$') | join(',') | length > 0 + - name: Ensure libvirt dir storage pool directories exist file: path: "{{ item.path }}" @@ -10,14 +18,6 @@ with_items: "{{ libvirt_host_pools }}" become: True -- name: Ensure libvirt LVM storage pool directories exist - lvg: - vg: "{{ item.source }}" - pvs: "{{ item.pvs }}" - when: item.type in ["lvm2", "logical"] - with_items: "{{ libvirt_host_pools }}" - become: True - - name: Ensure libvirt storage pools are defined virt_pool: name: "{{ item.name }}" @@ -25,14 +25,7 @@ xml: "{{ item.xml | default(lookup('template', 'pool.xml.j2')) }}" uri: "{{ libvirt_host_uri | default(omit, true) }}" with_items: "{{ libvirt_host_pools }}" - become: True - -- name: Ensure libvirt storage pools are active - virt_pool: - name: "{{ item.name }}" - state: active - uri: "{{ libvirt_host_uri | default(omit, true) }}" - with_items: "{{ libvirt_host_pools }}" + notify: Ensure libvirt storage pools are active become: True - name: Ensure libvirt storage pools are started on boot diff --git a/tasks/post-install-Debian.yml b/tasks/post-install-Debian.yml index 0e5bfaf..03f1b00 100644 --- a/tasks/post-install-Debian.yml +++ b/tasks/post-install-Debian.yml @@ -21,7 +21,7 @@ insertafter: '^#libvirtd_opts=' regexp: '^libvirtd_opts=' line: "libvirtd_opts={{ libvirt_host_libvirtd_args }}" - condition: "{{ libvirt_host_libvirtd_args != '' }}" + condition: "{{ libvirt_host_libvirtd_args | length > 0 }}" vars: libvirt_env_path: "{{ '/etc/default/libvirt-bin' if libvirt_bin_stat.stat.exists else '/etc/default/libvirtd' }}" tags: vars