[libvirt] [jenkins-ci PATCH v2 04/12] lcitool: Add inventory handling

Katerina Koukiou kkoukiou at redhat.com
Tue Jul 17 09:57:32 UTC 2018


On Thu, Jul 12, 2018 at 05:19:21PM +0200, Andrea Bolognani wrote:
> We use an actual YAML parser this time around, and bring
> the behavior more in line with what Ansible is doing, so
> interoperability should be more solid overall.
> 
> New in this implementation is more flexibility in defining
> host lists, including support for explicit lists as well
> as glob patterns.
> 
> Signed-off-by: Andrea Bolognani <abologna at redhat.com>
> ---
>  guests/lcitool | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
> 
> diff --git a/guests/lcitool b/guests/lcitool
> index e03b388..e90a33b 100755
> --- a/guests/lcitool
> +++ b/guests/lcitool
> @@ -19,11 +19,13 @@
>  
>  import argparse
>  import crypt
> +import fnmatch
>  import os
>  import random
>  import string
>  import sys
>  import textwrap
> +import yaml

Since you have to install yaml to actually use it, I suggest at this
point to create a requirements.txt file for this script, so that users
don't have to go over the code to check the dependencies.

>  
>  # This is necessary to maintain Python 2.7 compatibility
>  try:
> @@ -44,6 +46,32 @@ class Util:
>          salt = "".join(random.choice(alphabeth) for x in range(0, 16))
>          return "$6${}$".format(salt)
>  
> +    @staticmethod
> +    def expand_pattern(pattern, source, name):
> +        if pattern == None:
> +            raise Error("Missing {} list".format(name))
> +
> +        if pattern == "all":
> +            pattern = "*"
> +
> +        # This works correctly for single items as well as more complex
> +        # cases such as explicit lists, glob patterns and any combination
> +        # of the above
> +        matches = []
> +        for partial_pattern in pattern.split(","):
> +
> +            partial_matches = []
> +            for item in source:
> +                if fnmatch.fnmatch(item, partial_pattern):
> +                    partial_matches += [item]
> +
> +            if len(partial_matches) == 0:
> +                raise Error("Invalid {} list '{}'".format(name, pattern))
> +
> +            matches += partial_matches
> +
> +        return sorted(set(matches))
> +
>  class Config:
>  
>      def _get_config_file(self, name):
> @@ -142,10 +170,73 @@ class Config:
>  
>          return root_hash_file
>  
> +class Inventory:
> +
> +    def __init__(self):
> +        try:
> +            parser = configparser.SafeConfigParser()
> +            parser.read("./ansible.cfg")
> +            inventory_path = parser.get("defaults", "inventory")
> +        except:
> +            raise Error("Can't find inventory location in ansible.cfg")
> +
> +        self._facts = {}
> +        try:
> +            # We can only deal with trivial inventories, but that's
> +            # all we need right now and we can expand support further
> +            # later on if necessary
> +            with open(inventory_path, "r") as f:
> +                for line in f:
> +                    host = line.strip()
> +                    self._facts[host] = {}
> +        except:
> +            raise Error(
> +                "Missing or invalid inventory ({})".format(
> +                    inventory_path,
> +                )
> +            )
> +
> +        for host in self._facts:
> +            try:
> +                self._facts[host] = self._read_all_facts(host)
> +                self._facts[host]["inventory_hostname"] = host
> +            except:
> +                raise Error("Can't load facts for '{}'".format(host))
> +
> +    def _add_facts_from_file(self, facts, yaml_path):
> +        with open(yaml_path, "r") as f:
> +            some_facts = yaml.load(f)
> +            for fact in some_facts:
> +                facts[fact] = some_facts[fact]
> +
> +    def _read_all_facts(self, host):
> +        facts = {}
> +
> +        # We load from group_vars/ first and host_vars/ second, sorting
> +        # files alphabetically; doing so should result in our view of
> +        # the facts matching Ansible's
> +        for source in ["./group_vars/all/", "./host_vars/{}/".format(host)]:
> +            for item in sorted(os.listdir(source)):
> +                yaml_path = os.path.join(source, item)
> +                if not os.path.isfile(yaml_path):
> +                    continue
> +                if not yaml_path.endswith(".yml"):
> +                    continue
> +                self._add_facts_from_file(facts, yaml_path)
> +
> +        return facts
> +
> +    def expand_pattern(self, pattern):
> +        return Util.expand_pattern(pattern, self._facts, "host")
> +
> +    def get_facts(self, host):
> +        return self._facts[host]
> +
>  class Application:
>  
>      def __init__(self):
>          self._config = Config()
> +        self._inventory = Inventory()
>  
>          self._parser = argparse.ArgumentParser(
>              conflict_handler = "resolve",
> -- 
> 2.17.1
> 
> --
> libvir-list mailing list
> libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20180717/9ffdc3ca/attachment-0001.sig>


More information about the libvir-list mailing list