Add commands to enable and disable the serial console
This allows you to access the serial console from within Horizon. Change-Id: Id40e72047174fc0c0c565871f24b775b30e83825 Story: 2004192 Task: 27682
This commit is contained in:
parent
60ecee2b25
commit
4867c91481
130
ansible/baremetal-compute-serial-console.yml
Normal file
130
ansible/baremetal-compute-serial-console.yml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
# This playbook will enable a serial console on all ironic nodes. This
|
||||||
|
# will allow you to access the serial console from within Horizon.
|
||||||
|
# See: https://docs.openstack.org/ironic/latest/admin/console.html
|
||||||
|
|
||||||
|
- name: Setup OpenStack Environment
|
||||||
|
hosts: controllers[0]
|
||||||
|
gather_facts: False
|
||||||
|
vars:
|
||||||
|
venv: "{{ virtualenv_path }}/openstack-cli"
|
||||||
|
pre_tasks:
|
||||||
|
- name: Set up openstack cli virtualenv
|
||||||
|
pip:
|
||||||
|
virtualenv: "{{ venv }}"
|
||||||
|
name:
|
||||||
|
- python-openstackclient
|
||||||
|
- python-ironicclient
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Fail if allocation pool start not defined
|
||||||
|
fail:
|
||||||
|
msg: >
|
||||||
|
The variable, ironic_serial_console_tcp_pool_start is not defined.
|
||||||
|
This variable is required to run this playbook.
|
||||||
|
when: not ironic_serial_console_tcp_pool_start
|
||||||
|
|
||||||
|
- name: Fail if allocation pool end not defined
|
||||||
|
fail:
|
||||||
|
msg: >
|
||||||
|
The variable, ironic_serial_console_tcp_pool_end is not defined.
|
||||||
|
This variable is required to run this playbook.
|
||||||
|
when:
|
||||||
|
- not ironic_serial_console_tcp_pool_end
|
||||||
|
|
||||||
|
- name: Get list of nodes that we should configure serial consoles on
|
||||||
|
set_fact:
|
||||||
|
baremetal_nodes: >-
|
||||||
|
{{ query('inventory_hostnames', console_compute_node_limit |
|
||||||
|
default('baremetal-compute') ) | unique }}
|
||||||
|
|
||||||
|
- name: Reserve TCP ports for ironic serial consoles
|
||||||
|
include_role:
|
||||||
|
name: console-allocation
|
||||||
|
vars:
|
||||||
|
console_allocation_pool_start: "{{ ironic_serial_console_tcp_pool_start }}"
|
||||||
|
console_allocation_pool_end: "{{ ironic_serial_console_tcp_pool_end }}"
|
||||||
|
console_allocation_ironic_nodes: "{{ baremetal_nodes }}"
|
||||||
|
console_allocation_filename: "{{ kayobe_config_path }}/console-allocation.yml"
|
||||||
|
when: cmd == "enable"
|
||||||
|
|
||||||
|
- name: Enable serial console
|
||||||
|
hosts: "{{ console_compute_node_limit | default('baremetal-compute') }}"
|
||||||
|
gather_facts: False
|
||||||
|
vars:
|
||||||
|
venv: "{{ virtualenv_path }}/openstack-cli"
|
||||||
|
controller_host: "{{ groups['controllers'][0] }}"
|
||||||
|
tasks:
|
||||||
|
- name: Get list of nodes
|
||||||
|
command: >
|
||||||
|
{{ venv }}/bin/openstack baremetal node list -f json --long
|
||||||
|
register: nodes
|
||||||
|
delegate_to: "{{ controller_host }}"
|
||||||
|
environment: "{{ openstack_auth_env }}"
|
||||||
|
run_once: true
|
||||||
|
changed_when: false
|
||||||
|
vars:
|
||||||
|
# NOTE: Without this, the controller's ansible_host variable will not
|
||||||
|
# be respected when using delegate_to.
|
||||||
|
ansible_host: "{{ hostvars[controller_host].ansible_host | default(controller_host) }}"
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Fail if console interface is not ipmitool-socat
|
||||||
|
fail:
|
||||||
|
msg: >-
|
||||||
|
In order to use the serial console you must set the console_interface to ipmitool-socat.
|
||||||
|
when: node["Console Interface"] != "ipmitool-socat"
|
||||||
|
|
||||||
|
- name: Set IPMI serial console terminal port
|
||||||
|
vars:
|
||||||
|
name: "{{ node['Name'] }}"
|
||||||
|
port: "{{ hostvars[controller_host].console_allocation_result.ports[name] }}"
|
||||||
|
# NOTE: Without this, the controller's ansible_host variable will not
|
||||||
|
# be respected when using delegate_to.
|
||||||
|
ansible_host: "{{ hostvars[controller_host].ansible_host | default(controller_host) }}"
|
||||||
|
command: >
|
||||||
|
{{ venv }}/bin/openstack baremetal node set {{ name }} --driver-info ipmi_terminal_port={{ port }}
|
||||||
|
delegate_to: "{{ controller_host }}"
|
||||||
|
environment: "{{ openstack_auth_env }}"
|
||||||
|
when: >-
|
||||||
|
node['Driver Info'].ipmi_terminal_port is not defined or
|
||||||
|
node['Driver Info'].ipmi_terminal_port | int != port | int
|
||||||
|
|
||||||
|
- name: Enable the IPMI socat serial console
|
||||||
|
vars:
|
||||||
|
# NOTE: Without this, the controller's ansible_host variable will not
|
||||||
|
# be respected when using delegate_to.
|
||||||
|
ansible_host: "{{ hostvars[controller_host].ansible_host | default(controller_host) }}"
|
||||||
|
command: >
|
||||||
|
{{ venv }}/bin/openstack baremetal node console enable {{ node['Name'] }}
|
||||||
|
delegate_to: "{{ controller_host }}"
|
||||||
|
environment: "{{ openstack_auth_env }}"
|
||||||
|
when: not node['Console Enabled']
|
||||||
|
vars:
|
||||||
|
matching_nodes: >-
|
||||||
|
{{ (nodes.stdout | from_json) | selectattr('Name', 'defined') |
|
||||||
|
selectattr('Name', 'equalto', inventory_hostname ) | list }}
|
||||||
|
node: "{{ matching_nodes | first }}"
|
||||||
|
when:
|
||||||
|
- cmd == "enable"
|
||||||
|
- matching_nodes | length > 0
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Disable the IPMI socat serial console
|
||||||
|
vars:
|
||||||
|
# NOTE: Without this, the controller's ansible_host variable will not
|
||||||
|
# be respected when using delegate_to.
|
||||||
|
ansible_host: "{{ hostvars[controller_host].ansible_host | default(controller_host) }}"
|
||||||
|
command: >
|
||||||
|
{{ venv }}/bin/openstack baremetal node console disable {{ node['Name'] }}
|
||||||
|
delegate_to: "{{ controller_host }}"
|
||||||
|
environment: "{{ openstack_auth_env }}"
|
||||||
|
when: node['Console Enabled']
|
||||||
|
vars:
|
||||||
|
matching_nodes: >-
|
||||||
|
{{ (nodes.stdout | from_json) | selectattr('Name', 'defined') |
|
||||||
|
selectattr('Name', 'equalto', inventory_hostname ) | list }}
|
||||||
|
node: "{{ matching_nodes | first }}"
|
||||||
|
when:
|
||||||
|
- cmd == "disable"
|
||||||
|
- matching_nodes | length > 0
|
@ -132,3 +132,16 @@ kolla_ironic_pxe_append_params_extra: []
|
|||||||
kolla_ironic_pxe_append_params: >
|
kolla_ironic_pxe_append_params: >
|
||||||
{{ kolla_ironic_pxe_append_params_default +
|
{{ kolla_ironic_pxe_append_params_default +
|
||||||
kolla_ironic_pxe_append_params_extra }}
|
kolla_ironic_pxe_append_params_extra }}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Ironic Node Configuration
|
||||||
|
|
||||||
|
# This defines the start of the range of TCP ports to used for the IPMI socat
|
||||||
|
# serial consoles
|
||||||
|
ironic_serial_console_tcp_pool_start: 30000
|
||||||
|
|
||||||
|
# This defines the end of the range of TCP ports to used for the IPMI socat
|
||||||
|
# serial consoles
|
||||||
|
ironic_serial_console_tcp_pool_end: 31000
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
13
ansible/roles/console-allocation/defaults/main.yml
Normal file
13
ansible/roles/console-allocation/defaults/main.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
# Path to file in which to store console allocations.
|
||||||
|
console_allocation_filename:
|
||||||
|
|
||||||
|
# List of Names or UUIDs corresponding to Ironic nodes that you want to allocate
|
||||||
|
# serial consoles for
|
||||||
|
console_allocation_ironic_nodes: []
|
||||||
|
|
||||||
|
# allocation_pool_start: First TCP port in the allocation pool
|
||||||
|
console_allocation_pool_start:
|
||||||
|
|
||||||
|
# allocation_pool_end: Last TCP port in the allocation pool
|
||||||
|
console_allocation_pool_end:
|
192
ansible/roles/console-allocation/library/console_allocation.py
Normal file
192
ansible/roles/console-allocation/library/console_allocation.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Copyright (c) 2017 StackHPC Ltd.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
# TODO(wszumski): If we have multiple conductors and they are on different machines
|
||||||
|
# we could make a pool per machine.
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
module: console_allocation
|
||||||
|
short_description: Allocate a serial console TCP port for an Ironic node from a pool
|
||||||
|
author: Mark Goddard (mark@stackhpc.com) and Will Szumski (will@stackhpc.com)
|
||||||
|
options:
|
||||||
|
- option-name: nodes
|
||||||
|
description: List of Names or UUIDs corresponding to Ironic Nodes
|
||||||
|
required: True
|
||||||
|
type: list
|
||||||
|
- option-name: allocation_pool_start
|
||||||
|
description: First address of the pool from which to allocate
|
||||||
|
required: True
|
||||||
|
type: int
|
||||||
|
- option-name: allocation_pool_end
|
||||||
|
description: Last address of the pool from which to allocate
|
||||||
|
required: True
|
||||||
|
type: int
|
||||||
|
- option-name: allocation_file
|
||||||
|
description: >
|
||||||
|
Path to a file in which to store the allocations. Will be created if it
|
||||||
|
does not exist.
|
||||||
|
required: True
|
||||||
|
type: string
|
||||||
|
requirements:
|
||||||
|
- PyYAML
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = """
|
||||||
|
- name: Ensure Ironic node has a TCP port assigned for it's serial console
|
||||||
|
console_allocation:
|
||||||
|
nodes: ['node-1', 'node-2']
|
||||||
|
allocation_pool_start: 30000
|
||||||
|
allocation_pool_end: 31000
|
||||||
|
allocation_file: /path/to/allocation/file.yml
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
ports:
|
||||||
|
description: >
|
||||||
|
A dictionary mapping the node name to the allocated serial console TCP port
|
||||||
|
returned: success
|
||||||
|
type: dict
|
||||||
|
sample: { 'node1' : 30000, 'node2':300001 }
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import *
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Store a list of import errors to report to the user.
|
||||||
|
IMPORT_ERRORS=[]
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except Exception as e:
|
||||||
|
IMPORT_ERRORS.append(e)
|
||||||
|
|
||||||
|
|
||||||
|
def read_allocations(module):
|
||||||
|
"""Read TCP port allocations from the allocation file."""
|
||||||
|
filename = module.params['allocation_file']
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
content = yaml.load(f)
|
||||||
|
except IOError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
# Ignore ENOENT - we will create the file.
|
||||||
|
return {}
|
||||||
|
module.fail_json(msg="Failed to open allocation file %s for reading" % filename)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
module.fail_json(msg="Failed to parse allocation file %s as YAML" % filename)
|
||||||
|
if content is None:
|
||||||
|
# If the file is empty, yaml.load() will return None.
|
||||||
|
content = {}
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def write_allocations(module, allocations):
|
||||||
|
"""Write TCP port allocations to the allocation file."""
|
||||||
|
filename = module.params['allocation_file']
|
||||||
|
try:
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
yaml.dump(allocations, f, default_flow_style=False)
|
||||||
|
except IOError as e:
|
||||||
|
module.fail_json(msg="Failed to open allocation file %s for writing" % filename)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
module.fail_json(msg="Failed to dump allocation file %s as YAML" % filename)
|
||||||
|
|
||||||
|
def is_valid_port(port):
|
||||||
|
try:
|
||||||
|
int(port)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
if port < 0:
|
||||||
|
return False
|
||||||
|
if port > 65535:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update_allocation(module, allocations):
|
||||||
|
"""Allocate a TCP port of an Ironic serial console.
|
||||||
|
|
||||||
|
:param module: AnsibleModule instance
|
||||||
|
:param allocations: Existing IP address allocations
|
||||||
|
"""
|
||||||
|
nodes = module.params['nodes']
|
||||||
|
|
||||||
|
allocation_pool_start = module.params['allocation_pool_start']
|
||||||
|
allocation_pool_end = module.params['allocation_pool_end']
|
||||||
|
result = {
|
||||||
|
'changed': False,
|
||||||
|
'ports': {}
|
||||||
|
}
|
||||||
|
object_name = "serial_console_allocations"
|
||||||
|
console_allocations = allocations.setdefault(object_name, {})
|
||||||
|
invalid_allocations = {node: port for node, port in console_allocations.items()
|
||||||
|
if not is_valid_port(port)}
|
||||||
|
if invalid_allocations:
|
||||||
|
module.fail_json(msg="Found invalid existing allocations in %s: %s" %
|
||||||
|
(object_name,
|
||||||
|
", ".join("%s: %s" % (node, port)
|
||||||
|
for node, port in invalid_allocations.items())))
|
||||||
|
|
||||||
|
allocated_consoles = { int(x) for x in console_allocations.values() }
|
||||||
|
allocation_pool = { x for x in range(allocation_pool_start, allocation_pool_end + 1) }
|
||||||
|
free_ports = list(allocation_pool - allocated_consoles)
|
||||||
|
free_ports.sort(reverse=True)
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
if node not in console_allocations:
|
||||||
|
if len(free_ports) < 1:
|
||||||
|
module.fail_json(msg="No unallocated TCP ports for %s in %s" % (node, object_name))
|
||||||
|
result['changed'] = True
|
||||||
|
free_port = free_ports.pop()
|
||||||
|
console_allocations[node] = free_port
|
||||||
|
result['ports'][node] = console_allocations[node]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def allocate(module):
|
||||||
|
"""Allocate a TCP port an ironic serial console, updating the allocation file."""
|
||||||
|
allocations = read_allocations(module)
|
||||||
|
result = update_allocation(module, allocations)
|
||||||
|
if result['changed'] and not module.check_mode:
|
||||||
|
write_allocations(module, allocations)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=dict(
|
||||||
|
nodes=dict(required=True, type='list'),
|
||||||
|
allocation_pool_start=dict(required=True, type='int'),
|
||||||
|
allocation_pool_end=dict(required=True, type='int'),
|
||||||
|
allocation_file=dict(required=True, type='str'),
|
||||||
|
),
|
||||||
|
supports_check_mode=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fail if there were any exceptions when importing modules.
|
||||||
|
if IMPORT_ERRORS:
|
||||||
|
module.fail_json(msg="Import errors: %s" %
|
||||||
|
", ".join([repr(e) for e in IMPORT_ERRORS]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = allocate(module)
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg="Failed to allocate TCP port: %s" % repr(e))
|
||||||
|
else:
|
||||||
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
73
ansible/roles/console-allocation/tasks/main.yml
Normal file
73
ansible/roles/console-allocation/tasks/main.yml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
# Facts may not be available for the Ansible control host, so read the OS
|
||||||
|
# release manually.
|
||||||
|
- name: Check the OS release
|
||||||
|
local_action:
|
||||||
|
module: shell . /etc/os-release && echo $ID
|
||||||
|
changed_when: False
|
||||||
|
register: console_allocation_os_release
|
||||||
|
|
||||||
|
- name: Include RedHat family-specific variables
|
||||||
|
include_vars: "RedHat.yml"
|
||||||
|
when: console_allocation_os_release.stdout in ['centos', 'fedora', 'rhel']
|
||||||
|
|
||||||
|
- name: Include Debian family-specific variables
|
||||||
|
include_vars: "Debian.yml"
|
||||||
|
when: console_allocation_os_release.stdout in ['debian', 'ubuntu']
|
||||||
|
|
||||||
|
# Note: Currently we install these using the system package manager rather than
|
||||||
|
# pip to a virtualenv. This is because Yum is required elsewhere and cannot
|
||||||
|
# easily be installed in a virtualenv.
|
||||||
|
- name: Ensure package dependencies are installed
|
||||||
|
local_action:
|
||||||
|
module: package
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: installed
|
||||||
|
use: "{{ console_allocation_package_manager }}"
|
||||||
|
become: True
|
||||||
|
with_items: "{{ console_allocation_package_dependencies }}"
|
||||||
|
run_once: True
|
||||||
|
|
||||||
|
- name: Validate allocation pool start
|
||||||
|
vars:
|
||||||
|
port: "{{ console_allocation_pool_start | int(default=-1) }}"
|
||||||
|
fail:
|
||||||
|
msg: >-
|
||||||
|
You must must define an console_allocation_pool_start. This should
|
||||||
|
be a valid TCP port.
|
||||||
|
when: >-
|
||||||
|
console_allocation_pool_end is none or
|
||||||
|
port | int < 0 or port | int > 65535
|
||||||
|
|
||||||
|
- name: Validate allocation pool end
|
||||||
|
vars:
|
||||||
|
port: "{{ console_allocation_pool_end | int(default=-1) }}"
|
||||||
|
fail:
|
||||||
|
msg: >-
|
||||||
|
You must must define an console_allocation_pool_end. This should
|
||||||
|
be a valid TCP port.
|
||||||
|
when: >-
|
||||||
|
console_allocation_pool_end is none or
|
||||||
|
port | int < 0 or port | int > 65535
|
||||||
|
|
||||||
|
- name: Validate that allocation start is less than allocation end
|
||||||
|
fail:
|
||||||
|
msg: >-
|
||||||
|
console_allocation_start and console_allocation_end define a range
|
||||||
|
of TCP ports. You have defined a range with a start that is less than
|
||||||
|
the end
|
||||||
|
when:
|
||||||
|
- (console_allocation_pool_start | int) > (console_allocation_pool_end | int)
|
||||||
|
|
||||||
|
- name: Ensure Ironic serial console ports are allocated
|
||||||
|
local_action:
|
||||||
|
module: console_allocation
|
||||||
|
allocation_file: "{{ console_allocation_filename }}"
|
||||||
|
nodes: "{{ console_allocation_ironic_nodes }}"
|
||||||
|
allocation_pool_start: "{{ console_allocation_pool_start }}"
|
||||||
|
allocation_pool_end: "{{ console_allocation_pool_end }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Register a fact containing the console allocation result
|
||||||
|
set_fact:
|
||||||
|
console_allocation_result: "{{ result }}"
|
7
ansible/roles/console-allocation/vars/Debian.yml
Normal file
7
ansible/roles/console-allocation/vars/Debian.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
# Package manager to use.
|
||||||
|
console_allocation_package_manager: apt
|
||||||
|
|
||||||
|
# List of packages to install.
|
||||||
|
console_allocation_package_dependencies:
|
||||||
|
- python-yaml
|
7
ansible/roles/console-allocation/vars/RedHat.yml
Normal file
7
ansible/roles/console-allocation/vars/RedHat.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
# Package manager to use.
|
||||||
|
console_allocation_package_manager: yum
|
||||||
|
|
||||||
|
# List of packages to install.
|
||||||
|
console_allocation_package_dependencies:
|
||||||
|
- PyYAML
|
@ -203,6 +203,53 @@ according to their inventory host names, you can run the following command::
|
|||||||
This command will use the ``ipmi_address`` host variable from the inventory
|
This command will use the ``ipmi_address`` host variable from the inventory
|
||||||
to map the inventory host name to the correct node.
|
to map the inventory host name to the correct node.
|
||||||
|
|
||||||
|
|
||||||
|
Ironic Serial Console
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
To access the baremetal nodes from within Horizon you need to enable the serial
|
||||||
|
console. For this to work the you must set ``kolla_enable_nova_serialconsole_proxy``
|
||||||
|
to ``true`` in ``etc/kayobe/kolla.yml``::
|
||||||
|
|
||||||
|
kolla_enable_nova_serialconsole_proxy: true
|
||||||
|
|
||||||
|
The console interface on the Ironic nodes is expected to be ``ipmitool-socat``, you
|
||||||
|
can check this with::
|
||||||
|
|
||||||
|
openstack baremetal node show <node_id> --fields console_interface
|
||||||
|
|
||||||
|
where <node_id> should be the UUID or name of the Ironic node you want to check.
|
||||||
|
|
||||||
|
If you have set ``kolla_ironic_enabled_console_interfaces`` in ``etc/kayobe/ironic.yml``,
|
||||||
|
it should include ``ipmitool-socat`` in the list of enabled interfaces.
|
||||||
|
|
||||||
|
The playbook to enable the serial console currently only works if the Ironic node
|
||||||
|
name matches the inventory hostname.
|
||||||
|
|
||||||
|
Once these requirements have been satisfied, you can run::
|
||||||
|
|
||||||
|
(kayobe) $ kayobe baremetal compute serial console enable
|
||||||
|
|
||||||
|
This will reserve a TCP port for each node to use for the serial console interface.
|
||||||
|
The allocations are stored in ``${KAYOBE_CONFIG_PATH}/console-allocation.yml``. The
|
||||||
|
current implementation uses a global pool, which is specified by
|
||||||
|
``ironic_serial_console_tcp_pool_start`` and ``ironic_serial_console_tcp_pool_end``;
|
||||||
|
these variables can set in ``etc/kayobe/ironic.yml``.
|
||||||
|
|
||||||
|
To disable the serial console you can use::
|
||||||
|
|
||||||
|
(kayobe) $ kayobe baremetal compute serial console disable
|
||||||
|
|
||||||
|
The port allocated for each node is retained and must be manually removed from
|
||||||
|
``${KAYOBE_CONFIG_PATH}/console-allocation.yml`` if you want it to be reused by another
|
||||||
|
Ironic node with a different name.
|
||||||
|
|
||||||
|
You can optionally limit the nodes targeted by setting ``baremetal-compute-limit``::
|
||||||
|
|
||||||
|
(kayobe) $ kayobe baremetal compute serial console enable --baremetal-compute-limit sand-6-1
|
||||||
|
|
||||||
|
which should take the form of an `ansible host pattern <https://docs.ansible.com/ansible/latest/user_guide/intro_patterns.html>`_.
|
||||||
|
|
||||||
.. _update_deployment_image:
|
.. _update_deployment_image:
|
||||||
|
|
||||||
Update Deployment Image
|
Update Deployment Image
|
||||||
|
@ -106,6 +106,17 @@
|
|||||||
# List of kernel parameters to append for baremetal PXE boot.
|
# List of kernel parameters to append for baremetal PXE boot.
|
||||||
#kolla_ironic_pxe_append_params:
|
#kolla_ironic_pxe_append_params:
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Ironic Node Configuration
|
||||||
|
|
||||||
|
# This defines the start of the range of TCP ports to used for the IPMI socat
|
||||||
|
# serial consoles
|
||||||
|
#ironic_serial_console_tcp_pool_start:
|
||||||
|
|
||||||
|
# This defines the end of the range of TCP ports to used for the IPMI socat
|
||||||
|
# serial consoles
|
||||||
|
#ironic_serial_console_tcp_pool_end:
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Dummy variable to allow Ansible to accept this file.
|
# Dummy variable to allow Ansible to accept this file.
|
||||||
workaround_ansible_issue_8743: yes
|
workaround_ansible_issue_8743: yes
|
||||||
|
@ -1247,6 +1247,57 @@ class BaremetalComputeRename(KayobeAnsibleMixin, VaultMixin, Command):
|
|||||||
self.run_kayobe_playbooks(parsed_args, playbooks)
|
self.run_kayobe_playbooks(parsed_args, playbooks)
|
||||||
|
|
||||||
|
|
||||||
|
class BaremetalComputeSerialConsoleBase(KayobeAnsibleMixin, VaultMixin,
|
||||||
|
Command):
|
||||||
|
|
||||||
|
"""Base class for the baremetal serial console commands"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_limit(parsed_args, extra_vars):
|
||||||
|
if parsed_args.baremetal_compute_limit:
|
||||||
|
extra_vars["console_compute_node_limit"] = (
|
||||||
|
parsed_args.baremetal_compute_limit
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(BaremetalComputeSerialConsoleBase, self).get_parser(
|
||||||
|
prog_name)
|
||||||
|
group = parser.add_argument_group("Baremetal Serial Consoles")
|
||||||
|
group.add_argument("--baremetal-compute-limit",
|
||||||
|
help="Limit the change to the hosts specified in "
|
||||||
|
"this limit"
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
class BaremetalComputeSerialConsoleEnable(BaremetalComputeSerialConsoleBase):
|
||||||
|
"""Enable Serial Console for Baremetal Compute Nodes"""
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.LOG.debug("Enabling serial console for ironic nodes")
|
||||||
|
extra_vars = {}
|
||||||
|
BaremetalComputeSerialConsoleBase.process_limit(parsed_args,
|
||||||
|
extra_vars)
|
||||||
|
extra_vars["cmd"] = "enable"
|
||||||
|
playbooks = _build_playbook_list("baremetal-compute-serial-console")
|
||||||
|
self.run_kayobe_playbooks(parsed_args, playbooks,
|
||||||
|
extra_vars=extra_vars)
|
||||||
|
|
||||||
|
|
||||||
|
class BaremetalComputeSerialConsoleDisable(BaremetalComputeSerialConsoleBase):
|
||||||
|
"""Disable Serial Console for Baremetal Compute Nodes"""
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
self.app.LOG.debug("Disable serial console for ironic nodes")
|
||||||
|
extra_vars = {}
|
||||||
|
BaremetalComputeSerialConsoleBase.process_limit(parsed_args,
|
||||||
|
extra_vars)
|
||||||
|
extra_vars["cmd"] = "disable"
|
||||||
|
playbooks = _build_playbook_list("baremetal-compute-serial-console")
|
||||||
|
self.run_kayobe_playbooks(parsed_args, playbooks,
|
||||||
|
extra_vars=extra_vars)
|
||||||
|
|
||||||
|
|
||||||
class BaremetalComputeUpdateDeploymentImage(KayobeAnsibleMixin, VaultMixin,
|
class BaremetalComputeUpdateDeploymentImage(KayobeAnsibleMixin, VaultMixin,
|
||||||
Command):
|
Command):
|
||||||
"""Update the Ironic nodes to use the new kernel and ramdisk images."""
|
"""Update the Ironic nodes to use the new kernel and ramdisk images."""
|
||||||
|
@ -1150,6 +1150,100 @@ class TestCase(unittest.TestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(expected_calls, mock_run.call_args_list)
|
self.assertEqual(expected_calls, mock_run.call_args_list)
|
||||||
|
|
||||||
|
@mock.patch.object(commands.KayobeAnsibleMixin,
|
||||||
|
"run_kayobe_playbooks")
|
||||||
|
def test_baremetal_compute_serial_console_enable(self, mock_run):
|
||||||
|
command = commands.BaremetalComputeSerialConsoleEnable(TestApp(), [])
|
||||||
|
parser = command.get_parser("test")
|
||||||
|
parsed_args = parser.parse_args([])
|
||||||
|
result = command.run(parsed_args)
|
||||||
|
self.assertEqual(0, result)
|
||||||
|
expected_calls = [
|
||||||
|
mock.call(
|
||||||
|
mock.ANY,
|
||||||
|
[
|
||||||
|
"ansible/baremetal-compute-serial-console.yml",
|
||||||
|
|
||||||
|
],
|
||||||
|
extra_vars={
|
||||||
|
"cmd": "enable",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.assertEqual(expected_calls, mock_run.call_args_list)
|
||||||
|
|
||||||
|
@mock.patch.object(commands.KayobeAnsibleMixin,
|
||||||
|
"run_kayobe_playbooks")
|
||||||
|
def test_baremetal_compute_serial_console_enable_with_limit(self,
|
||||||
|
mock_run):
|
||||||
|
command = commands.BaremetalComputeSerialConsoleEnable(TestApp(), [])
|
||||||
|
parser = command.get_parser("test")
|
||||||
|
parsed_args = parser.parse_args(["--baremetal-compute-limit",
|
||||||
|
"sand-6-1"])
|
||||||
|
result = command.run(parsed_args)
|
||||||
|
self.assertEqual(0, result)
|
||||||
|
expected_calls = [
|
||||||
|
mock.call(
|
||||||
|
mock.ANY,
|
||||||
|
[
|
||||||
|
"ansible/baremetal-compute-serial-console.yml",
|
||||||
|
|
||||||
|
],
|
||||||
|
extra_vars={
|
||||||
|
"cmd": "enable",
|
||||||
|
"console_compute_node_limit": "sand-6-1",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.assertEqual(expected_calls, mock_run.call_args_list)
|
||||||
|
|
||||||
|
@mock.patch.object(commands.KayobeAnsibleMixin,
|
||||||
|
"run_kayobe_playbooks")
|
||||||
|
def test_baremetal_compute_serial_console_disable(self, mock_run):
|
||||||
|
command = commands.BaremetalComputeSerialConsoleDisable(TestApp(), [])
|
||||||
|
parser = command.get_parser("test")
|
||||||
|
parsed_args = parser.parse_args([])
|
||||||
|
result = command.run(parsed_args)
|
||||||
|
self.assertEqual(0, result)
|
||||||
|
expected_calls = [
|
||||||
|
mock.call(
|
||||||
|
mock.ANY,
|
||||||
|
[
|
||||||
|
"ansible/baremetal-compute-serial-console.yml",
|
||||||
|
|
||||||
|
],
|
||||||
|
extra_vars={
|
||||||
|
"cmd": "disable",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.assertEqual(expected_calls, mock_run.call_args_list)
|
||||||
|
|
||||||
|
@mock.patch.object(commands.KayobeAnsibleMixin,
|
||||||
|
"run_kayobe_playbooks")
|
||||||
|
def test_baremetal_compute_serial_console_disable_with_limit(self,
|
||||||
|
mock_run):
|
||||||
|
command = commands.BaremetalComputeSerialConsoleDisable(TestApp(), [])
|
||||||
|
parser = command.get_parser("test")
|
||||||
|
parsed_args = parser.parse_args(["--baremetal-compute-limit",
|
||||||
|
"sand-6-1"])
|
||||||
|
result = command.run(parsed_args)
|
||||||
|
self.assertEqual(0, result)
|
||||||
|
expected_calls = [
|
||||||
|
mock.call(
|
||||||
|
mock.ANY,
|
||||||
|
[
|
||||||
|
"ansible/baremetal-compute-serial-console.yml",
|
||||||
|
|
||||||
|
],
|
||||||
|
extra_vars={
|
||||||
|
"cmd": "disable",
|
||||||
|
"console_compute_node_limit": "sand-6-1",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.assertEqual(expected_calls, mock_run.call_args_list)
|
||||||
|
|
||||||
@mock.patch.object(commands.KayobeAnsibleMixin,
|
@mock.patch.object(commands.KayobeAnsibleMixin,
|
||||||
"run_kayobe_playbooks")
|
"run_kayobe_playbooks")
|
||||||
def test_baremetal_compute_update_deployment_image(self, mock_run):
|
def test_baremetal_compute_update_deployment_image(self, mock_run):
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Added commands to enable and disable the Ironic serial console.
|
||||||
|
This allows you to use the serial console from within Horizon.
|
@ -33,6 +33,8 @@ kayobe.cli=
|
|||||||
baremetal_compute_provide = kayobe.cli.commands:BaremetalComputeProvide
|
baremetal_compute_provide = kayobe.cli.commands:BaremetalComputeProvide
|
||||||
baremetal_compute_rename = kayobe.cli.commands:BaremetalComputeRename
|
baremetal_compute_rename = kayobe.cli.commands:BaremetalComputeRename
|
||||||
baremetal_compute_update_deployment_image = kayobe.cli.commands:BaremetalComputeUpdateDeploymentImage
|
baremetal_compute_update_deployment_image = kayobe.cli.commands:BaremetalComputeUpdateDeploymentImage
|
||||||
|
baremetal_compute_serial_console_enable = kayobe.cli.commands:BaremetalComputeSerialConsoleEnable
|
||||||
|
baremetal_compute_serial_console_disable = kayobe.cli.commands:BaremetalComputeSerialConsoleDisable
|
||||||
control_host_bootstrap = kayobe.cli.commands:ControlHostBootstrap
|
control_host_bootstrap = kayobe.cli.commands:ControlHostBootstrap
|
||||||
control_host_upgrade = kayobe.cli.commands:ControlHostUpgrade
|
control_host_upgrade = kayobe.cli.commands:ControlHostUpgrade
|
||||||
configuration_dump = kayobe.cli.commands:ConfigurationDump
|
configuration_dump = kayobe.cli.commands:ConfigurationDump
|
||||||
|
Loading…
Reference in New Issue
Block a user