From 63d69ccefdca774d90be02a5625f543194163e73 Mon Sep 17 00:00:00 2001
From: Brett Holman <brett.holman@canonical.com>
Date: Sat, 6 Aug 2022 00:11:25 +0200
Subject: [PATCH] Add Ansible Config Module (#1579)

Reference:https://github.com/canonical/cloud-init/commit/63d69ccefdca774d90be02a5625f543194163e73
Conflict:(1)only change cloudinit/util.py.
(2)add 'from collections import namedtuple'

The intended purpose of this commit is to augment cloud-init's
configuration capabilities during boot. This allows users
to run ansible playbooks against the local node as part of
the boot order. Current Ansible workflows on nodes booted
by cloud-init requires waiting for the booted node before
running the playbook against the online node from the external
control node. In the current state this could potentially
be automated to using phone-home or runcmd modules, however
neither of these options provides an obvious solution for
integration and both require an external service to operate.

This module enables users to seamlessly integrate cloud-init
and Ansible auto-configuration during boot using ansible-pull,
a less common mode of operation that differs from the normal
mode of operation by installing Ansible locally and running a
playbook pulled from a VCS repository.

Expected future work in this module includes an option to
configure management nodes.
---
 cloudinit/util.py | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/cloudinit/util.py b/cloudinit/util.py
index 7c47871..8763050 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -33,7 +33,8 @@ import sys
 import time
 from base64 import b64decode, b64encode
 from errno import ENOENT
-from functools import lru_cache
+from collections import namedtuple
+from functools import lru_cache, total_ordering
 from urllib import parse
 from typing import List
 
@@ -2776,4 +2777,46 @@ def get_proc_ppid(pid):
         LOG.warning('Failed to load /proc/%s/stat. %s', pid, e)
     return ppid
 
-# vi: ts=4 expandtab
+
+@total_ordering
+class Version(namedtuple("Version", ["major", "minor", "patch", "rev"])):
+    def __new__(cls, major=-1, minor=-1, patch=-1, rev=-1):
+        """Default of -1 allows us to tiebreak in favor of the most specific
+        number"""
+        return super(Version, cls).__new__(cls, major, minor, patch, rev)
+
+    @classmethod
+    def from_str(cls, version: str):
+        return cls(*(list(map(int, version.split(".")))))
+
+    def __gt__(self, other):
+        return 1 == self._compare_version(other)
+
+    def __eq__(self, other):
+        return (
+            self.major == other.major
+            and self.minor == other.minor
+            and self.patch == other.patch
+            and self.rev == other.rev
+        )
+
+    def _compare_version(self, other) -> int:
+        """
+        return values:
+            1: self > v2
+            -1: self < v2
+            0: self == v2
+        to break a tie between 3.1.N and 3.1, always treat the more
+        specific number as larger
+        """
+        if self == other:
+            return 0
+        if self.major > other.major:
+            return 1
+        if self.minor > other.minor:
+            return 1
+        if self.patch > other.patch:
+            return 1
+        if self.rev > other.rev:
+            return 1
+        return -1
-- 
2.33.0


