kayobe/ansible/idrac-bootstrap.yml

232 lines
10 KiB
YAML

---
# This playbook is intended to bootstrap network configuration of the iDRACs on
# a set of Dell servers. By default, iDRACs have a known static IP address
# configured. Since all iDRACs have the same default IP address, we need a way
# to isolate a single iDRAC while we set its network configuration. We do this
# using a temporary VLAN accessible from one of the controllers.
#
# We use the following procedure:
# 1. Create a VLAN interface on the controller node with IP in the iDRAC
# default subnet.
# 2. Create the temporary bootstrap VLAN on the switch, accessible by the
# controller and trunked to all switches within the network.
# 3. For each iDRAC switch port in turn, flip to the temporary VLAN and
# configure the iDRAC's IP address, before returning the port to the iDRAC
# management VLAN.
# 4. Remove the temporary bootstrap VLAN from the switch.
# 5. Remove the VLAN interface on the controller node.
# Playbook variables:
#
# idrac_limit: Colon-separated list of names of idracs to limit bootstrappping
# to. These names should be present in the variable
# idrac_network_ips. If omitted all idracs will be bootstrapped.
- name: Ensure the iDRAC switches are supported
hosts: "{{ idrac_bootstrap_switch_group }}"
gather_facts: no
vars:
supported_switch_types:
- dellos6
tasks:
- name: Ensure switch type is supported
fail:
msg: >
The iDRAC bootstrap process currently only supports DellOS6 switches.
when: switch_type not in supported_switch_types
# 1. Create a VLAN interface on the controller node with IP in the iDRAC
# default subnet.
- name: Ensure the controller bootstrap network is configured
hosts: "{{ idrac_bootstrap_controller_group ~ '[0]' }}"
tasks:
# Install Dell server admin tools.
- block:
- name: Ensure wget is installed
package:
name: wget
state: present
cache_valid_time: "{{ apt_cache_valid_time if ansible_facts.os_family == 'Debian' else omit }}"
update_cache: "{{ True if ansible_facts.os_family == 'Debian' else omit }}"
- name: Ensure Dell srvadmin repository is installed
shell: "wget -q -O - http://linux.dell.com/repo/hardware/latest/bootstrap.cgi | bash"
- name: Ensure Dell srvadmin-idrac7 package is installed
package:
name: srvadmin-idrac7
state: present
# Configure access to the temporary network on a controller.
- block:
# Clear any previous state.
- name: Ensure iDRAC bootstrap network namespace is deleted from the controller
command: "ip netns delete {{ idrac_bootstrap_net_namespace }}"
args:
removes: "/var/run/netns/{{ idrac_bootstrap_net_namespace }}"
- name: Ensure iDRAC bootstrap network namespace exists on controller
command: "ip netns add {{ idrac_bootstrap_net_namespace }}"
- name: Ensure bootstrap VLAN interface exists on the controller
command: "ip link add link {{ idrac_bootstrap_controller_interface }} name {{ idrac_bootstrap_controller_vlan_interface }} type vlan id {{ idrac_bootstrap_vlan }}"
- name: Ensure bootstrap VLAN interface is in network namespace
command: "ip link set {{ idrac_bootstrap_controller_vlan_interface }} netns {{ idrac_bootstrap_net_namespace }}"
- name: Ensure the bootstrap VLAN interface is active
command: "ip netns exec {{ idrac_bootstrap_net_namespace }} ip link set {{ idrac_bootstrap_controller_vlan_interface }} up"
- name: Ensure the bootstrap VLAN interface IP address is configured
command: "ip netns exec {{ idrac_bootstrap_net_namespace }} ip address add {{ idrac_bootstrap_controller_ip }}/24 dev {{ idrac_bootstrap_controller_vlan_interface }}"
rescue:
- name: Rescue | Ensure the bootstrap network namespace is removed from the controller
command: "ip netns delete {{ idrac_bootstrap_net_namespace }}"
- name: Rescue | Fail playbook execution on error
fail:
msg: >
Failed to configure access to temporary iDRAC bootstrap
network on controller.
become: True
# 2. Create the temporary bootstrap VLAN on the switch, accessible by the
# controller and trunked to all switches within the network.
- name: Ensure the bootstrap VLAN is configured on switches
hosts: "{{ idrac_bootstrap_switch_group }}"
gather_facts: no
vars:
switch_interface_config_bootstrap_trunk:
config:
- "switchport trunk allowed vlan add {{ idrac_bootstrap_vlan }}"
# Initialise the switch interface configuration.
switch_interface_config_bootstrap: {}
pre_tasks:
- name: Update facts about switch trunk interfaces
set_fact:
switch_interface_config_bootstrap: >
{{ switch_interface_config_bootstrap | combine({item.key: switch_interface_config_bootstrap_trunk}) }}
with_dict: "{{ switch_interface_config }}"
when: >
{{ item.value.description | default == groups[idrac_bootstrap_controller_group][0] or
item.value.description | default | replace('-trunk', '') in groups[idrac_bootstrap_switch_group] }}
roles:
# Configure bootstrap VLAN on the switch and add controller and trunk
# interfaces to it.
- role: dell-switch
dell_switch_type: "{{ switch_type }}"
dell_switch_provider: "{{ switch_dellos_provider }}"
dell_switch_config:
- "vlan {{ idrac_bootstrap_vlan }}"
dell_switch_interface_config: "{{ switch_interface_config_bootstrap }}"
when: switch_interface_config_bootstrap != {}
# 3. For each iDRAC switch port in turn, flip to the temporary VLAN and
# configure the iDRAC's IP address, before returning the port to the iDRAC
# management VLAN.
- name: Ensure iDRACs are bootstrapped
hosts: "{{ idrac_bootstrap_switch_group }}"
gather_facts: no
vars:
# Set this to a colon-separated list of idrac hostnames to bootstrap.
# If unset, all idracs will be bootstrapped.
idrac_limit: ""
idrac_limit_list: "{{ idrac_limit.split(':') }}"
# This is a separate play so that we can apply the serial keyword.
serial: 1
tasks:
- name: Initialise facts containing successful, unchanged and failed iDRACs
set_fact:
idrac_bootstrap_success: []
idrac_bootstrap_unchanged: []
idrac_bootstrap_failed: []
# Iterate over each switch port with an iDRAC attached in turn.
- name: Ensure iDRACs are (sequentially) bootstrapped
include_tasks: idrac-bootstrap-one.yml
vars:
dell_switch_type: "{{ switch_type }}"
dell_switch_provider: "{{ switch_dellos_provider }}"
switch_interface_name: "{{ item.key }}"
idrac_port_description: "{{ item.value.description }}"
idrac_network_ip: "{{ idrac_network_ips[idrac_port_description] }}"
idrac_bootstrap_controller: "{{ hostvars[groups[idrac_bootstrap_controller_group][0]].ansible_host }}"
with_dict: "{{ switch_interface_config }}"
when: >
{{ item.value.description | default in idrac_network_ips and
(not idrac_limit or item.value.description | default in idrac_limit_list) }}
# 4. Remove the temporary bootstrap VLAN from the switch.
- name: Ensure the bootstrap VLAN is removed from switches
hosts: "{{ idrac_bootstrap_switch_group }}"
gather_facts: no
vars:
switch_interface_config_bootstrap_trunk:
config:
- "switchport trunk allowed vlan remove {{ idrac_bootstrap_vlan }}"
# Initialise the switch interface configuration.
switch_interface_config_bootstrap: {}
pre_tasks:
- name: Update facts about switch trunk interfaces
set_fact:
switch_interface_config_bootstrap: >
{{ switch_interface_config_bootstrap | combine({item.key: switch_interface_config_bootstrap_trunk}) }}
with_dict: "{{ switch_interface_config }}"
when: >
{{ item.value.description | default == groups[idrac_bootstrap_controller_group][0] or
item.value.description | default | replace('-trunk', '') in groups[idrac_bootstrap_switch_group] }}
roles:
# Remove bootstrap VLAN from the switch and remove controller and trunk
# interfaces from it.
- role: dell-switch
dell_switch_type: "{{ switch_type }}"
dell_switch_provider: "{{ switch_dellos_provider }}"
dell_switch_config:
- "no vlan {{ idrac_bootstrap_vlan }}"
dell_switch_interface_config: "{{ switch_interface_config_bootstrap }}"
when: switch_interface_config_bootstrap != {}
# 5. Remove the VLAN interface on the controller node.
- name: Ensure the controller bootstrap network is cleaned up
hosts: "{{ idrac_bootstrap_controller_group ~ '[0]' }}"
tasks:
# This should also delete the network interface within the namespace.
- name: Ensure the bootstrap network namespace is removed from the controller
command: "ip netns delete {{ idrac_bootstrap_net_namespace }}"
become: True
- name: Display the results of the iDRAC bootstrap procedure
hosts: "{{ idrac_bootstrap_switch_group }}"
gather_facts: no
tasks:
- name: Display a list of failed iDRACs
set_fact:
idrac_bootstrap_failed_port_descriptions: "{{ idrac_bootstrap_failed | map(attribute='port description') | list }}"
when: idrac_bootstrap_failed | length > 0
- name: Display a list of successfully bootstrapped iDRACs
debug:
var: idrac_bootstrap_success
- name: Display a list of iDRACs that did not require bootstrapping
debug:
var: idrac_bootstrap_unchanged
- name: Display a list of failed iDRACs
debug:
var: idrac_bootstrap_failed_port_descriptions
when: idrac_bootstrap_failed | length > 0
- name: Display a list of failed iDRACs with debug output for the failed tasks
debug:
var: idrac_bootstrap_failed
when: idrac_bootstrap_failed | length > 0
- name: Fail if there were any iDRAC bootstrapping failures
fail:
msg: >
One or more iDRACs failed to bootstrap, see the list above for
details.
when: idrac_bootstrap_failed | length > 0