metalsmith_deployment role switch to metalsmith_instances
Instead of wrapping the metalsmith CLI, the metalsmith_deployment role now uses the metalsmith_instances module. There are differences between the instances formats of the role and the module which are partially resolved with a simple transformation module called metalsmith_deployment_defaults. A 'candidates' attribute is added to metalsmith_instances, but keeping the single 'name' attribute to remain compatible with the TripleO usage. Unresolved differences between the 2 are described below: metalsmith_instances doesn't have a per-instance state attribute, instead it has one state attribute for all instances. I propose that support for per-instance state is dropped. This was never documented in the README.rst anyway. extra_args only applies to a CLI. Apart from --dry-run these arguments are either for output formatting or Ironic API authentication. I propose that this option is dropped. A metalsmith_debug arg is added to make the ouput more verbose. Change-Id: Ia30620821182c58050813e807cdde50a27d03c15
This commit is contained in:
parent
4de7115247
commit
a7bd8fe1ae
|
@ -56,6 +56,7 @@ class TestMetalsmithInstances(unittest.TestCase):
|
|||
provisioner = mock.Mock()
|
||||
instances = [{
|
||||
'name': 'node',
|
||||
'candidates': ['other_node'],
|
||||
'resource_class': 'boxen',
|
||||
'capabilities': {'foo': 'bar'},
|
||||
'traits': ['this', 'that'],
|
||||
|
@ -72,7 +73,7 @@ class TestMetalsmithInstances(unittest.TestCase):
|
|||
result = mi.reserve(provisioner, instances, True)
|
||||
provisioner.reserve_node.assert_has_calls([
|
||||
mock.call(
|
||||
candidates=['node'],
|
||||
candidates=['other_node', 'node'],
|
||||
capabilities={'foo': 'bar'},
|
||||
conductor_group='group',
|
||||
resource_class='boxen',
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
# Copyright 2020 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: metalsmith_deployment_defaults
|
||||
short_description: Transform instances list data for metalsmith_instances
|
||||
author: "Steve Baker (@stevebaker)"
|
||||
description:
|
||||
- Takes a list of instances from the metalsmith_deployment role
|
||||
and a dict of defaults and transforms that to the format required
|
||||
by the metalsmith_instances module.
|
||||
options:
|
||||
instances:
|
||||
description:
|
||||
- List of node description dicts to perform operations on (in the
|
||||
metalsmith_deployment instances format)
|
||||
type: list
|
||||
default: []
|
||||
elements: dict
|
||||
defaults:
|
||||
description:
|
||||
- Dict of defaults to use for missing values. Keys correspond to the
|
||||
metalsmith_deployment instances format.
|
||||
type: dict
|
||||
'''
|
||||
|
||||
|
||||
def transform(module, instances, defaults):
|
||||
mi = []
|
||||
|
||||
def value(src, key, dest, to_key=None):
|
||||
if not to_key:
|
||||
to_key = key
|
||||
value = src.get(key, defaults.get(key))
|
||||
if value:
|
||||
dest[to_key] = value
|
||||
|
||||
for src in instances:
|
||||
dest = {'image': {}}
|
||||
value(src, 'hostname', dest)
|
||||
value(src, 'candidates', dest)
|
||||
value(src, 'nics', dest)
|
||||
value(src, 'netboot', dest)
|
||||
value(src, 'root_size', dest, 'root_size_gb')
|
||||
value(src, 'swap_size', dest, 'swap_size_mb')
|
||||
value(src, 'capabilities', dest)
|
||||
value(src, 'traits', dest)
|
||||
value(src, 'resource_class', dest)
|
||||
value(src, 'conductor_group', dest)
|
||||
value(src, 'user_name', dest)
|
||||
image = dest['image']
|
||||
value(src, 'image', image, 'href')
|
||||
value(src, 'image_checksum', image, 'checksum')
|
||||
value(src, 'image_kernel', image, 'kernel')
|
||||
value(src, 'image_ramdisk', image, 'ramdisk')
|
||||
|
||||
# keys in metalsmith_instances not currently in metalsmith_deployment:
|
||||
# passwordless_sudo
|
||||
|
||||
# keys in metalsmith_deployment not currently in metalsmith_instances:
|
||||
# extra_args (CLI args cannot translate to the python lib,
|
||||
# but they are mainly for auth and output formatting apart
|
||||
# from --dry-run)
|
||||
if 'extra_args' in src:
|
||||
module.fail_json(
|
||||
changed=False,
|
||||
msg="extra_args is no longer supported"
|
||||
)
|
||||
|
||||
# state (metalsmith_instances has a single state attribute for every
|
||||
# instance)
|
||||
if 'state' in src:
|
||||
module.fail_json(
|
||||
changed=False,
|
||||
msg="Per-instance state is no longer supported, "
|
||||
"use variable metalsmith_state"
|
||||
)
|
||||
|
||||
# source keys could be a string or a list of strings
|
||||
# and the strings could be a path to a public key or the key contents.
|
||||
# Normalize this to a list of key contents
|
||||
keys = []
|
||||
source_keys = src.get('ssh_public_keys')
|
||||
if source_keys:
|
||||
if isinstance(source_keys, str):
|
||||
source_keys = [source_keys]
|
||||
for source_key in source_keys:
|
||||
if os.path.isfile(source_key):
|
||||
with open(source_key) as f:
|
||||
source_key = f.read()
|
||||
keys.append(source_key)
|
||||
if keys:
|
||||
dest['ssh_public_keys'] = keys
|
||||
|
||||
mi.append(dest)
|
||||
|
||||
module.exit_json(
|
||||
changed=False,
|
||||
msg="{} instances transformed".format(len(mi)),
|
||||
instances=mi
|
||||
)
|
||||
return mi
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
supports_check_mode=False,
|
||||
)
|
||||
|
||||
instances = module.params['instances']
|
||||
defaults = module.params['defaults']
|
||||
transform(module, instances, defaults)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -58,8 +58,14 @@ options:
|
|||
type: str
|
||||
name:
|
||||
description:
|
||||
- The name of an existing node to provision
|
||||
- The name of an existing node to provision, this name is appended
|
||||
to the candidates list
|
||||
type: str
|
||||
candidates:
|
||||
description:
|
||||
- List of nodes (UUIDs or names) to be considered for deployment
|
||||
type: list
|
||||
elements: str
|
||||
image:
|
||||
description:
|
||||
- Details of the image you want to provision onto the node
|
||||
|
@ -127,7 +133,6 @@ options:
|
|||
ssh_public_keys:
|
||||
description:
|
||||
- SSH public keys to load
|
||||
type: str
|
||||
resource_class:
|
||||
description:
|
||||
- Node resource class to provision
|
||||
|
@ -179,6 +184,7 @@ options:
|
|||
- Maximum number of instances to provision at once. Set to 0 to have no
|
||||
concurrency limit
|
||||
type: int
|
||||
default: 0
|
||||
log_level:
|
||||
description:
|
||||
- Set the logging level for the log which is available in the
|
||||
|
@ -238,12 +244,10 @@ def _get_source(instance):
|
|||
def reserve(provisioner, instances, clean_up):
|
||||
nodes = []
|
||||
for instance in instances:
|
||||
candidates = instance.get('candidates', [])
|
||||
if instance.get('name') is not None:
|
||||
# NOTE(dtantsur): metalsmith accepts list of instances to pick
|
||||
# from. We implement a simplest case when a user can pick a
|
||||
# node by its name (actually, UUID will also work).
|
||||
candidates = [instance['name']]
|
||||
else:
|
||||
candidates.append(instance['name'])
|
||||
if not candidates:
|
||||
candidates = None
|
||||
try:
|
||||
node = provisioner.reserve_node(
|
||||
|
|
|
@ -19,6 +19,8 @@ The following optional variables provide the defaults for Instance_ attributes:
|
|||
the default for ``capabilities``.
|
||||
``metalsmith_conductor_group``
|
||||
the default for ``conductor_group``.
|
||||
``metalsmith_debug``
|
||||
Show extra debug information, defaults to ``false``.
|
||||
``metalsmith_extra_args``
|
||||
the default for ``extra_args``.
|
||||
``metalsmith_image``
|
||||
|
@ -39,6 +41,9 @@ The following optional variables provide the defaults for Instance_ attributes:
|
|||
the default for ``root_size``.
|
||||
``metalsmith_ssh_public_keys``
|
||||
the default for ``ssh_public_keys``.
|
||||
``metalsmith_state``
|
||||
the default state for instances, valid values are ``reserved``, ``absent``
|
||||
or the default value ``present``.
|
||||
``metalsmith_swap_size``
|
||||
the default for ``swap_size``.
|
||||
``metalsmith_traits``
|
||||
|
@ -62,6 +67,7 @@ Each instances has the following attributes:
|
|||
|
||||
``extra_args`` (defaults to ``metalsmith_extra_args``)
|
||||
additional arguments to pass to the ``metalsmith`` CLI on all calls.
|
||||
(No longer supported, will raise an error if used)
|
||||
``image`` (defaults to ``metalsmith_image``)
|
||||
UUID, name or HTTP(s) URL of the image to use for deployment. Mandatory.
|
||||
``image_checksum`` (defaults to ``metalsmith_image_checksum``)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
metalsmith_candidates: []
|
||||
metalsmith_capabilities: {}
|
||||
metalsmith_conductor_group:
|
||||
metalsmith_debug: false
|
||||
metalsmith_extra_args:
|
||||
metalsmith_image_checksum:
|
||||
metalsmith_image_kernel:
|
||||
|
@ -11,6 +12,7 @@ metalsmith_nics: []
|
|||
metalsmith_resource_class:
|
||||
metalsmith_root_size:
|
||||
metalsmith_ssh_public_keys: []
|
||||
metalsmith_state: present
|
||||
metalsmith_swap_size:
|
||||
metalsmith_traits: []
|
||||
metalsmith_user_name: metalsmith
|
||||
|
|
|
@ -1,86 +1,42 @@
|
|||
---
|
||||
|
||||
- name: Start provisioning of instances
|
||||
command: >
|
||||
metalsmith {{ extra_args }} deploy --no-wait
|
||||
{% for cap_name, cap_value in capabilities.items() %}
|
||||
--capability {{ cap_name }}={{ cap_value }}
|
||||
{% endfor %}
|
||||
{% for trait in traits %}
|
||||
--trait {{ trait }}
|
||||
{% endfor %}
|
||||
{% for nic in nics %}
|
||||
{% for nic_type, nic_value in nic.items() %}
|
||||
--{{ nic_type }} {{ nic_value }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% if root_size %}
|
||||
--root-size {{ root_size }}
|
||||
{% endif %}
|
||||
{% if swap_size %}
|
||||
--swap-size {{ swap_size }}
|
||||
{% endif %}
|
||||
{% for ssh_key in ssh_public_keys %}
|
||||
--ssh-public-key {{ ssh_key }}
|
||||
{% endfor %}
|
||||
--image {{ image }}
|
||||
{% if image_checksum %}
|
||||
--image-checksum {{ image_checksum }}
|
||||
{% endif %}
|
||||
{% if image_kernel %}
|
||||
--image-kernel {{ image_kernel }}
|
||||
{% endif %}
|
||||
{% if image_ramdisk %}
|
||||
--image-ramdisk {{ image_ramdisk }}
|
||||
{% endif %}
|
||||
--hostname {{ instance.hostname }}
|
||||
{% if netboot %}
|
||||
--netboot
|
||||
{% endif %}
|
||||
{% if user_name %}
|
||||
--user-name {{ user_name }}
|
||||
{% endif %}
|
||||
{% if resource_class %}
|
||||
--resource-class {{ resource_class }}
|
||||
{% endif %}
|
||||
{% if conductor_group %}
|
||||
--conductor-group {{ conductor_group }}
|
||||
{% endif %}
|
||||
{% for node in candidates %}
|
||||
--candidate {{ node }}
|
||||
{% endfor %}
|
||||
when: state == 'present'
|
||||
vars:
|
||||
candidates: "{{ instance.candidates | default(metalsmith_candidates) }}"
|
||||
capabilities: "{{ instance.capabilities | default(metalsmith_capabilities) }}"
|
||||
conductor_group: "{{ instance.conductor_group | default(metalsmith_conductor_group) }}"
|
||||
extra_args: "{{ instance.extra_args | default(metalsmith_extra_args) }}"
|
||||
image: "{{ instance.image | default(metalsmith_image) }}"
|
||||
image_checksum: "{{ instance.image_checksum | default(metalsmith_image_checksum) }}"
|
||||
image_kernel: "{{ instance.image_kernel | default(metalsmith_image_kernel) }}"
|
||||
image_ramdisk: "{{ instance.image_ramdisk | default(metalsmith_image_ramdisk) }}"
|
||||
netboot: "{{ instance.netboot | default(metalsmith_netboot) }}"
|
||||
nics: "{{ instance.nics | default(metalsmith_nics) }}"
|
||||
resource_class: "{{ instance.resource_class | default(metalsmith_resource_class) }}"
|
||||
root_size: "{{ instance.root_size | default(metalsmith_root_size) }}"
|
||||
ssh_public_keys: "{{ instance.ssh_public_keys | default(metalsmith_ssh_public_keys) }}"
|
||||
state: "{{ instance.state | default('present') }}"
|
||||
swap_size: "{{ instance.swap_size | default(metalsmith_swap_size) }}"
|
||||
traits: "{{ instance.traits | default(metalsmith_traits) }}"
|
||||
user_name: "{{ instance.user_name | default(metalsmith_user_name) }}"
|
||||
with_items: "{{ metalsmith_instances }}"
|
||||
loop_control:
|
||||
label: "{{ instance.hostname or instance }}"
|
||||
loop_var: instance
|
||||
- name: Build instance defaults
|
||||
metalsmith_deployment_defaults:
|
||||
instances: "{{ metalsmith_instances }}"
|
||||
defaults:
|
||||
candidates: "{{ metalsmith_candidates }}"
|
||||
capabilities: "{{ metalsmith_capabilities }}"
|
||||
conductor_group: "{{ metalsmith_conductor_group }}"
|
||||
extra_args: "{{ metalsmith_extra_args }}"
|
||||
image: "{{ metalsmith_image }}"
|
||||
image_checksum: "{{ metalsmith_image_checksum }}"
|
||||
image_kernel: "{{ metalsmith_image_kernel }}"
|
||||
image_ramdisk: "{{ metalsmith_image_ramdisk }}"
|
||||
netboot: "{{ metalsmith_netboot }}"
|
||||
nics: "{{ metalsmith_nics }}"
|
||||
resource_class: "{{ metalsmith_resource_class }}"
|
||||
root_size: "{{ metalsmith_root_size }}"
|
||||
ssh_public_keys: "{{ metalsmith_ssh_public_keys }}"
|
||||
swap_size: "{{ metalsmith_swap_size }}"
|
||||
traits: "{{ metalsmith_traits }}"
|
||||
user_name: "{{ metalsmith_user_name }}"
|
||||
register: instances
|
||||
|
||||
- name: Wait for provisioning of instances
|
||||
command: >
|
||||
metalsmith {{ metalsmith_extra_args }} wait
|
||||
{% if metalsmith_provisioning_timeout %}
|
||||
--timeout {{ metalsmith_provisioning_timeout }}
|
||||
{% endif %}
|
||||
{% for instance in metalsmith_instances %}
|
||||
{% if (instance.state | default('present')) == 'present' %}
|
||||
{{ instance.hostname }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
- name: Show instances data
|
||||
debug:
|
||||
msg: "{{ instances.instances | to_yaml }}"
|
||||
when: metalsmith_debug|bool
|
||||
|
||||
- name: Provision instances
|
||||
metalsmith_instances:
|
||||
instances: "{{ instances.instances }}"
|
||||
state: "{{ metalsmith_state }}"
|
||||
wait: true
|
||||
timeout: "{{ metalsmith_provisioning_timeout }}"
|
||||
log_level: "{{ 'debug' if metalsmith_debug|bool else 'info' }}"
|
||||
register: baremetal_reserved
|
||||
|
||||
- name: Metalsmith log for reserve instances
|
||||
debug:
|
||||
var: baremetal_reserved.logging
|
||||
when: metalsmith_debug|bool
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
include_role:
|
||||
name: metalsmith_deployment
|
||||
vars:
|
||||
metalsmith_extra_args: --debug
|
||||
metalsmith_debug: true
|
||||
metalsmith_resource_class: baremetal
|
||||
metalsmith_instances:
|
||||
- hostname: test
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../metalsmith_ansible/ansible_plugins/modules
|
Loading…
Reference in New Issue