Support Ironic inspector in Bifrost container

Adds support for setting which ports to add during inspection and a
role for creating introspection rules in inspector.
This commit is contained in:
Mark Goddard 2017-03-03 14:19:53 +00:00
parent e7c3e7c94b
commit b235bcf65a
8 changed files with 266 additions and 0 deletions

View File

@ -0,0 +1,11 @@
---
- name: Ensure introspection rules are registered in Bifrost
hosts: seed
roles:
- role: ironic-inspector-rules
ironic_inspector_venv: "{{ ansible_env.PWD }}/shade-venv"
# No auth required for Bifrost.
ironic_inspector_auth_type: None
ironic_inspector_auth: {}
ironic_inspector_url: "http://localhost:5050"
ironic_inspector_rules: "{{ kolla_bifrost_inspector_rules }}"

View File

@ -32,6 +32,16 @@ kolla_bifrost_dib_packages: []
# Whether to enable ipmitool-based drivers.
kolla_bifrost_enable_ipmitool_drivers: true
###############################################################################
# Ironic Inspector configuration.
# Which MAC addresses to add as ports during introspection. One of 'all',
# 'active' or 'pxe'.
kolla_bifrost_inspector_port_addition: "all"
# List of introspection rules for Bifrost's Ironic Inspector service.
kolla_bifrost_inspector_rules: []
###############################################################################
# Inventory configuration.

View File

@ -0,0 +1,16 @@
---
# Path to a directory in which to create a virtualenv.
ironic_inspector_venv:
# Authentication type.
ironic_inspector_auth_type:
# Authentication information.
ironic_inspector_auth: {}
# URL of Ironic Inspector API endpoint.
ironic_inspector_url:
# List of rules which should exist. See the Inspector rules API for details of
# parameters available for rules.
ironic_inspector_rules: []

View File

