--- # 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_os_family == 'Debian' else omit }}" update_cache: "{{ True if ansible_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