Add commands to run command on hosts

Change-Id: I019fc3f5c59c383215febb958f9c4cf8c8b8e4a6
Story: 2003312
Task: 24270
This commit is contained in:
Kevin Tibi 2018-08-06 12:32:23 +02:00 committed by Will Szumski
parent 7cb684a253
commit b8305b1799
9 changed files with 201 additions and 0 deletions

View File

@ -0,0 +1,6 @@
---
- name: Run a command
hosts: seed-hypervisor:seed:overcloud
tasks:
- name: Run a command
shell: "{{ host_command_to_run }}"

View File

@ -21,6 +21,20 @@ To only install updates that have been marked security related::
Note that these commands do not affect packages installed in containers, only
those installed on the host.
Running Commands
================
It is possible to run a command on the overcloud hosts::
(kayobe) $ kayobe overcloud host command run --command "<command>"
For example::
(kayobe) $ kayobe overcloud host command run --command "service docker restart"
To execute the command with root privileges, add the ``--become`` argument.
Adding the ``--verbose`` argument allows the output of the command to be seen.
Reconfiguring Containerised Services
====================================

View File

@ -122,3 +122,21 @@ Finally, start the Ironic and Ironic Inspector services again::
docker exec -it bifrost_deploy \
systemctl start ironic-api ironic-conductor ironic-inspector
Running Commands
================
It is possible to run a command on the seed host::
(kayobe) $ kayobe seed host command run --command "<command>"
For example::
(kayobe) $ kayobe seed host command run --command "service docker restart"
Commands can also be run on the seed hypervisor host, if one is in use::
(kayobe) $ kayobe seed hypervisor host command run --command "<command>"
To execute the command with root privileges, add the ``--become`` argument.
Adding the ``--verbose`` argument allows the output of the command to be seen.

View File