@ -0,0 +1,159 @@
#!/usr/bin/python
from ansible.module_utils.basic import *
from ansible.module_utils.openstack import *
# Store a list of import errors to report to the user.
IMPORT_ERRORS = []
try:
import ironic_inspector_client
except Exception as e:
IMPORT_ERRORS.append(e)
try:
import shade
except Exception as e:
IMPORT_ERRORS.append(e)
DOCUMENTATION = """
module: os_ironic_inspector_rule
short_description: Create or destroy an Ironic Inspector rule.
author: "Mark Goddard <mark@stackhpc.com>"
extends_documentation_fragment: openstack
description:
- Create or destroy an Ironic inspector rule.
options:
state:
description:
- State of the rule
choices: ["present", "absent"]
uuid:
description:
- Globally unique identifier for the rule.
required: false
description:
description:
- Description for the rule.
required: false
conditions:
description:
- List of conditions that must be met in order to apply the rule.
required: true
actions:
description:
- List of actions to be taken when the conditions are met.
required: true
"""
EXAMPLES = """
# Ensure that an inspector rule exists.
os_ironic_inspector_rule:
cloud: "openstack"
state: present
uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69"
description: Set IPMI username in driver_info if not set
conditions:
- field: "node://driver_info.ipmi_username"
op: "is-empty"
actions:
- action: "set-attribute"
path: "driver_info/ipmi_username"
value: "root"
"""
def _build_client(module):
"""Create and return an Ironic inspector client."""
cloud = shade.operator_cloud(**module.params)
session = cloud.cloud_config.get_session()
client = ironic_inspector_client.v1.ClientV1(
inspector_url=module.params['inspector_url'],
session=session, region_name=module.params['region_name'])
return client
def _ensure_rule_present(module, client):
"""Ensure that an inspector rule is present."""
if module.params['uuid']:
try:
rule = client.rules.get(module.params['uuid'])
except ironic_inspector_client.ClientError as e:
if e.response.status_code != 404:
module.fail_json(msg="Failed retrieving Inspector rule %s: %s"
% (module.params['uuid'], repr(e)))
else:
# Check whether the rule differs from the request.
keys = ('conditions', 'actions', 'description')
for key in keys:
if rule[key] != module.params[key]:
break
else:
# Nothing to do - rule exists and is as requested.
return False
# Rule differs - delete it before recreating.
_ensure_rule_absent(module, client)
client.rules.create(module.params['conditions'], module.params['actions'],
module.params['uuid'], module.params['description'])
return True
def _ensure_rule_absent(module, client):
"""Ensure that an inspector rule is absent."""
if not module.params['uuid']:
module.fail_json(msg="UUID is required to ensure rules are absent")
try:
client.rules.delete(module.params['uuid'])
except ironic_inspector_client.ClientError as e:
# If the rule does not exist, no problem and no change.
if e.response.status_code == 404:
return False
module.fail_json(msg="Failed retrieving Inspector rule %s: %s"
% (module.params['uuid'], repr(e)))
return True
def main():
argument_spec = openstack_full_argument_spec(
conditions=dict(type='list', required=True),
actions=dict(type='list', required=True),
description=dict(required=False),
uuid=dict(required=False),
state=dict(required=False, default='present',
choices=['present', 'absent']),
inspector_url=dict(required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
# 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]))
if (module.params['auth_type'] in [None, 'None'] and
module.params['inspector_url'] is None):
module.fail_json(msg="Authentication appears disabled, please "
"define an inspector_url parameter")
if (module.params['inspector_url'] and
module.params['auth_type'] in [None, 'None']):
module.params['auth'] = dict(
endpoint=module.params['inspector_url']
)
try:
client = _build_client(module)
if module.params["state"] == "present":
changed = _ensure_rule_present(module, client)
else:
changed = _ensure_rule_absent(module, client)
except Exception as e:
module.fail_json(msg="Failed to configure Ironic Inspector rule: %s" %
repr(e))
else:
module.exit_json(changed=changed)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,50 @@
---
- name: Ensure required packages are installed
yum:
name: "{{ item }}"
state: installed
become: True
with_items:
- python-devel
- python-pip
- python-virtualenv
- name: Ensure the latest version of pip is installed
pip:
name: "{{ item.name }}"
state: latest
virtualenv: "{{ ironic_inspector_venv }}"
with_items:
- { name: pip }
- name: Ensure required Python packages are installed
pip:
name: "{{ item.name }}"
version: "{{ item.version | default(omit) }}"
state: present
virtualenv: "{{ ironic_inspector_venv }}"
with_items:
- name: python-ironic-inspector-client
- name: shade
- name: Set a fact to ensure Ansible uses the python interpreter in the virtualenv
set_fact:
ansible_python_interpreter: "{{ ironic_inspector_venv }}/bin/python"
- name: Ensure introspection rules exist
os_ironic_inspector_rule:
auth_type: "{{ ironic_inspector_auth_type }}"
auth: "{{ ironic_inspector_auth }}"
conditions: "{{ item.conditions }}"
actions: "{{ item.actions }}"
description: "{{ item.description | default(omit) }}"
uuid: "{{ item.uuid | default(item.description | to_uuid) | default(omit) }}"
state: present
inspector_url: "{{ ironic_inspector_url }}"
with_items: "{{ ironic_inspector_rules }}"
# This variable is unset before we set it, and it does not appear to be
# possible to unset a variable in Ansible.
- name: Set a fact to reset the Ansible python interpreter
set_fact:
ansible_python_interpreter: /usr/bin/python

View File

@ -42,6 +42,10 @@ kolla_bifrost_dnsmasq_dns_servers: []
# DNS domain provided to nodes via DHCP.
kolla_bifrost_domain:
# Which MAC addresses to add as ports during introspection. One of 'all',
# 'active' or 'pxe'.
kolla_bifrost_inspector_port_addition:
# Server inventory to be configured in {{ kolla_node_custom_config_path }}/bifrost/servers.yml.
kolla_bifrost_servers: {}

View File

@ -27,6 +27,12 @@ dnsmasq_dns_servers: "{{ kolla_bifrost_dnsmasq_dns_servers | join(',') }}"
domain: "{{ kolla_bifrost_domain }}"
{% endif %}
{% if kolla_bifrost_inspector_port_addition %}
# Which MAC addresses to add as ports during introspection. One of 'all',
# 'active' or 'pxe'.
inspector_port_addition: "{{ kolla_bifrost_inspector_port_addition }}"
{% endif %}
{% if kolla_bifrost_extra_globals %}
###############################################################################
# Extra configuration

View File

@ -28,6 +28,16 @@
# Whether to enable ipmitool-based drivers.
#kolla_bifrost_enable_ipmitool_drivers:
###############################################################################
# Ironic Inspector configuration.
# Which MAC addresses to add as ports during introspection. One of 'all',
# 'active' or 'pxe'.
#kolla_bifrost_inspector_port_addition:
# List of introspection rules for Bifrost's Ironic Inspector service.
#kolla_bifrost_inspector_rules:
###############################################################################
# Inventory configuration.