rename_host instruction
This commit is contained in:
parent
84cc28b60f
commit
381970f626
2 changed files with 95 additions and 2 deletions
|
@ -118,6 +118,14 @@ used:
|
||||||
to write to the output. By default the message will be `fail requested`
|
to write to the output. By default the message will be `fail requested`
|
||||||
followed by the name of the current host.
|
followed by the name of the current host.
|
||||||
|
|
||||||
|
#### rename_host
|
||||||
|
|
||||||
|
This action changes the name of the current host. It can only be executed once
|
||||||
|
for each host. The actual renaming will occur after the script has completed
|
||||||
|
its execution. It requires the following field:
|
||||||
|
|
||||||
|
* `name`: the new name of the host.
|
||||||
|
|
||||||
#### set_fact
|
#### set_fact
|
||||||
|
|
||||||
This action sets an Ansible fact associated to the current host. The following
|
This action sets an Ansible fact associated to the current host. The following
|
||||||
|
|
|
@ -7,6 +7,7 @@ from ansible.errors import AnsibleParserError, AnsibleRuntimeError, AnsibleError
|
||||||
from ansible.inventory.helpers import get_group_vars
|
from ansible.inventory.helpers import get_group_vars
|
||||||
from ansible.module_utils.six import string_types
|
from ansible.module_utils.six import string_types
|
||||||
from ansible.module_utils.parsing.convert_bool import boolean
|
from ansible.module_utils.parsing.convert_bool import boolean
|
||||||
|
from ansible.parsing.utils import addresses
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin
|
from ansible.plugins.inventory import BaseInventoryPlugin
|
||||||
from ansible.utils.vars import isidentifier, combine_vars
|
from ansible.utils.vars import isidentifier, combine_vars
|
||||||
|
|
||||||
|
@ -82,6 +83,8 @@ DOCUMENTATION = """
|
||||||
Jinja templates.
|
Jinja templates.
|
||||||
- C(stop) stops processing the list of instructions for the current
|
- C(stop) stops processing the list of instructions for the current
|
||||||
host.
|
host.
|
||||||
|
- C(rename_host) changes a host's name. It can only be executed once per
|
||||||
|
host.
|
||||||
type: list
|
type: list
|
||||||
elements: dict
|
elements: dict
|
||||||
required: True
|
required: True
|
||||||
|
@ -104,6 +107,7 @@ INSTR_OWN_FIELDS = {
|
||||||
"block": ("block", "rescue", "always", "locals"),
|
"block": ("block", "rescue", "always", "locals"),
|
||||||
"create_group": ("group", "parent", "add_host"),
|
"create_group": ("group", "parent", "add_host"),
|
||||||
"fail": ("msg",),
|
"fail": ("msg",),
|
||||||
|
"rename_host": ("name",),
|
||||||
"set_fact": ("name", "value"),
|
"set_fact": ("name", "value"),
|
||||||
"set_var": ("name", "value"),
|
"set_var": ("name", "value"),
|
||||||
"stop": (),
|
"stop": (),
|
||||||
|
@ -915,6 +919,46 @@ class RciFail(RcInstruction):
|
||||||
raise AnsibleRuntimeError(message)
|
raise AnsibleRuntimeError(message)
|
||||||
|
|
||||||
|
|
||||||
|
class RciRenameHost(RcInstruction):
|
||||||
|
"""``rename_host`` instruction implementation."""
|
||||||
|
|
||||||
|
def __init__(self, inventory, templar, display):
|
||||||
|
super().__init__(inventory, templar, display, "rename_host")
|
||||||
|
self._name = None
|
||||||
|
self._name_may_be_template = None
|
||||||
|
|
||||||
|
def parse_action(self, record):
|
||||||
|
assert self._name is None and self._name_may_be_template is None
|
||||||
|
if "name" not in record:
|
||||||
|
raise AnsibleParserError("%s: missing 'name' field" % (self._action,))
|
||||||
|
name = record["name"]
|
||||||
|
if not isinstance(name, string_types):
|
||||||
|
raise AnsibleParserError("%s: 'name' must be a string" % (self._action,))
|
||||||
|
nmbt = self._templar.is_possibly_template(name)
|
||||||
|
if not (nmbt or addresses.patterns['hostname'].match(name)):
|
||||||
|
raise AnsibleParserError(
|
||||||
|
"%s: '%s' is not a valid host name" % (self._action, name)
|
||||||
|
)
|
||||||
|
self._name_may_be_template = nmbt
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
def execute_action(self, host_name, context):
|
||||||
|
if context.new_name is not None:
|
||||||
|
raise AnsibleRuntimeError("Host has already been renamed")
|
||||||
|
if self._name_may_be_template:
|
||||||
|
self._templar.available_variables = context.variables
|
||||||
|
name = self._templar.template(self._name)
|
||||||
|
if not addresses.patterns['hostname'].match(name):
|
||||||
|
raise AnsibleRuntimeError(
|
||||||
|
"%s: '%s' is not a valid host name" % (self._action, name)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
name = self._name
|
||||||
|
self._display.vvv("- renaming host %s to %s" % (host_name, name))
|
||||||
|
context.new_name = name
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class RciBlock(RcInstruction):
|
class RciBlock(RcInstruction):
|
||||||
"""``block`` instruction implementation."""
|
"""``block`` instruction implementation."""
|
||||||
|
|
||||||
|
@ -1042,6 +1086,7 @@ INSTRUCTIONS = {
|
||||||
"block": RciBlock,
|
"block": RciBlock,
|
||||||
"create_group": RciCreateGroup,
|
"create_group": RciCreateGroup,
|
||||||
"fail": RciFail,
|
"fail": RciFail,
|
||||||
|
"rename_host": RciRenameHost,
|
||||||
"set_fact": lambda i, t, d: RciSetVarOrFact(i, t, d, True),
|
"set_fact": lambda i, t, d: RciSetVarOrFact(i, t, d, True),
|
||||||
"set_var": lambda i, t, d: RciSetVarOrFact(i, t, d, False),
|
"set_var": lambda i, t, d: RciSetVarOrFact(i, t, d, False),
|
||||||
"stop": RciStop,
|
"stop": RciStop,
|
||||||
|
@ -1077,16 +1122,25 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
)
|
)
|
||||||
self.dump_program(instructions)
|
self.dump_program(instructions)
|
||||||
# Execute it for each host
|
# Execute it for each host
|
||||||
|
rename = []
|
||||||
for host in inventory.hosts:
|
for host in inventory.hosts:
|
||||||
self.display.vvv("executing reconstructed script for %s" % (host,))
|
self.display.vvv("executing reconstructed script for %s" % (host,))
|
||||||
try:
|
try:
|
||||||
self.exec_for_host(host, instructions)
|
new_name = self.exec_for_host(host, instructions)
|
||||||
except AnsibleError as e:
|
except AnsibleError as e:
|
||||||
if self.get_option("strictness") == "full":
|
if self.get_option("strictness") == "full":
|
||||||
raise
|
raise
|
||||||
self.display.warning(
|
self.display.warning(
|
||||||
"reconstructed - error on host %s: %s" % (host, repr(e))
|
"reconstructed - error on host %s: %s" % (host, repr(e))
|
||||||
)
|
)
|
||||||
|
continue
|
||||||
|
if new_name is not None:
|
||||||
|
rename.append((host, new_name))
|
||||||
|
# Rename hosts
|
||||||
|
for old_name, new_name in rename:
|
||||||
|
if old_name.lower() == new_name.lower():
|
||||||
|
continue
|
||||||
|
self.rename_host(old_name, new_name)
|
||||||
|
|
||||||
def exec_for_host(self, host, instructions):
|
def exec_for_host(self, host, instructions):
|
||||||
"""Execute the program for a single host.
|
"""Execute the program for a single host.
|
||||||
|
@ -1107,7 +1161,8 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
context = Context(combine_vars(group_vars, host_vars))
|
context = Context(combine_vars(group_vars, host_vars))
|
||||||
for instruction in instructions:
|
for instruction in instructions:
|
||||||
if not instruction.run_for(host, context):
|
if not instruction.run_for(host, context):
|
||||||
return
|
break
|
||||||
|
return context.new_name
|
||||||
|
|
||||||
def dump_program(self, instructions):
|
def dump_program(self, instructions):
|
||||||
"""Dump the whole program to the log, depending on verbosity level.
|
"""Dump the whole program to the log, depending on verbosity level.
|
||||||
|
@ -1129,3 +1184,33 @@ class InventoryModule(BaseInventoryPlugin):
|
||||||
output.append("")
|
output.append("")
|
||||||
output.extend(instr.dump())
|
output.extend(instr.dump())
|
||||||
self.display.vvvv("parsed program:\n\n" + "\n".join(" " + s for s in output))
|
self.display.vvvv("parsed program:\n\n" + "\n".join(" " + s for s in output))
|
||||||
|
|
||||||
|
def rename_host(self, old_name, new_name):
|
||||||
|
"""Renames a host.
|
||||||
|
|
||||||
|
This method "renames" a host by removing the host's current inventory
|
||||||
|
record and creating a new one with the same data, except for the name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
old_name: the host's old name
|
||||||
|
new_name: the host's new name
|
||||||
|
"""
|
||||||
|
if self.inventory.get_host(new_name) is not None:
|
||||||
|
raise AnsibleRuntimeError("duplicate host name %s" % (new_name,))
|
||||||
|
self.display.vvv("renaming host %s to %s" % (old_name, new_name))
|
||||||
|
host = self.inventory.get_host(old_name)
|
||||||
|
group_names = []
|
||||||
|
for g in host.get_groups():
|
||||||
|
if g.name == 'all':
|
||||||
|
continue
|
||||||
|
if old_name in g.host_names:
|
||||||
|
group_names.append(g.name)
|
||||||
|
self.inventory.remove_host(host)
|
||||||
|
self.inventory.add_host(new_name)
|
||||||
|
new_host = self.inventory.get_host(new_name)
|
||||||
|
new_host._uuid = host._uuid
|
||||||
|
if host.address == host.name:
|
||||||
|
new_host.address = new_name
|
||||||
|
new_host.vars = host.vars
|
||||||
|
for group in group_names:
|
||||||
|
self.inventory.add_child(group, new_name)
|
||||||
|
|
Loading…
Reference in a new issue