From 8e69fb0ae8851c6249b604f49db6348c5b3d9a96 Mon Sep 17 00:00:00 2001
From: Pierre Riteau <pierre@stackhpc.com>
Date: Fri, 17 May 2019 18:32:38 +0100
Subject: [PATCH] Add command to update packages on the seed hypervisor host

Change-Id: I6edda0527a892c55261cc6fc48a0feb899b348d6
---
 doc/source/administration/seed.rst            |  4 +
 doc/source/upgrading.rst                      | 21 ++++-
 kayobe/cli/commands.py                        | 27 +++++++
 kayobe/tests/unit/cli/test_commands.py        | 78 +++++++++++++++++++
 ...-host-package-update-2ad745a2d9fec134.yaml |  7 ++
 5 files changed, 136 insertions(+), 1 deletion(-)
 create mode 100644 releasenotes/notes/seed-hypervisor-host-package-update-2ad745a2d9fec134.yaml

diff --git a/doc/source/administration/seed.rst b/doc/source/administration/seed.rst
index 2ea6d878f..f2aa91b27 100644
--- a/doc/source/administration/seed.rst
+++ b/doc/source/administration/seed.rst
@@ -45,6 +45,10 @@ 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.
 
+Packages can also be updated on the seed hypervisor host, if one is in use::
+
+    (kayobe) $ kayobe seed hypervisor package update --packages <package1>,<package2>
+
 Kernel Updates
 --------------
 
diff --git a/doc/source/upgrading.rst b/doc/source/upgrading.rst
index e2ed53a45..8d46f9ea3 100644
--- a/doc/source/upgrading.rst
+++ b/doc/source/upgrading.rst
@@ -130,7 +130,26 @@ Upgrading the Seed Hypervisor
 =============================
 
 Currently, upgrading the seed hypervisor services is not supported.  It may
-however be necessary to upgrade some host services::
+however be necessary to upgrade host packages and some host services.
+
+Upgrading Host Packages
+-----------------------
+
+Prior to upgrading the seed hypervisor, it may be desirable to upgrade system
+packages on the seed hypervisor host.
+
+To update all eligible packages, use ``*``, escaping if necessary::
+
+    (kayobe) $ kayobe seed hypervisor host package update --packages *
+
+To only install updates that have been marked security related::
+
+    (kayobe) $ kayobe seed hypervisor host package update --packages <packages> --security
+
+Upgrading Host Services
+-----------------------
+
+It may be necessary to upgrade some host services::
 
     (kayobe) $ kayobe seed hypervisor host upgrade
 
diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py
index dbb8bcc3a..6162b07aa 100644
--- a/kayobe/cli/commands.py
+++ b/kayobe/cli/commands.py
@@ -333,6 +333,33 @@ class SeedHypervisorHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin,
                                   limit="seed-hypervisor")
 
 
+class SeedHypervisorHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
+    """Update packages on the seed hypervisor host."""
+
+    def get_parser(self, prog_name):
+        parser = super(SeedHypervisorHostPackageUpdate, self).get_parser(
+            prog_name)
+        group = parser.add_argument_group("Host Package Updates")
+        group.add_argument("--packages", required=True,
+                           help="List of packages to update. Use '*' to "
+                                "update all packages.")
+        group.add_argument("--security", action='store_true',
+                           help="Only install updates that have been marked "
+                                "security related.")
+        return parser
+
+    def take_action(self, parsed_args):
+        self.app.LOG.debug("Updating seed hypervisor host packages")
+        extra_vars = {
+            "host_package_update_packages": parsed_args.packages,
+            "host_package_update_security": parsed_args.security,
+        }
+        playbooks = _build_playbook_list("host-package-update")
+        self.run_kayobe_playbooks(parsed_args, playbooks,
+                                  limit="seed-hypervisor",
+                                  extra_vars=extra_vars)
+
+
 class SeedHypervisorHostCommandRun(KayobeAnsibleMixin, VaultMixin, Command):
     """Run command on the seed hypervisor host."""
 
diff --git a/kayobe/tests/unit/cli/test_commands.py b/kayobe/tests/unit/cli/test_commands.py
index b4788fdea..f20de7e81 100644
--- a/kayobe/tests/unit/cli/test_commands.py
+++ b/kayobe/tests/unit/cli/test_commands.py
@@ -302,6 +302,84 @@ 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_package_update_all(self, mock_run):
+        command = commands.SeedHypervisorHostPackageUpdate(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--packages", "*"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "host-package-update.yml"),
+                ],
+                limit="seed-hypervisor",
+                extra_vars={
+                    "host_package_update_packages": "*",
+                    "host_package_update_security": False,
+                },
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    def test_seed_hypervisor_host_package_update_list(self, mock_run):
+        command = commands.SeedHypervisorHostPackageUpdate(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--packages", "p1,p2"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "host-package-update.yml"),
+                ],
+                limit="seed-hypervisor",
+                extra_vars={
+                    "host_package_update_packages": "p1,p2",
+                    "host_package_update_security": False,
+                },
+            ),
+        ]
+        self.assertEqual(expected_calls, mock_run.call_args_list)
+
+    @mock.patch.object(commands.KayobeAnsibleMixin,
+                       "run_kayobe_playbooks")
+    def test_seed_hypervisor_host_package_update_security(self, mock_run):
+        command = commands.SeedHypervisorHostPackageUpdate(TestApp(), [])
+        parser = command.get_parser("test")
+        parsed_args = parser.parse_args(["--packages", "*", "--security"])
+
+        result = command.run(parsed_args)
+        self.assertEqual(0, result)
+
+        expected_calls = [
+            mock.call(
+                mock.ANY,
+                [
+                    utils.get_data_files_path(
+                        "ansible", "host-package-update.yml"),
+                ],
+                limit="seed-hypervisor",
+                extra_vars={
+                    "host_package_update_packages": "*",
+                    "host_package_update_security": True,
+                },
+            ),
+        ]
+        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):
diff --git a/releasenotes/notes/seed-hypervisor-host-package-update-2ad745a2d9fec134.yaml b/releasenotes/notes/seed-hypervisor-host-package-update-2ad745a2d9fec134.yaml
new file mode 100644
index 000000000..433d335f4
--- /dev/null
+++ b/releasenotes/notes/seed-hypervisor-host-package-update-2ad745a2d9fec134.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Add command to update packages on the seed hypervisor host, as already
+    available for seed and overcloud hosts:
+
+    ``kayobe seed hypervisor host package update --packages <packages>``