Add Ironic enrolment Ansible role
This commit is contained in:
parent
8e9e91ac79
commit
81f1696263
@ -51,6 +51,8 @@ class ActionModule(ActionBase):
|
|||||||
for typ, cnt in six.iteritems(task_vars['specs']):
|
for typ, cnt in six.iteritems(task_vars['specs']):
|
||||||
for _ in six.moves.range(cnt):
|
for _ in six.moves.range(cnt):
|
||||||
node = deepcopy(task_vars['node_types'][typ])
|
node = deepcopy(task_vars['node_types'][typ])
|
||||||
|
# Set the type, for future reference.
|
||||||
|
node['type'] = typ
|
||||||
# Sequentially number the node and volume names.
|
# Sequentially number the node and volume names.
|
||||||
node['name'] = "%s%d" % (task_vars['node_name_prefix'], idx)
|
node['name'] = "%s%d" % (task_vars['node_name_prefix'], idx)
|
||||||
for vol_idx, vol in enumerate(node['volumes']):
|
for vol_idx, vol in enumerate(node['volumes']):
|
||||||
|
@ -89,3 +89,27 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
loop_var: domain
|
loop_var: domain
|
||||||
index_var: port_offset
|
index_var: port_offset
|
||||||
|
|
||||||
|
- hosts: localhost
|
||||||
|
tasks:
|
||||||
|
- name: Check that OpenStack credentials exist in the environment
|
||||||
|
fail:
|
||||||
|
msg: >
|
||||||
|
$OS_USERNAME was not found in the environment. Ensure the OpenStack
|
||||||
|
credentials exist in your environment, perhaps by sourcing your RC file.
|
||||||
|
when: not lookup('env', 'OS_USERNAME')
|
||||||
|
|
||||||
|
- name: Perform Ironic enrolment for each hypervisor's nodes
|
||||||
|
include_role:
|
||||||
|
name: ironic-enrolment
|
||||||
|
vars:
|
||||||
|
ironic_deploy_kernel_uuid: "{{ deploy_kernel_uuid }}"
|
||||||
|
ironic_deploy_ramdisk_uuid: "{{ deploy_ramdisk_uuid }}"
|
||||||
|
ironic_nodes: "{{ alloc.1 }}"
|
||||||
|
ironic_hypervisor: "{{ alloc.0 }}"
|
||||||
|
ironic_virtualenv_path: "{{ virtualenv_path }}"
|
||||||
|
ironic_python_upper_constraints_url: >-
|
||||||
|
{{ python_upper_constraints_url }}
|
||||||
|
loop: "{{ allocations.result.iteritems() | list }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: alloc
|
||||||
|
@ -111,7 +111,7 @@ def ovs_link_name(context, node, physnet):
|
|||||||
def source_to_ovs_link_name(context, source):
|
def source_to_ovs_link_name(context, source):
|
||||||
"""Get the corresponding OVS link name for a source link name.
|
"""Get the corresponding OVS link name for a source link name.
|
||||||
"""
|
"""
|
||||||
base = source[:len(_get_hostvar(context, 'veth_node_source_suffix'))]
|
base = source[:-len(_get_hostvar(context, 'veth_node_source_suffix'))]
|
||||||
return base + _get_hostvar(context, 'veth_node_ovs_suffix')
|
return base + _get_hostvar(context, 'veth_node_ovs_suffix')
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,3 +32,8 @@ node_types: {}
|
|||||||
# # 'type0'.
|
# # 'type0'.
|
||||||
# type0: 4
|
# type0: 4
|
||||||
specs: {}
|
specs: {}
|
||||||
|
|
||||||
|
# The Glance UUID of the image to use for the deployment kernel.
|
||||||
|
deploy_kernel_uuid:
|
||||||
|
# The Glance UUID of the image to use for the deployment ramdisk.
|
||||||
|
deploy_ramdisk_uuid:
|
||||||
|
30
ansible/roles/ironic-enrolment/README.md
Normal file
30
ansible/roles/ironic-enrolment/README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Ironic Enrolment
|
||||||
|
================
|
||||||
|
|
||||||
|
This role enrols nodes with OpenStack Ironic, creates Ironic ports for each of
|
||||||
|
the nodes' NICs, and sets relevant attributes on created resources.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- *OS_\** environment variables for the OpenStack cloud in question present in
|
||||||
|
the shell environment. These can be sourced from an OpenStack RC file, for
|
||||||
|
example.
|
||||||
|
|
||||||
|
- The `virsh` command-line tool present at `/bin/virsh`.
|
||||||
|
|
||||||
|
Role Variables
|
||||||
|
--------------
|
||||||
|
|
||||||
|
- `ironic_nodes`: A list of dicts of details for nodes that are to be enroled
|
||||||
|
in Ironic.
|
||||||
|
- `ironic_hypervisor`: The hostname of the hypervisor on which `ironic_nodes`
|
||||||
|
exist.
|
||||||
|
- `ironic_deploy_kernel_uuid`: The Glance UUID of the image to use for the
|
||||||
|
deployment kernel.
|
||||||
|
- `ironic_deploy_ramdisk_uuid`: The Glance UUID of the image to use for the
|
||||||
|
deployment ramdisk.
|
||||||
|
- `ironic_virtualenv_path`: The path to the virtualenv in which to install the
|
||||||
|
OpenStack clients.
|
||||||
|
- `ironic_python_upper_constraints_url`: The URL of the upper constraints file
|
||||||
|
to pass to pip when installing Python packages.
|
14
ansible/roles/ironic-enrolment/defaults/main.yml
Normal file
14
ansible/roles/ironic-enrolment/defaults/main.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
# A list of dicts of details for nodes that are to be enroled in Ironic.
|
||||||
|
ironic_nodes: []
|
||||||
|
# The hostname of the hypervisor where these nodes exist.
|
||||||
|
ironic_hypervisor:
|
||||||
|
# The Glance UUID of the image to use for the deployment kernel.
|
||||||
|
ironic_deploy_kernel_uuid:
|
||||||
|
# The Glance UUID of the image to use for the deployment ramdisk.
|
||||||
|
ironic_deploy_ramdisk_uuid:
|
||||||
|
# The path to the virtualenv in which to install the OpenStack clients.
|
||||||
|
ironic_virtualenv_path:
|
||||||
|
# The URL of the upper constraints file to pass to pip when installing Python
|
||||||
|
# packages.
|
||||||
|
ironic_python_upper_constraints_url:
|
6
ansible/roles/ironic-enrolment/files/requirements.txt
Normal file
6
ansible/roles/ironic-enrolment/files/requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# This file contains the Python packages that are needed in the Tenks virtual
|
||||||
|
# env.
|
||||||
|
|
||||||
|
openstacksdk>=0.17.2 # Apache
|
||||||
|
python-ironicclient>=2.5.0 # Apache
|
||||||
|
python-openstackclient>=3.16.0 # Apache
|
18
ansible/roles/ironic-enrolment/tasks/main.yml
Normal file
18
ansible/roles/ironic-enrolment/tasks/main.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure Python requirements are installed
|
||||||
|
pip:
|
||||||
|
requirements: "{{ '/'.join([role_path, 'files', 'requirements.txt']) }}"
|
||||||
|
extra_args: >-
|
||||||
|
-c {{ ironic_python_upper_constraints_url }}
|
||||||
|
virtualenv: "{{ ironic_virtualenv_path }}"
|
||||||
|
|
||||||
|
- name: Enrol the Ironic nodes
|
||||||
|
include_tasks: node.yml
|
||||||
|
vars:
|
||||||
|
node: "{{ ironic_node }}"
|
||||||
|
ipmi_port: >-
|
||||||
|
{{ hostvars[ironic_hypervisor].ipmi_port_range_start + port_offset }}
|
||||||
|
loop: "{{ ironic_nodes | sort(attribute='name') }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: ironic_node
|
||||||
|
index_var: port_offset
|
90
ansible/roles/ironic-enrolment/tasks/node.yml
Normal file
90
ansible/roles/ironic-enrolment/tasks/node.yml
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
- name: Get vNIC MAC addresses
|
||||||
|
# The output format of this command gives two lines of header, followed by
|
||||||
|
# (for each vNIC):
|
||||||
|
# <name> <type> <source interface> <model> <MAC>
|
||||||
|
# The VMs will have been created with the virt module, using become: true.
|
||||||
|
# This targets /bin/virsh rather than /usr/bin/virsh.
|
||||||
|
command: /bin/virsh domiflist '{{ node.name }}'
|
||||||
|
register: iflist_res
|
||||||
|
changed_when: false
|
||||||
|
become: true
|
||||||
|
delegate_to: "{{ ironic_hypervisor }}"
|
||||||
|
run_once: true
|
||||||
|
|
||||||
|
# We need to do this for each run to ensure other nodes' NICs don't carry over
|
||||||
|
# to this run.
|
||||||
|
- name: Reset list of NICs
|
||||||
|
set_fact:
|
||||||
|
nics: []
|
||||||
|
|
||||||
|
- name: Collect MAC addresses into NIC list
|
||||||
|
set_fact:
|
||||||
|
nics: "{{ nics | union([{'mac': item.split()[4]}]) }}"
|
||||||
|
loop: "{{ iflist_res.stdout_lines[2:] }}"
|
||||||
|
|
||||||
|
- name: Create node in Ironic
|
||||||
|
os_ironic:
|
||||||
|
auth_type: password
|
||||||
|
driver: ipmi
|
||||||
|
driver_info:
|
||||||
|
power:
|
||||||
|
ipmi_address: "{{ hostvars[ironic_hypervisor].ipmi_address }}"
|
||||||
|
# This is passed in from main.yml.
|
||||||
|
ipmi_port: "{{ ipmi_port }}"
|
||||||
|
ipmi_username: "{{ hostvars[ironic_hypervisor].ipmi_username }}"
|
||||||
|
ipmi_password: "{{ hostvars[ironic_hypervisor].ipmi_password }}"
|
||||||
|
deploy:
|
||||||
|
deploy_kernel: "{{ ironic_deploy_kernel_uuid | default(omit, true) }}"
|
||||||
|
deploy_ramdisk: "{{ ironic_deploy_ramdisk_uuid | default(omit, true) }}"
|
||||||
|
name: "{{ node.name }}"
|
||||||
|
nics: "{{ nics }}"
|
||||||
|
properties:
|
||||||
|
ram: "{{ node.memory_mb }}"
|
||||||
|
# FIXME(w-miller): Instead of assuming the first volume is the primary
|
||||||
|
# volume, make this configurable?
|
||||||
|
disk_size: >-
|
||||||
|
{{ (node.volumes.0.capacity | default('1')) | size_string_to_gb }}
|
||||||
|
cpus: "{{ node.vcpus }}"
|
||||||
|
vars:
|
||||||
|
# This module requires the openstacksdk package, which is installed within
|
||||||
|
# our virtualenv.
|
||||||
|
ansible_python_interpreter: >-
|
||||||
|
{{ '/'.join([ironic_virtualenv_path, 'bin', 'python']) }}
|
||||||
|
register: created_node
|
||||||
|
|
||||||
|
# The os_ironic module automatically brings the node from 'enrol' to
|
||||||
|
# 'available' state, but we still need to set more port and node attributes.
|
||||||
|
# Use maintenance mode to do this.
|
||||||
|
- name: Put Ironic node into maintenance mode
|
||||||
|
command: >-
|
||||||
|
'{{ ironic_virtualenv_path }}/bin/openstack' baremetal node maintenance set
|
||||||
|
'{{ created_node.uuid }}'
|
||||||
|
|
||||||
|
# FIXME(w-miller): Make interfaces/driver configurable, for example to allow
|
||||||
|
# use of Redfish instead of IPMI.
|
||||||
|
- name: Set Ironic node resource class
|
||||||
|
command: >-
|
||||||
|
'{{ ironic_virtualenv_path }}/bin/openstack' baremetal node set
|
||||||
|
'{{ created_node.uuid }}'
|
||||||
|
--resource-class {{ node.type }}
|
||||||
|
# --boot-interface pxe
|
||||||
|
# --deploy-interface iscsi
|
||||||
|
# --management-interface ipmitool
|
||||||
|
# --network-interface neutron
|
||||||
|
# --power-interface ipmitool
|
||||||
|
|
||||||
|
- name: Set additional Ironic port attributes
|
||||||
|
include_tasks: port.yml
|
||||||
|
vars:
|
||||||
|
source_interface: "{{ vnic.split()[2] }}"
|
||||||
|
mac: "{{ vnic.split()[4] }}"
|
||||||
|
# Loop over each NIC.
|
||||||
|
loop: "{{ iflist_res.stdout_lines[2:] }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: vnic
|
||||||
|
|
||||||
|
- name: Bring Ironic node out of maintenance mode
|
||||||
|
command: >-
|
||||||
|
'{{ ironic_virtualenv_path }}/bin/openstack' baremetal node maintenance
|
||||||
|
unset '{{ created_node.uuid }}'
|
29
ansible/roles/ironic-enrolment/tasks/port.yml
Normal file
29
ansible/roles/ironic-enrolment/tasks/port.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
- name: Get Ironic port UUID
|
||||||
|
command: >-
|
||||||
|
'{{ ironic_virtualenv_path }}/bin/openstack' baremetal port list
|
||||||
|
--format value
|
||||||
|
--column UUID
|
||||||
|
--address {{ mac }}
|
||||||
|
register: uuid
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get physical network name
|
||||||
|
set_fact:
|
||||||
|
physnet: "{{ source_interface | source_link_to_physnet_name }}"
|
||||||
|
|
||||||
|
- name: Get bridge name
|
||||||
|
set_fact:
|
||||||
|
bridge: "{{ physnet | bridge_name }}"
|
||||||
|
|
||||||
|
- name: Set Ironic port attributes
|
||||||
|
command: >-
|
||||||
|
'{{ ironic_virtualenv_path }}/bin/openstack' baremetal port set
|
||||||
|
{{ uuid.stdout }}
|
||||||
|
--physical-network '{{ physnet }}'
|
||||||
|
--local-link-connection switch_id='{{ hostvars[ironic_hypervisor][
|
||||||
|
'ansible_' + bridge
|
||||||
|
].macaddress }}'
|
||||||
|
--local-link-connection switch_info='{{ bridge }}'
|
||||||
|
--local-link-connection port_id='{{ source_interface
|
||||||
|
| source_to_ovs_link_name }}'
|
Loading…
x
Reference in New Issue
Block a user