@ -326,6 +326,27 @@ class SeedHypervisorHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin,
limit="seed-hypervisor")
class SeedHypervisorHostCommandRun(KayobeAnsibleMixin, VaultMixin, Command):
"""Run command on the seed hypervisor host."""
def get_parser(self, prog_name):
parser = super(SeedHypervisorHostCommandRun, self).get_parser(
prog_name)
group = parser.add_argument_group("Host Command Run")
group.add_argument("--command", required=True,
help="Command to run (required).")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Run command on seed hypervisor host")
extra_vars = {
"host_command_to_run": utils.escape_jinja(parsed_args.command)}
playbooks = _build_playbook_list("host-command-run")
self.run_kayobe_playbooks(parsed_args, playbooks,
limit="seed-hypervisor",
extra_vars=extra_vars)
class SeedHypervisorHostUpgrade(KayobeAnsibleMixin, VaultMixin, Command):
"""Upgrade the seed hypervisor host services.
@ -503,6 +524,25 @@ class SeedHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
extra_vars=extra_vars)
class SeedHostCommandRun(KayobeAnsibleMixin, VaultMixin, Command):
"""Run command on the seed host."""
def get_parser(self, prog_name):
parser = super(SeedHostCommandRun, self).get_parser(prog_name)
group = parser.add_argument_group("Host Command Run")
group.add_argument("--command", required=True,
help="Command to run (required).")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Run command on seed host")
extra_vars = {
"host_command_to_run": utils.escape_jinja(parsed_args.command)}
playbooks = _build_playbook_list("host-command-run")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed",
extra_vars=extra_vars)
class SeedHostUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
Command):
"""Upgrade the seed host services.
@ -877,6 +917,25 @@ class OvercloudHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
extra_vars=extra_vars)
class OvercloudHostCommandRun(KayobeAnsibleMixin, VaultMixin, Command):
"""Run command on the overcloud host."""
def get_parser(self, prog_name):
parser = super(OvercloudHostCommandRun, self).get_parser(prog_name)
group = parser.add_argument_group("Host Command Run")
group.add_argument("--command", required=True,
help="Command to run (required).")
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Run command on overcloud host")
extra_vars = {
"host_command_to_run": utils.escape_jinja(parsed_args.command)}
playbooks = _build_playbook_list("host-command-run")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud",
extra_vars=extra_vars)
class OvercloudHostUpgrade(KayobeAnsibleMixin, VaultMixin, Command):
"""Upgrade the overcloud host services.

View File

@ -273,6 +273,30 @@ class TestCase(unittest.TestCase):
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_hypervisor_host_command_run(self, mock_run):
command = commands.SeedHypervisorHostCommandRun(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--command", "ls -a"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible",
"host-command-run.yml"),
],
limit="seed-hypervisor",
extra_vars={
"host_command_to_run": utils.escape_jinja("ls -a")},
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_hypervisor_host_upgrade(self, mock_run):
@ -485,6 +509,30 @@ class TestCase(unittest.TestCase):
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_host_command_run(self, mock_run):
command = commands.SeedHostCommandRun(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--command", "ls -a"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible",
"host-command-run.yml"),
],
limit="seed",
extra_vars={
"host_command_to_run": utils.escape_jinja("ls -a")},
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_host_package_update_all(self, mock_run):
@ -1059,6 +1107,30 @@ class TestCase(unittest.TestCase):
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_overcloud_host_command_run(self, mock_run):
command = commands.OvercloudHostCommandRun(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--command", "ls -a"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible",
"host-command-run.yml"),
],
limit="overcloud",
extra_vars={
"host_command_to_run": utils.escape_jinja("ls -a")},
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_overcloud_host_package_update_all(self, mock_run):

View File

@ -123,3 +123,8 @@ key2: value2
def test_quote_and_escape_non_string(self):
self.assertEqual(True, utils.quote_and_escape(True))
def test_escape_jinja(self):
value = "string to escape"
expected = "{{'c3RyaW5nIHRvIGVzY2FwZQ==' | b64decode }}"
self.assertEqual(expected, utils.escape_jinja(value))

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import base64
import glob
import logging
import os
@ -165,3 +166,18 @@ def quote_and_escape(value):
if not isinstance(value, six.string_types):
return value
return "'" + value.replace("'", "'\\''") + "'"
def escape_jinja(string):
"""Escapes a string so that jinja template variables are not expanded
:param string: the string to escape
:return: the escaped string
"""
# We base64 encode the string to avoid the need to escape characters.
# This is because ansible has some parsing quirks that makes it fairly
# hard to escape stuff in generic way.
# See: https://github.com/ansible/ansible/issues/10464
b64_value = base64.b64encode(string.encode())
return ''.join(('{{', "'", b64_value.decode(), "' | b64decode ", '}}'))

View File

@ -0,0 +1,8 @@
---
features:
- |
Add commands to run commands on seed hypervisor, seed and overcloud hosts:
``kayobe seed hypervisor host command run --command <command>``
``kayobe seed host command run --command <command>``
``kayobe overcloud host command run --command <command>``

View File

@ -58,6 +58,7 @@ kayobe.cli=
overcloud_hardware_inspect = kayobe.cli.commands:OvercloudHardwareInspect
overcloud_host_configure = kayobe.cli.commands:OvercloudHostConfigure
overcloud_host_package_update = kayobe.cli.commands:OvercloudHostPackageUpdate
overcloud_host_command_run = kayobe.cli.commands:OvercloudHostCommandRun
overcloud_host_upgrade = kayobe.cli.commands:OvercloudHostUpgrade
overcloud_introspection_data_save = kayobe.cli.commands:OvercloudIntrospectionDataSave
overcloud_inventory_discover = kayobe.cli.commands:OvercloudInventoryDiscover
@ -75,8 +76,10 @@ kayobe.cli=
seed_deployment_image_build = kayobe.cli.commands:SeedDeploymentImageBuild
seed_host_configure = kayobe.cli.commands:SeedHostConfigure
seed_host_package_update = kayobe.cli.commands:SeedHostPackageUpdate
seed_host_command_run = kayobe.cli.commands:SeedHostCommandRun
seed_host_upgrade = kayobe.cli.commands:SeedHostUpgrade
seed_hypervisor_host_configure = kayobe.cli.commands:SeedHypervisorHostConfigure
seed_hypervisor_host_command_run = kayobe.cli.commands:SeedHypervisorHostCommandRun
seed_hypervisor_host_upgrade = kayobe.cli.commands:SeedHypervisorHostUpgrade
seed_service_deploy = kayobe.cli.commands:SeedServiceDeploy
seed_service_upgrade = kayobe.cli.commands:SeedServiceUpgrade