├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── defaults └── main.yml ├── meta └── main.yml ├── package.json ├── tasks ├── create.yml ├── install.yml ├── main.yml ├── rename.yml └── secondary_storage.yml ├── templates ├── apt_proxy.j2 ├── environment.j2 ├── first_boot.sh.j2 ├── interfaces.j2 ├── netplan.yml.j2 ├── network_interfaces.j2 ├── proxy.sh.j2 ├── user.pub.j2 ├── virt.conf.j2 └── vmbuilder.partitions.j2 ├── vars └── main.yaml └── yamllint.yaml /.gitattributes: -------------------------------------------------------------------------------- 1 | *.yaml linguist-language=Ruby 2 | *.yml linguist-language=Ruby 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * **I'm submitting a ...** 2 | [ ] bug report 3 | [ ] feature request 4 | [ ] question about the decisions made in the repository 5 | 6 | * **Do you want to request a *feature* or report a *bug*?** 7 | 8 | 9 | 10 | * **What is the current behavior?** 11 | 12 | 13 | 14 | * **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via 15 | https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). 16 | 17 | 18 | 19 | * **What is the expected behavior?** 20 | 21 | 22 | 23 | * **What is the motivation / use case for changing the behavior?** 24 | 25 | 26 | 27 | * **Please tell us about your environment:** 28 | 29 | - Angular version: 2.0.0-rc.X 30 | - Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] 31 | 32 | 33 | 34 | * **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) 35 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What kind of change does this PR introduce? 2 | 3 | 4 | #### Changed behavior 5 | 6 | 7 | #### PivotalTracker Stories 8 | [#000000000](https://www.pivotaltracker.com/story/show/000000000) 9 | 10 | 11 | #### Other information, known issues 12 | 13 | 14 | #### Tests 15 | 16 | 17 | #### Remaining Tasks 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | before_install: 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq python-apt python-pycurl 7 | install: 8 | - pip install ansible 9 | script: 10 | - echo travis.dev > inventory 11 | - ansible-playbook -i inventory --syntax-check --list-tasks test.yml 12 | - ansible-playbook -i inventory --extra-vars "common_name=travis.dev" --connection=local --sudo -vvvv test.yml 13 | addons: 14 | hosts: 15 | - travis.dev 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Isaac Kehle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role - virsh 2 | 3 | VM Deployment using virsh 4 | 5 | Available on Ansible Galaxy: [isaackehle.virsh](https://galaxy.ansible.com/isaackehle/ansible_virsh) 6 | 7 | ## Variables 8 | 9 | ```yaml 10 | deploy_dir: Required for where the base path lives 11 | ``` 12 | 13 | ## Tags 14 | 15 | ```YAML 16 | tags: 17 | - install 18 | - rename 19 | - create 20 | - storage 21 | ``` 22 | 23 | ## Examples 24 | 25 | ### Example to install the packages 26 | 27 | ```YAML 28 | - hosts: all 29 | 30 | vars: 31 | flags: ['install'] 32 | 33 | roles: 34 | - { role: isaackehle.virsh } 35 | ``` 36 | 37 | ### Example to create a vm 38 | 39 | ```YAML 40 | - hosts: all 41 | 42 | vars: 43 | flags: ['create'] 44 | vm_name: 'vm_name' 45 | vm_cfg: 46 | storage: 47 | primary: 48 | size: 320G 49 | type: xfs 50 | name: vda 51 | secondary: 52 | size: 3T 53 | type: xfs 54 | name: vdb 55 | vcpus: 4 56 | memory: 57 | size: 4096 58 | graphics: 59 | vnc: 60 | port: 5900 61 | network: 62 | do_proxy: true # Enable proxy settings for the new VM. Ensure proxy config is included. 63 | dchp: true # Whether to use DHCP 64 | # Required when not using DHCP 65 | ip: xx.xx.xx.xx # IP Address to use 66 | mask: x # Network Mask Value 67 | gateway: xx.xx.xx.xx # Network Gateway 68 | netmask: xx.xx.xx.xx # Netmask 69 | broadcast: xx.xx.xx.xx # Broadcast address 70 | mac: xxx.xxx.xxx.xxx # MAC Address 71 | proxy: 72 | addr: "address.com" 73 | port: "3128" 74 | 75 | 76 | roles: 77 | - { role: isaackehle.virsh } 78 | ``` 79 | 80 | ### Example to rename a vm 81 | 82 | ```YAML 83 | - hosts: all 84 | 85 | vars: 86 | flags: ['rename'] 87 | vm_name: 'vm_name' 88 | vm_old: 'old_vm' 89 | 90 | roles: 91 | - { role: isaackehle.virsh } 92 | ``` 93 | 94 | ### Example add secondary storage 95 | 96 | ```YAML 97 | - hosts: all 98 | 99 | vars: 100 | flags: ['secondary_storage'] 101 | vm_name: 'vm_name' 102 | vm_cfg: 103 | storage: 104 | primary: 105 | size: 320G 106 | type: xfs 107 | name: vda 108 | secondary: 109 | size: 3T 110 | type: xfs 111 | name: vdb 112 | base_image_path: '/mnt/storage/' 113 | 114 | roles: 115 | - { role: isaackehle.virsh } 116 | ``` 117 | 118 | ## Linting 119 | 120 | ```bash 121 | yamllint -c yamllint.yaml . 122 | ansible-lint . 123 | ``` 124 | 125 | ## License 126 | 127 | MIT 128 | 129 | ## Author Information 130 | 131 | Isaac Kehle 132 | @isaackehle ([twitter](https://twitter.com/isaackehle), [github](https://github.com/isaackehle), [linkedin](https://www.linkedin.com/in/isaackehle)) 133 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | flags: [] 4 | 5 | default_pwd: "defaultpwd" 6 | ssh_user_key: "/etc/ssl/private/key.pem" 7 | hostname: "" 8 | 9 | version: '18.04.1' 10 | 11 | vm_old: '' 12 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Isaac Kehle 3 | description: VM Deployment using virsh 4 | company: isaackehle 5 | license: MIT 6 | min_ansible_version: 2.4 7 | platforms: 8 | - name: Ubuntu 9 | versions: 10 | - xenial 11 | galaxy_tags: 12 | - system 13 | dependencies: [] 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ansible-virsh", 3 | "version": "2.0.0", 4 | "description": "VM Deployment using virsh", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "postversion": "git push && git push --tags" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/isaackehle/ansible-virsh.git" 12 | }, 13 | "author": "Isaac Kehle (isaac@kehle.org)", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/isaackehle/ansible-virsh/issues" 17 | }, 18 | "homepage": "https://github.com/isaackehle/ansible-virsh#readme", 19 | "main": "tasks/main.yml", 20 | "keywords": [ 21 | "ansible" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tasks/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Standard Facts 3 | set_fact: 4 | version_cfg: "{{ versionConfigs[version] }}" 5 | iface_name: ens3 6 | img_file: "{{ base_image_path }}/{{ vm_cfg.storage.primary.name }}.qcow2" 7 | tmp_prefix: "/tmp/{{ vm_name }}_" 8 | force: true 9 | user_pubfile_data: "{{ lookup('file', user_pubfile) }}" 10 | networkInfo: bridge:br0,model=virtio 11 | 12 | - name: Local Config Files 13 | set_fact: 14 | config_files_local: 15 | - { 16 | src: ../templates/environment.j2, 17 | dest: "{{ tmp_prefix }}environment", 18 | } 19 | - { src: ../templates/interfaces.j2, dest: "{{ tmp_prefix }}interfaces" } 20 | - { src: ../templates/apt_proxy.j2, dest: "{{ tmp_prefix }}apt_proxy" } 21 | - { src: ../templates/proxy.sh.j2, dest: "{{ tmp_prefix }}proxy.sh" } 22 | - { 23 | src: ../templates/netplan.yml.j2, 24 | dest: "{{ tmp_prefix }}netplan.yml", 25 | } 26 | 27 | - name: Build local configuration files 28 | template: 29 | src: "{{ item.src }}" 30 | dest: "{{ item.dest }}" 31 | delegate_to: localhost 32 | with_items: "{{ config_files_local }}" 33 | 34 | - name: Read local config files into variables 35 | set_fact: 36 | environment_data: "{{ lookup('file', '{{ tmp_prefix }}environment') }}" 37 | interfaces_data: "{{ lookup('file', '{{ tmp_prefix }}interfaces') }}" 38 | apt_proxy_data: "{{ lookup('file', '{{ tmp_prefix }}apt_proxy') }}" 39 | proxy_sh_data: "{{ lookup('file', '{{ tmp_prefix }}proxy.sh') }}" 40 | netplan_yml_data: "{{ lookup('file', '{{ tmp_prefix }}netplan.yml') }}" 41 | 42 | - name: Build the first boot script 43 | set_fact: 44 | config_files: 45 | - { src: ../templates/first_boot.sh.j2, dest: /tmp/first_boot.sh } 46 | 47 | - name: Save configuration files 48 | template: 49 | src: "{{ item.src }}" 50 | dest: "{{ item.dest }}" 51 | with_items: "{{ config_files }}" 52 | 53 | - name: Ensure base image path directory exists 54 | file: 55 | path: "{{ base_image_path }}" 56 | state: directory 57 | mode: "0770" 58 | owner: "{{ libvirt_user }}" 59 | group: "{{ libvirt_group }}" 60 | become: true 61 | 62 | - name: Additional Network Info 63 | set_fact: 64 | networkInfo: "{{ networkInfo }},mac={{ vm_cfg.network.mac }}" 65 | when: vm_cfg.network is defined and vm_cfg.network.mac is defined and vm_cfg.network.mac | length > 0 66 | 67 | - name: Destroy the image 68 | virt: 69 | name: "{{ vm_name }}" 70 | state: destroyed 71 | become: true 72 | ignore_errors: true 73 | when: force 74 | 75 | - name: Undefine the image 76 | virt: 77 | name: "{{ vm_name }}" 78 | command: undefine 79 | become: true 80 | ignore_errors: true 81 | when: force 82 | 83 | - name: Delete the image file 84 | file: 85 | path: "{{ img_file }}" 86 | state: absent 87 | when: force 88 | 89 | - name: Builder Args 90 | set_fact: 91 | builder_args: 92 | - "{{ version_cfg.version }}" 93 | - --output {{ img_file }} 94 | - --size {{ vm_cfg.storage.primary.size }} 95 | - --format qcow2 96 | - --hostname {{ hostname }} 97 | - --timezone {{ timezone }} 98 | # - --install rsync 99 | - --firstboot-command 'dpkg-reconfigure openssh-server' 100 | - --firstboot /tmp/first_boot.sh 101 | 102 | - name: Installer Args 103 | set_fact: 104 | installer_args: 105 | - --connect qemu:///system 106 | - --os-variant {{ version_cfg.variant }} 107 | - --import 108 | - --name {{ vm_name }} 109 | - --memory {{ vm_cfg.memory.size }} 110 | - --disk path={{ img_file }},format=qcow2 111 | - --accelerate 112 | - --vcpus {{ vm_cfg.vcpus }} 113 | - --hvm 114 | - --graphics vnc,port={{ vm_cfg.graphics.vnc.port }} 115 | - --noautoconsole 116 | - --network {{ networkInfo }} 117 | - --autostart 118 | 119 | - name: Test to see if {{ img_file }} exists 120 | stat: 121 | path: "{{ img_file }}" 122 | get_attributes: false 123 | get_checksum: false 124 | register: stat_result 125 | become: true 126 | 127 | - name: Create the image file using virt-builder 128 | command: virt-builder {{ builder_args | join(' ') }} 129 | become: true 130 | when: not stat_result.stat.exists 131 | 132 | - name: Set Permissions 133 | become: true 134 | file: 135 | path: "{{ img_file }}" 136 | state: touch 137 | mode: 0770 138 | owner: "{{ libvirt_user }}" 139 | group: "{{ libvirt_group }}" 140 | 141 | - name: Create the VM 142 | command: virt-install {{ installer_args | join(' ') }} 143 | when: true 144 | -------------------------------------------------------------------------------- /tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Packages 3 | apt: 4 | name: 5 | - qemu-kvm 6 | - qemu 7 | - virt-manager 8 | - virt-viewer 9 | - libvirt-bin 10 | - uuid 11 | - bridge-utils 12 | - libosinfo-bin 13 | - ubuntu-vm-builder 14 | - libguestfs-tools 15 | state: present 16 | become: true 17 | 18 | - name: Set the groups needed 19 | set_fact: 20 | virt_groups: 21 | - libvirtd 22 | - netdev 23 | - "{{ libvirt_group }}" 24 | 25 | - name: Create the target user 26 | user: 27 | name: "{{ target_user }}" 28 | shell: /bin/bash 29 | groups: "{{ virt_groups }}" 30 | append: yes 31 | when: target_user is defined 32 | become: true 33 | 34 | - name: Create the remote user 35 | user: 36 | name: "{{ remote_user }}" 37 | shell: /bin/bash 38 | groups: "{{ virt_groups }}" 39 | append: yes 40 | when: remote_user is defined 41 | become: true 42 | 43 | - name: Create the libvirt user 44 | user: 45 | name: "{{ libvirt_user }}" 46 | shell: /bin/bash 47 | groups: "{{ libvirt_group }}" 48 | append: yes 49 | when: libvirt_user is defined 50 | become: true 51 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Required Items 3 | fail: 4 | msg: "Please ensure '{{ item.use }}' is set" 5 | when: 6 | - not item.val is defined or item.val | length == 0 7 | with_items: 8 | - { val: vm_name, use: "VM Name" } 9 | - { val: vm_cfg, use: "VM Configuration" } 10 | - { val: vm_cfg.storage, use: "VM Storage" } 11 | - { val: vm_cfg.graphics, use: "VM Graphics" } 12 | - { val: vm_cfg.vcpus, use: "VM Virtual CPUs" } 13 | - { val: version, use: "OS Version" } 14 | 15 | - include_tasks: install.yml 16 | when: ((['install'] | intersect(flags)) | length > 0) 17 | tags: 18 | - install 19 | 20 | - include_tasks: create.yml 21 | when: ((['create'] | intersect(flags)) | length > 0) 22 | tags: 23 | - create 24 | 25 | - include_tasks: rename.yml 26 | when: ((['rename'] | intersect(flags)) | length > 0) 27 | tags: 28 | - rename 29 | 30 | - include_tasks: secondary_storage.yml 31 | when: ((['secondary_storage'] | intersect(flags)) | length > 0) 32 | tags: 33 | - storage 34 | - create 35 | -------------------------------------------------------------------------------- /tasks/rename.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Required Items 3 | fail: 4 | msg: "Please ensure '{{ item.use }}' is set" 5 | when: 6 | - not {{ item.var }} is defined or {{ item.var }} | length == 0 7 | with_items: 8 | - { var: vm_old, use: "Old VM" } 9 | - { var: vm_name, use: "New VM" } 10 | 11 | - name: Check for enabled 12 | command: virsh list --all | grep "{{ vm_old }}" 13 | ignore_errors: true 14 | register: list_result 15 | when: true 16 | 17 | - name: Test if the VM exists 18 | set_fact: 19 | vm_exists: true 20 | when: list_result.stdout | search(vm_old) 21 | 22 | - name: Fail when vm doesn't exist 23 | fail: 24 | msg: "{{ vm_old }} Not Found" 25 | when: not vm_exists 26 | 27 | - name: Test if the VM is started 28 | set_fact: 29 | vm_started: true 30 | when: list_result.stdout | search("running") 31 | 32 | - name: Ensure old is shut down 33 | command: virsh shutdown {{ vm_old }} 34 | when: vm_started 35 | 36 | - name: dump xml 37 | command: virsh dumpxml {{ vm_old }} > /tmp/{{ vm_name }}.xml creates="/tmp/{{ vm_name }}.xml" 38 | 39 | - name: replace 40 | when: true 41 | lineinfile: 42 | dest: "/tmp/{{ vm_name }}.xml" 43 | state: present 44 | regexp: "{{ vm_old }}" 45 | line: "{{ vm_name }}" 46 | backup: true 47 | 48 | - name: old path result 49 | command: virsh dumpxml {{ vm_old }} | grep 'source file' | cut -d\' -f2 50 | register: old_file_result 51 | when: true 52 | 53 | - name: new path result 54 | command: grep 'source file' /tmp/{{ vm_name }}.xml | cut -d\' -f2 55 | register: new_file_result 56 | when: true 57 | 58 | - name: File Definitions 59 | set_fact: 60 | old_file: "{{ old_file_result.stdout }}" 61 | new_file: "{{ new_file_result.stdout }}" 62 | 63 | - name: stat old_file 64 | stat: 65 | path: "{{ old_file }}" 66 | get_checksum: false 67 | get_md5: false 68 | register: old_stat 69 | 70 | - name: Whether to rename the folder 71 | set_fact: 72 | do_folder_rename: true 73 | when: old_stat.stat.exists 74 | 75 | - name: Rename old folder 76 | command: dirname "{{ old_file }}" 77 | register: old_folder_result 78 | when: do_folder_rename 79 | 80 | - name: Old Folder Name 81 | set_fact: 82 | old_folder: "{{ old_folder_result.stdout }}" 83 | when: do_folder_rename 84 | 85 | - name: Rename images folder 86 | command: dirname "{{ old_folder }}" 87 | register: images_folder_result 88 | when: do_folder_rename 89 | 90 | - name: Old Folder Name 91 | set_fact: 92 | images_folder: "{{ images_folder_result.stdout }}" 93 | when: do_folder_rename 94 | 95 | - name: Folder Names 96 | set_fact: 97 | old_folder: "{{ images_folder }}/{{ vm_old }}" 98 | new_folder: "{{ images_folder }}/{{ vm_name }}" 99 | when: do_folder_rename 100 | 101 | - name: Move old to new 102 | command: mv "{{ old_folder }}" "{{ new_folder }}" 103 | when: do_folder_rename 104 | become: true 105 | 106 | - name: Undefine "{{ vm_old }}" 107 | command: virsh undefine "{{ vm_old }}" 108 | when: do_change 109 | 110 | - name: Define "{{ vm_name }}" 111 | command: virsh define "/tmp/{{ vm_name }}.xml" 112 | when: do_change 113 | 114 | - name: Start "{{ vm_name }}" 115 | command: virsh start "{{ vm_name }}" 116 | when: do_change 117 | -------------------------------------------------------------------------------- /tasks/secondary_storage.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Default variables 3 | set_fact: 4 | force: false 5 | img_file: "{{ base_image_path }}/{{ vm_cfg.storage.secondary.name }}.qcow2" 6 | 7 | - name: Delete the image file 8 | file: 9 | path: "{{ img_file }}" 10 | state: absent 11 | when: force 12 | become: true 13 | 14 | - name: stat {{ img_file }} 15 | stat: 16 | path: "{{ img_file }}" 17 | get_checksum: false 18 | get_md5: false 19 | register: old_stat 20 | 21 | - name: Create Storage 22 | command: qemu-img create -f qcow2 -o preallocation=metadata {{ img_file }} {{ vm_cfg.storage.secondary.size }} 23 | when: not old_stat.stat.exists 24 | become: true 25 | 26 | - name: Set Permissions 27 | become: true 28 | file: 29 | path: "{{ img_file }}" 30 | state: touch 31 | mode: 0770 32 | owner: "{{ libvirt_user }}" 33 | group: "{{ libvirt_group }}" 34 | 35 | - name: Params 36 | set_fact: 37 | targetParam: "--target {{ vm_cfg.storage.secondary.name }}" 38 | sourceParam: "--source {{ img_file }}" 39 | domainParam: "--domain {{ vm_name }}" 40 | otherParams: "--driver qemu --subdriver qcow2 --targetbus virtio --config --live" 41 | 42 | - name: Attaching Storage to Client 43 | command: virsh attach-disk {{ domainParam }} {{ sourceParam }} {{ targetParam }} {{ otherParams }} 44 | become: true 45 | when: true 46 | 47 | - name: Get the xml for the new configuration 48 | virt: 49 | name: "{{ vm_name }}" 50 | command: get_xml 51 | -------------------------------------------------------------------------------- /templates/apt_proxy.j2: -------------------------------------------------------------------------------- 1 | Acquire::http::proxy "http://{{ proxy.addr }}:{{ proxy.port }}/"; 2 | Acquire::ftp::proxy "ftp://{{ proxy.addr }}:{{ proxy.port }}/"; 3 | Acquire::https::proxy "https://{{ proxy.addr }}:{{ proxy.port }}/"; 4 | -------------------------------------------------------------------------------- /templates/environment.j2: -------------------------------------------------------------------------------- 1 | http_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 2 | https_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 3 | ftp_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 4 | no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com" 5 | HTTP_PROXY=http://{{ proxy.addr }}:{{ proxy.port }}/ 6 | HTTPS_PROXY=http://{{ proxy.addr }}:{{ proxy.port }}/ 7 | FTP_PROXY=http://{{ proxy.addr }}:{{ proxy.port }}/ 8 | NO_PROXY="localhost,127.0.0.1,localaddress,.localdomain.com" 9 | -------------------------------------------------------------------------------- /templates/first_boot.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script will run the first time the virtual machine boots 3 | # It is ran as root. 4 | 5 | 6 | echo 'Add Default User and Groups' 7 | groupadd {{ generic_group }} 8 | useradd -m -p '' --comment '{{ generic_user.description }}' --shell=/bin/bash --groups=sudo,adm,{{ generic_group }} {{ generic_user.username }} 9 | mkdir -p /home/{{ generic_user.username }}/.ssh 10 | echo "{{ user_pubfile_data }}" > /home/{{ generic_user.username }}/.ssh/authorized_keys 11 | chown {{ generic_user.username }}.{{ generic_user.username }} -R /home/{{ generic_user.username }}/.ssh 12 | chmod 0400 /home/{{ generic_user.username }}/.ssh/authorized_keys 13 | 14 | systemctl enable ssh 15 | echo 'PermitRootLogin no' >> /etc/ssh/sshd_config 16 | systemctl restart ssh 17 | 18 | echo "updating sudoers" 19 | sed -i.bak_init 's/ALL=(ALL:ALL) ALL/ALL=(ALL:ALL) NOPASSWD: ALL/' /etc/sudoers 20 | 21 | sed -i.bak_init 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config 22 | sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config 23 | 24 | systemctl restart sshd 25 | 26 | {% if vm_cfg.network.do_proxy is defined and vm_cfg.network.do_proxy %} 27 | echo 'Setting up proxy environment' 28 | 29 | echo 'Configure apt proxy' 30 | echo '{{ apt_proxy_data }}' > /etc/apt/apt.conf.d/80proxy 31 | chown root.root /etc/apt/apt.conf.d/80proxy 32 | chmod 644 /etc/apt/apt.conf.d/80proxy 33 | echo 'updated /etc/apt/apt.conf.d/80proxy' 34 | 35 | echo 'Configure global environment file' 36 | echo '{{ environment_data }}' >> /etc/environment 37 | echo 'updated /etc/environment' 38 | 39 | echo 'Configure profile proxy' 40 | echo "{{ proxy_sh_data }}" > /etc/profile.d/proxy.sh 41 | chown root.root /etc/profile.d/proxy.sh 42 | chmod 644 /etc/profile.d/proxy.sh 43 | echo 'updated /etc/profile.d/proxy.sh' 44 | {% endif %} 45 | 46 | {% if version_cfg.variant == 'ubuntu16.04' or version_cfg.variant == 'ubuntu14.04' %} 47 | echo 'Update ifconfig network interfaces' 48 | echo "{{ interfaces_data }}" > /etc/network/interfaces 49 | chown root.root /etc/network/interfaces 50 | chmod 644 /etc/network/interfaces 51 | echo 'updated /etc/network/interfaces' 52 | {% endif %} 53 | 54 | {% if version_cfg.variant == 'ubuntu18.04' %} 55 | echo 'Update netplan' 56 | echo "{{ netplan_yml_data }}" > /etc/netplan/01-netcfg.yaml 57 | chown root.root /etc/netplan/01-netcfg.yaml 58 | chmod 644 /etc/netplan/01-netcfg.yaml 59 | echo 'updated /etc/netplan/01-netcfg.yaml' 60 | 61 | echo 'Applying netplan' 62 | netplan apply 63 | 64 | echo 'Bringing up the ip link' 65 | ip link set {{ iface_name }} up 66 | 67 | ip a s 68 | systemctl restart networkd-dispatcher 69 | # dhclient & 70 | #sleep 5 # to ensure IP is stable 71 | 72 | echo 'Waiting for {{ iface_name }} to be available ...' 73 | 74 | for i in {1..60} 75 | do 76 | output=$(ip route | grep -oP "default via .+ dev {{ iface_name }}"); 77 | if [ -n "$output" ]; then break; fi 78 | sleep 2; 79 | done 80 | 81 | {% endif %} 82 | 83 | echo 'Install additional software packages' 84 | apt update 85 | apt install -y python 86 | -------------------------------------------------------------------------------- /templates/interfaces.j2: -------------------------------------------------------------------------------- 1 | # The loopback network interface 2 | auto lo 3 | iface lo inet loopback 4 | 5 | # The primary network interface 6 | {% if vm_cfg.network is defined and vm_cfg.network.dhcp is defined and vm_cfg.network.dhcp %} 7 | auto {{ iface_name }} 8 | iface {{ iface_name }} inet dhcp 9 | {% else %} 10 | auto {{ iface_name }} 11 | iface {{ iface_name }} inet static 12 | address {{ vm_cfg.network.ip }} 13 | netmask {{ vm_cfg.network.netmask }} 14 | gateway {{ vm_cfg.network.gateway }} 15 | {% endif %} 16 | -------------------------------------------------------------------------------- /templates/netplan.yml.j2: -------------------------------------------------------------------------------- 1 | network: 2 | version: 2 3 | ethernets: 4 | {{ iface_name }}: 5 | {% if vm_cfg.network is defined and vm_cfg.network.dhcp is defined and vm_cfg.network.dhcp %} 6 | dhcp4: true 7 | {% else %} 8 | dhcp4: false 9 | addresses: [ "{{ vm_cfg.network.ip }}"/"{{ vm_cfg.network.mask }}" ] 10 | {% if vm_cfg.network is defined and vm_cfg.network.gateway is defined and vm_cfg.network.gateway | length > 0 %} 11 | gateway4: {{ vm_cfg.network.gateway }} 12 | {% endif %} 13 | {% if dns_servers is defined %} 14 | nameservers: 15 | addresses: 16 | {% for s in dns_servers %} 17 | {% if s.network is defined %} 18 | - {{ s.network.ip }} # {{ s.description }} 19 | {% endif %} 20 | {% endfor %} 21 | {% endif %} 22 | 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /templates/network_interfaces.j2: -------------------------------------------------------------------------------- 1 | # This file describes the network interfaces available on your system 2 | # and how to activate them. For more information, see interfaces(5). 3 | 4 | source /etc/network/interfaces.d/* 5 | 6 | # The loopback network interface 7 | auto lo 8 | iface lo inet loopback 9 | 10 | # The primary network interface 11 | auto br0 12 | iface br0 inet dhcp 13 | bridge_ports eno1 eno2 eno3 eno4 14 | bridge_stp on 15 | bridge_fd 0 16 | bridge_maxwait 5 -------------------------------------------------------------------------------- /templates/proxy.sh.j2: -------------------------------------------------------------------------------- 1 | export http_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 2 | export https_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 3 | export npm_config_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 4 | export npm_config_https_proxy=http://{{ proxy.addr }}:{{ proxy.port }}/ 5 | -------------------------------------------------------------------------------- /templates/user.pub.j2: -------------------------------------------------------------------------------- 1 | {{ user_pubfile_data }} -------------------------------------------------------------------------------- /templates/virt.conf.j2: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | tmpfs = - 3 | 4 | user = {{ generic_user.username }} 5 | name = {{ generic_user.description }} 6 | pass = {{ default_pwd }} 7 | ssh-user-key = {{ ssh_user_key }} 8 | 9 | [ubuntu] 10 | suite = xenial 11 | flavour = virtual 12 | components = main, universe, restricted 13 | mirror = http://archive.ubuntu.com/ubuntu 14 | security-mirror = http://security.ubuntu.com/ubuntu 15 | 16 | ; Need linux-image-generic for workaround for /proc/cpuinfo 17 | ; https://bugs.launchpad.net/ubuntu/+source/vm-builder/+bug/1037607 18 | addpkg = unattended-upgrades, openssh-server, acpid, linux-image-generic, bash-completion, man, vim, nano, htop, tmux, build-essential 19 | 20 | [kvm] 21 | libvirt = qemu:///system 22 | arch = amd64 23 | cpus = {{ vm_cfg.vcpus }} 24 | 25 | ; br0 is the system bridge manually created. virbr0 is the virtual network bridge created via virt-manager 26 | #bridge = br0 27 | virtio_net = true 28 | timezone = {{ timezone }} 29 | 30 | # -memory 512 31 | # -smp cpus=1 32 | # -enable-kvm -------------------------------------------------------------------------------- /templates/vmbuilder.partitions.j2: -------------------------------------------------------------------------------- 1 | root 10240 2 | swap 2048 3 | -------------------------------------------------------------------------------- /vars/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | vm_exists: false 4 | vm_started: false 5 | 6 | do_folder_rename: false 7 | do_change: true 8 | 9 | libvirt_user: libvirt-qemu 10 | libvirt_group: kvm 11 | 12 | iso_path: "/var/lib/libvirt/boot/" 13 | configs_path: "/var/lib/libvirt/configs/" 14 | part_file: "/var/lib/libvirt/configs/vmbuilder.partitions" 15 | 16 | imageFileNames: 17 | 18.04.1: "ubuntu-18.04.1-live-server-amd64.iso" 18 | 16.04.5: "ubuntu-16.04.5-server-amd64.iso" 19 | 14.04.5: "ubuntu-14.04.5-server-amd64.iso" 20 | 21 | versionConfigs: 22 | 18.04.1: 23 | isoName: ubuntu-18.04.1-live-server-amd64.iso 24 | url: http://releases.ubuntu.com/18.04.1/ubuntu-18.04.1-live-server-amd64.iso 25 | version: ubuntu-18.04 26 | variant: ubuntu18.04 27 | 16.04.5: 28 | isoName: "ubuntu-16.04.5-server-amd64.iso" 29 | url: http://releases.ubuntu.com/16.04.5/ubuntu-16.04.5-server-amd64.iso 30 | version: ubuntu-16.04 31 | variant: ubuntu16.04 32 | 14.04.5: 33 | isoName: "ubuntu-14.04.5-server-amd64.iso" 34 | url: http://releases.ubuntu.com/14.04.5/ubuntu-14.04.5-server-amd64.iso 35 | version: ubuntu-14.04 36 | variant: ubuntu14.04 37 | -------------------------------------------------------------------------------- /yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Based on ansible-lint config: https://github.com/ansible/galaxy/blob/devel/galaxy/importer/linters/yamllint.yaml 3 | extends: default 4 | 5 | rules: 6 | braces: { max-spaces-inside: 1, level: error } 7 | brackets: { max-spaces-inside: 1, level: error } 8 | colons: { max-spaces-after: -1, level: error } 9 | commas: { max-spaces-after: -1, level: error } 10 | comments: disable 11 | comments-indentation: disable 12 | document-start: disable 13 | empty-lines: { max: 3, level: error } 14 | hyphens: { level: error } 15 | indentation: disable 16 | key-duplicates: enable 17 | line-length: disable 18 | new-line-at-end-of-file: disable 19 | new-lines: { type: unix } 20 | trailing-spaces: disable 21 | truthy: disable 22 | --------------------------------------------------------------------------------