├── tests ├── roles │ └── pxeserver ├── Vagrantfile └── test.yml ├── handlers └── main.yml ├── templates ├── etc_exports ├── etc_httpd_conf.d_pxeboot.conf ├── etc_dhcp_dhcpd-pxelinux.conf.j2 └── pxelinux.cfg_default ├── defaults └── main.yml ├── .gitignore ├── meta └── main.yml ├── vars └── CentOS.yml ├── CHANGELOG.md ├── LICENSE.md ├── tasks └── main.yml └── README.md /tests/roles/pxeserver: -------------------------------------------------------------------------------- 1 | ../.. -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | # roles/pxeserver/handlers/main.yml 2 | --- 3 | 4 | - name: restart nfs 5 | service: 6 | name: nfs 7 | state: restarted 8 | -------------------------------------------------------------------------------- /templates/etc_exports: -------------------------------------------------------------------------------- 1 | # NFS shares 2 | # 3 | # {{ ansible_managed }} 4 | {% for image in pxeserver_images %} 5 | {{ pxeserver_path }}/{{ image.name }}/rootfs *(rw,sync,no_root_squash) 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | # roles/pxeserver/defaults/main.yml 2 | --- 3 | 4 | pxeserver_directory: "pxelinux" 5 | pxeserver_path: "{{ tftp_root_directory }}/{{ pxeserver_directory }}" 6 | pxeserver_ip: "{{ ansible_default_ipv4.address }}" 7 | pxeserver_images: [] 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore 2 | 3 | # Hidden Vagrant-directory 4 | .vagrant 5 | 6 | # Backup files (e.g. Vim, Gedit, etc.) 7 | *~ 8 | 9 | # Vagrant base boxes (you never know when someone puts one in the repository) 10 | *.box 11 | 12 | # Ignore role dependencies (installed from Ansible Galaxy) 13 | tests/roles/*.* 14 | -------------------------------------------------------------------------------- /templates/etc_httpd_conf.d_pxeboot.conf: -------------------------------------------------------------------------------- 1 | # PXE server configuration 2 | # {{ ansible_managed }} 3 | {% for image in pxeserver_images %} 4 | 5 | Alias /{{ image.name }} {{ tftp_root_directory }}/{{ image.name }} 6 | 7 | 8 | Options Indexes FollowSymLinks 9 | Require all granted 10 | 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Bert Van Vreckem 4 | description: Role for setting up a PXE server on RHEL/CentOS 7 5 | company: 6 | license: BSD 7 | min_ansible_version: 2.0 8 | platforms: 9 | - name: EL 10 | versions: 11 | - 7 12 | galaxy_tags: 13 | - system 14 | - networking 15 | dependencies: 16 | - bertvv.tftp 17 | - bertvv.dhcp 18 | -------------------------------------------------------------------------------- /vars/CentOS.yml: -------------------------------------------------------------------------------- 1 | # roles/pxeserver/vars/main.yml 2 | --- 3 | 4 | pxeserver_packages: 5 | - nfs-utils 6 | - rsync 7 | - shim 8 | - syslinux 9 | 10 | pxeserver_services: 11 | - rpcbind 12 | - nfs-server 13 | 14 | pxeserver_files: 15 | - /boot/efi/EFI/BOOT/BOOTX64.EFI 16 | - /usr/share/syslinux/memdisk 17 | - /usr/share/syslinux/menu.c32 18 | - /usr/share/syslinux/pxelinux.0 19 | -------------------------------------------------------------------------------- /templates/etc_dhcp_dhcpd-pxelinux.conf.j2: -------------------------------------------------------------------------------- 1 | option arch code 93 = unsigned integer 16; # RFC4578 2 | 3 | class "pxeclients" { 4 | match if substring (option vendor-class-identifier, 0, 9) = "PXEClient"; 5 | next-server {{ pxeserver_ip }}; 6 | 7 | if option arch = 00:07 { 8 | filename "pxelinux/bootx64.efi"; 9 | # } else if option arch = 00:06 { 10 | # filename "pxelinux/bootia32.efi"; 11 | } else { 12 | filename "pxelinux/pxelinux.0"; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | This file contains al notable changes to the pxeserver Ansible role. 4 | 5 | This file adheres to the guidelines of [http://keepachangelog.com/](http://keepachangelog.com/). Versioning follows [Semantic Versioning](http://semver.org/). 6 | 7 | ## 1.0.0 - 2016-08-24 8 | 9 | First release! 10 | 11 | ### Added 12 | 13 | - Install the necessary packages, files and services 14 | - Allow installation of boot images 15 | - Provide a PXE boot menu 16 | 17 | -------------------------------------------------------------------------------- /tests/Vagrantfile: -------------------------------------------------------------------------------- 1 | # vi: set ft=ruby 2 | 3 | require 'rbconfig' 4 | 5 | ROLE_NAME = 'pxeserver' 6 | HOST_NAME = 'test' + ROLE_NAME 7 | VAGRANTFILE_API_VERSION = '2' 8 | 9 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 10 | config.vm.box = 'bento/centos-7.5' 11 | 12 | config.vm.define HOST_NAME do |node| 13 | node.vm.hostname = HOST_NAME 14 | node.vm.network 'private_network', ip: '192.168.50.2' 15 | node.vm.provision 'ansible' do |ansible| 16 | ansible.playbook = 'test.yml' 17 | end 18 | end 19 | end 20 | 21 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: true 4 | vars: 5 | dhcp_subnets: 6 | - ip: 192.168.50.0 7 | netmask: 255.255.255.0 8 | pxeserver_ip: 192.168.50.2 9 | pxeserver_images: 10 | - name: centos7_x86_64 11 | default: true 12 | kernel_url: http://ftp.belnet.be/ftp.centos.org/7/os/x86_64/isolinux/vmlinuz 13 | initrd_url: http://ftp.belnet.be/ftp.centos.org/7/os/x86_64/isolinux/initrd.img 14 | label: CentOS 7 x86_64 15 | 16 | roles: 17 | - bertvv.tftp 18 | - bertvv.dhcp 19 | - pxeserver 20 | -------------------------------------------------------------------------------- /templates/pxelinux.cfg_default: -------------------------------------------------------------------------------- 1 | DEFAULT menu.c32 2 | PROMPT 0 3 | TIMEOUT 50 4 | 5 | MENU title ########## PXE Boot Menu ########## 6 | {% for image in pxeserver_images %} 7 | LABEL {{ loop.index }} 8 | {% if image.default is defined and image.default == true %} 9 | MENU DEFAULT 10 | {% endif %} 11 | MENU LABEL ^{{ loop.index }}) {{ image.label }} 12 | KERNEL memdisk 13 | APPEND iso initrd={{ image.name }}/{{ image.iso }} raw 14 | {% endfor %} 15 | 16 | LABEL {{ pxeserver_images|length + 1 }} 17 | MENU LABEL ^{{ pxeserver_images|length + 1 }}) Boot from local drive 18 | LOCALBOOT 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # BSD License 2 | 3 | Copyright (c) 2014, Bert Van Vreckem, (bert.vanvreckem@gmail.com) 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | # roles/pxeserver/tasks/main.yml 2 | --- 3 | 4 | - include_vars: "{{ item }}" 5 | with_first_found: 6 | - "{{ ansible_distribution }}.yml" 7 | - "{{ ansible_os_family }}.yml" 8 | tags: pxeserver 9 | 10 | - name: Install packages 11 | package: 12 | name: "{{ item }}" 13 | state: installed 14 | with_items: "{{ pxeserver_packages }}" 15 | tags: pxeserver 16 | 17 | - name: Ensure services are started 18 | service: 19 | name: "{{ item }}" 20 | state: started 21 | enabled: true 22 | with_items: "{{ pxeserver_services }}" 23 | tags: pxeserver 24 | 25 | - name: Create directory for PXEboot related files 26 | file: 27 | path: "{{ pxeserver_path }}" 28 | state: directory 29 | tags: pxeserver 30 | 31 | - name: Make Network Boot Programs for BIOS and UEFI based systems available 32 | command: cp "{{ item }}" "{{ pxeserver_path }}/{{ item|regex_replace('.*/', '') }}" 33 | args: 34 | creates: "{{ pxeserver_path }}/{{ item|regex_replace('.*/', '') }}" 35 | with_items: "{{ pxeserver_files }}" 36 | tags: pxeserver 37 | 38 | - name: Create directory for PXE server configuration 39 | file: 40 | path: "{{ pxeserver_path }}/pxelinux.cfg" 41 | state: directory 42 | tags: pxeserver 43 | 44 | - name: Create directories for boot images 45 | file: 46 | path: "{{ pxeserver_path }}/{{ item.name }}" 47 | state: directory 48 | with_items: "{{ pxeserver_images }}" 49 | tags: pxeserver 50 | 51 | - name: Download ISO 52 | get_url: 53 | url: "{{ item.iso_url }}{{ item.iso }}" 54 | dest: "{{ pxeserver_path }}/{{ item.name }}/{{ item.iso }}" 55 | with_items: "{{ pxeserver_images }}" 56 | tags: pxeserver 57 | 58 | - name: Create default menu for BIOS based systems 59 | template: 60 | src: pxelinux.cfg_default 61 | dest: "{{ pxeserver_path }}/pxelinux.cfg/default" 62 | tags: pxeserver 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible role `pxeserver` 2 | 3 | An Ansible role for setting up a PXE boot server. 4 | 5 | - Install necessary packages 6 | - Configure and start necessary services (TFTP, DHCP, NFS) 7 | - Make installation images available to clients 8 | 9 | This role will download the kernel(s) and initrd(s) that you want to boot, but the filesystem (that will be shared over NFS) is not set up automatically. 10 | 11 | ## Requirements 12 | 13 | No specific requirements 14 | 15 | ## Role Variables 16 | 17 | *TODO* 18 | 19 | | Variable | Default | Comment | 20 | | :--- | :--- | :--- | 21 | | `pxeserver_directory` | `pxelinux` | Subdirectory under TFTP root for PXE specific files | 22 | | `pxeserver_images` | [] | List of dicts specifying PXEboot images to be served. See below. | 23 | | `pxeserver_ip` | `ansible_default_ipv4.address` | IP address of the PXE server | 24 | 25 | You can specify the boot images to be served with the variable `pxeserver_images`, a dict containing the keys listed below. Keys are *mandatory* unless specified. 26 | 27 | | Key | Value | 28 | | :--- | :--- | 29 | | `name` | A unique identifier for the image | 30 | | `default` | When `true`, this image is chosen as the default. May be omitted. | 31 | | `kernel_url` | URL where to download the kernel image. | 32 | | `initrd_url` | URL where to download the initrd image. | 33 | | `kickstart_url` | URL where to download the kickstart file. May be omitted | 34 | | `kickstart_path` | Location where to copy the kickstart file from. May be omitted | 35 | | `label` | Label for the PXE boot menu entry of this image. | 36 | 37 | ## Dependencies 38 | 39 | This role depends on: 40 | 41 | - [bertvv.tftp](https://galaxy.ansible.com/list#/roles/3597) 42 | 43 | ## Example Playbook 44 | 45 | See the [test playbook](tests/test.yml) 46 | 47 | ## Testing 48 | 49 | The `tests` directory contains tests for this role in the form of a Vagrant environment. You should install the dependent roles 50 | 51 | ```ShellSession 52 | $ cd tests/ 53 | $ ansible-galaxy install -p roles bertvv.tftp 54 | $ vagrant up 55 | ``` 56 | 57 | The last command creates a new CentOS 7 VM and applies the playbook [`test.yml`](tests/test.yml). This should result in a working PXE server. 58 | 59 | ## Contributing 60 | 61 | Issues, feature requests, ideas are appreciated and can be posted in the Issues section. Pull requests are also very welcome. Preferably, create a topic branch and when submitting, squash your commits into one (with a descriptive message). 62 | 63 | ## License 64 | 65 | BSD 66 | 67 | ## Further reading 68 | 69 | - J. East, and D. Domingo (eds.), 2015. [Setting up a remote diskless system](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Storage_Administration_Guide/ch-disklesssystems.html). In *Red Hat 7 Storage Administration Guide* 70 | - Petr Bokoč, et. al, 2015. [Preparing for a network installation](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Installation_Guide/chap-installation-server-setup.html). In *Red Hat 7 Installation Guide. 71 | 72 | ## Author Information 73 | 74 | Bert Van Vreckem (bert.vanvreckem@gmail.com) 75 | 76 | --------------------------------------------------------------------------------