Skip to content

Commit

Permalink
Merge pull request #1 from mila-iqia/feat/container_role
Browse files Browse the repository at this point in the history
feat: add new role to manage LXC containers
  • Loading branch information
btravouillon authored Feb 24, 2022
2 parents 3a7b892 + 1985aec commit 761973b
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 0 deletions.
152 changes: 152 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Ansible Collection - mila.proxmox

The purpose of this collection is to manage containers and virtual machines in
the [Proxmox VE](https://www.proxmox.com/en/proxmox-ve) virtualization
management platform.

NOTE: The collection is still in its early stages of development, things might
change quickly and without notice while version is still 0.x.y.

## Requirements

The below requirements are needed on the host that executes this collection:

- collection `community.general`
- jmespath
- proxmoxer
- requests

## Roles

### container

To manage containers, use the role `mila.proxmox.container`. This role makes use
of the Ansible modules `community.general.proxmox_template` and
`community.general.proxmox`.

The containers are created from a list defined in `proxmox_container`. Each item
in the list defines one container.

Example for a container:

proxmox_container:
- hostname: 'container1'
node: 'pve-1'
ostemplate: 'local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz'
disk: 'local:2'
cores: 2
nameserver: '10.10.0.1'
netif: '{"net0":"name=eth0,gw=10.10.0.254,hwaddr=42:4d:69:6c:61:00,ip=dhcp,bridge=vmbr0"}'
unprivileged: True
features:
- nesting=1 # For systemd, otherwise takes a lot of time to open ssh session

The role will parse `ostemplate` to ensure the required template is available on
all hosts.

Most of the parameters supported by the module `community.general.proxmox` can
be defined in the list. The following parameters are not supported yet, but
this might change in future development: `api_password`, `ip_address`,
`mounts`, `password`, `pool`, `purge`, `searchdomain` and `storage`. Note that
`proxmox_default_behavior` is enforced to `no_defaults`.

To authenticate to the REST API of your Proxmox VE cluster, you first need to
create an [API token][pve_api_tokens], then you must define the 4 **mandatory**
variables below:

- `proxmox_api_host`: the hostname or IP address of the Proxmox VE API server
- `proxmox_api_user`: the user name to authenticate (e.g. `ansible@pam`)
- `proxmox_api_token_id`: the token name (e.g. `prod`)
- `proxmox_api_token_secret`: the token secret provided by ProxmoxVE

[pve_api_tokens]: https://pve.proxmox.com/pve-docs/chapter-pveum.html#pveum_tokens

It is also possible to define the user, token id or token secret on a per
container basis for use cases where the management of a container requires
different access privileges.

proxmox_container:
- hostname: 'container2'
node: 'pve-1'
api_user: 'root@pam'
api_token_secret: 'MySecretShouldBeInAVault ;)'
features:
- keyctl=1 # This feature needs root access in ProxmoxVE
[...]

In the example above, `item.api_user` and `item.api_token_secret` will
respectively replace the values of `proxmox_api_user` and
`proxmox_api_token_secret`.

The example below creates two containers that will serve as DHCP servers. The
configuration of the DHCP service is not managed by this role. The variable
`proxmox_container_dhcp` is defined as a play variable. It is then passed to
`proxmox_container` as a role parameter. It is advised to define it in your
Ansible inventory.

- name: Instanciate DHCP servers in PVE
hosts: localhost
vars:
proxmox_api_token_secret: "{{ lookup('env','PROXMOX_API_TOKEN') }}"
proxmox_container_dhcp:
- hostname: 'dhcp1'
node: 'pve-1'
ostemplate: 'local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz'
disk: 'local:2'
cores: 2
nameserver: '10.10.0.1'
netif: '{"net0":"name=eth0,gw=10.10.0.254,ip=10.10.0.2/24,bridge=vmbr0"}'
unprivileged: True
features:
- nesting=1 # For systemd, otherwise takes a lot of time to open ssh session
- hostname: 'dhcp2'
node: 'pve-2'
ostemplate: 'local:vztmpl/debian-11-standard_11.0-1_amd64.tar.gz'
disk: 'local:2'
cores: 2
nameserver: '10.10.0.1'
netif: '{"net0":"name=eth0,gw=10.10.0.254,ip=10.10.0.3/24,bridge=vmbr0"}'
unprivileged: True
features:
- nesting=1 # For systemd, otherwise takes a lot of time to open ssh session
roles:
- name: Download proxmox appliance container template
role: mila.proxmox.container
vars:
proxmox_container: "{{ proxmox_container_dhcp }}"


Once a container exists, some parameters cannot be changed with the
`community.general.proxmox` module. To circumvent this limitation, it is
possible to destroy the containers and recreate these. For this, include the
role `mila.proxmox.container` in the pre_tasks of your playbook, with
`tasks_from: destroy`.

- name: Instanciate containers
hosts: localhost
pre_tasks:
- name: Ensure existing containers are removed
block:
- name: Remove SSH public host keys
ansible.builtin.known_hosts:
name: "{{ item }}"
state: absent
loop:
- 10.10.0.2
- 10.10.0.3
- name: Cleanup containers
ansible.builtin.include_role:
name: mila.proxmox.container
tasks_from: destroy
vars:
proxmox_container: "{{ proxmox_container_list }}"
when: proxmox_container_destroy_first is defined and proxmox_container_destroy_first
roles:
- name: Instanciate containers in Proxmox VE cluster
role: mila.proxmox.container
vars:
proxmox_container: "{{ proxmox_container_list }}"

In the example above, ansible-playbook must run with `-e
proxmox_container_destroy_first=True` for the destruction of the containers to
be effective.
66 changes: 66 additions & 0 deletions galaxy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
### REQUIRED
# The namespace of the collection. This can be a company/brand/organization or product namespace under which all
# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
# underscores or numbers and cannot contain consecutive underscores
namespace: mila

# The name of the collection. Has the same character restrictions as 'namespace'
name: proxmox

# The version of the collection. Must be compatible with semantic versioning
version: 0.1.0

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md

# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
# @nicks:irc/im.site#channel'
authors:
- Bruno Travouillon <bruno.travouillon@mila.quebec>


### OPTIONAL but strongly recommended
# A short summary description of the collection
description: Ansible Collection to manage containers and virtual machines with Proxmox VE

# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
license:
- MIT

# The path to the license file for the collection. This path is relative to the root of the collection. This key is
# mutually exclusive with 'license'
license_file: ''

# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
# requirements as 'namespace' and 'name'
tags:
- proxmox
- virtualization
- container
- vm

# Collections that this collection requires to be installed for it to be usable. The key of the dict is the
# collection label 'namespace.name'. The value is a version range
# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version
# range specifiers can be set and are separated by ','
dependencies: {community.general: '>4.0.0'}

# The URL of the originating SCM repository
repository: https://github.com/mila-iqia/ansible-collection-proxmox

# The URL to any online docs
# documentation: http://docs.example.com

# The URL to the homepage of the collection/project
# homepage: http://example.com

# The URL to the collection issue tracker
issues: https://github.com/mila-iqia/ansible-collection-proxmox/issues

# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
# and '.git' are always filtered
build_ignore: []

57 changes: 57 additions & 0 deletions roles/container/tasks/create.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
- name: Setup LXC Containers
community.general.proxmox:
proxmox_default_behavior: 'no_defaults'
api_host: "{{ proxmox_api_host }}"
api_token_id: "{{ item.api_token_id | default(proxmox_api_token_id) }}"
api_token_secret: "{{ item.api_token_secret | default(proxmox_api_token_secret) }}"
api_user: "{{ item.api_user | default(proxmox_api_user) }}"
cores: "{{ item.cores | default(1) }}"
cpus: "{{ item.cpus | default(1) }}"
cpuunits: "{{ item.cpuunits | default(1000) }}"
description: "{{ item.description | default(omit) }}"
disk: "{{ item.disk }}"
features: "{{ item.features | default(omit) }}"
force: "{{ item.force | default(omit) | bool }}"
hookscript: "{{ item.hookscript | default(omit) }}"
hostname: "{{ item.hostname }}"
memory: "{{ item.memory | default(512) }}"
nameserver: "{{ item.nameserver | default(omit) }}"
# Remember to create bridge first on hypervisor
# https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_11_Bullseye#Create_a_Linux_Bridge
netif: "{{ item.netif }}"
node: "{{ item.node }}"
onboot: "{{ item.onboot | default(False) }}"
ostemplate: "{{ item.ostemplate }}"
pubkey: "{{ item.pubkey | default(proxmox_pubkey) | default(omit) }}"
state: "{{ item.state | default('present') }}"
swap: "{{ item.swap | default(0) }}"
timeout: "{{ item.timeout | default(120) }}"
unprivileged: "{{ item.unprivileged | default(omit) }}"
validate_certs: "{{ item.validate_certs | default(omit) | bool }}"
vmid: "{{ item.vmid | default(omit) }}"
loop: "{{ proxmox_container }}"
register: proxmox_container_setup

- name: Pause for 10 seconds to finish LXC setup
pause:
seconds: 10
when: proxmox_container_setup.changed

- name: Start Containers
community.general.proxmox:
proxmox_default_behavior: 'no_defaults'
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
node: "{{ item.node }}"
hostname: "{{ item.hostname }}"
state: started
loop: "{{ proxmox_container }}"
register: proxmox_container_start

- name: Pause for 20 seconds to finish LXC start
pause:
seconds: 20
when: proxmox_container_start.changed
32 changes: 32 additions & 0 deletions roles/container/tasks/destroy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
- name: Stop Containers
community.general.proxmox:
proxmox_default_behavior: 'no_defaults'
#proxmox_default_behavior: 'compatibility'
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
node: "{{ item.node }}"
hostname: "{{ item.hostname }}"
state: stopped
loop: "{{ proxmox_container }}"
register: proxmox_container_stop

- name: Pause for 10 seconds to finish LXC stop
pause:
seconds: 10
when: proxmox_container_stop.changed

- name: Destroy Containers
community.general.proxmox:
proxmox_default_behavior: 'no_defaults'
#proxmox_default_behavior: 'compatibility'
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
node: "{{ item.node }}"
hostname: "{{ item.hostname }}"
state: absent
loop: "{{ proxmox_container }}"
18 changes: 18 additions & 0 deletions roles/container/tasks/install_templates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
- name: Set facts to install OS templates
ansible.builtin.set_fact:
_ostemplate_regex: '^([a-z]+):([a-z]+)/(.*)'
_list_of_templates: "{{ proxmox_container | community.general.json_query('[*].ostemplate') | unique }}"

- name: Download proxmox appliance container templates
community.general.proxmox_template:
api_host: "{{ proxmox_api_host }}"
api_user: "{{ proxmox_api_user }}"
api_token_id: "{{ proxmox_api_token_id }}"
api_token_secret: "{{ proxmox_api_token_secret }}"
node: "{{ item.0 }}"
storage: "{{ item.1 | regex_search(_ostemplate_regex, '\\1') | first }}"
content_type: "{{ item.1 | regex_search(_ostemplate_regex, '\\2') | first }}"
template: "{{ item.1 | regex_search(_ostemplate_regex, '\\3') | first }}"
timeout: 300
loop: "{{ groups[pve_group] | product(_list_of_templates) | list }}"
8 changes: 8 additions & 0 deletions roles/container/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- name: Import tasks to install containers templates
ansible.builtin.include_tasks:
file: install_templates.yml

- name: Import tasks to create containers
ansible.builtin.include_tasks:
file: create.yml
Empty file added roles/vm/.gitkeep
Empty file.

0 comments on commit 761973b

Please sign in to comment.