Skip to content

Commit b747ed7

Browse files
authored
Merge pull request #435 from xcp-ng/ohu/config-file/000
scripts/tools - add an inventory-like file for update command
2 parents 57bce80 + 194cd8e commit b747ed7

4 files changed

Lines changed: 98 additions & 14 deletions

File tree

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ uv run scripts/tools.py -h
569569
This command performs an update operation on remote targets.
570570

571571
```bash
572-
uv run scripts/tools.py update primary1 primary2
572+
uv run scripts/tools.py update -H primary1 primary2
573573
```
574574

575575
For each primary target :
@@ -578,3 +578,41 @@ For each primary target :
578578
2. Update with repository manager (yum): Optionally enables repositories
579579
3. Reboot
580580
4. Get attached secondary hosts and repeats three previous steps for each secondary
581+
582+
**Inventory file**
583+
584+
`update` command can read an inventory file in [TOML v1.0.0](https://toml.io/en/v1.0.0) format:
585+
586+
```bash
587+
uv run scripts/tools.py update -i my_inventory.toml
588+
```
589+
590+
> [!NOTE]
591+
> You can use either `-i/--inventory` or `-H/--hosts`.
592+
>
593+
> **Above flags can't be used together**
594+
595+
Take a look at an example inventory file:
596+
597+
```toml
598+
# my_inventory.toml
599+
600+
[all]
601+
enablerepos = ["xcp-ng-base"]
602+
603+
[servers]
604+
605+
[servers."ip_or_hostname-1"]
606+
607+
[servers."ip_or_hostname-2"]
608+
609+
enablerepos = ["xcp-ng-updates"]
610+
```
611+
612+
> [!IMPORTANT]
613+
> Config values under `servers` override values under `all`. For instance, the above inventory would produce
614+
> the following python dict:
615+
>
616+
> `{'ip_or_hostname-1': {'enablerepos': ['xcp-ng-base']}, 'ip_or_hostname-2': {'enablerepos': ['xcp-ng-updates']}}`
617+
>
618+
> Using *enablerepo flag* `-e` with inventory is still possible, it won't be used though.

lib/tools/cli.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@
44
"""
55
import argparse
66
import logging
7+
from pathlib import Path
78

89
from lib.common import HostAddress
910
from lib.tools import logger
11+
from lib.tools.inventory import into_inventory, load_inventory
1012
from lib.tools.tasks.update import update_all
1113

1214
def _command_update(args):
13-
update_all(args.hosts, args.repos)
15+
if args.inventory:
16+
inventory = load_inventory(args.inventory)
17+
else:
18+
inventory = into_inventory(args.hosts, args.repos)
19+
20+
update_all(inventory)
1421

1522

1623
def cli():
@@ -27,11 +34,13 @@ def cli():
2734
description="Run update tasks on target(s)",
2835
help="Run update tasks on target(s)",
2936
)
30-
subparser_cmd_update.add_argument(
31-
"hosts", type=HostAddress, metavar="HOST", nargs="+", help="Hostname(s) or ip address(es) of target(s)"
37+
cmd_update_excl_grp = subparser_cmd_update.add_mutually_exclusive_group(required=True)
38+
cmd_update_excl_grp.add_argument(
39+
"-H", "--hosts", type=HostAddress, metavar="HOST", nargs="+", help="Hostname(s) or ip address(es) of target(s)"
3240
)
41+
cmd_update_excl_grp.add_argument("-i", "--inventory", type=Path, help="Use an hosts inventory file")
3342
subparser_cmd_update.add_argument(
34-
"--enablerepo",
43+
"-e", "--enablerepo",
3544
metavar="REPO",
3645
action="append",
3746
dest="repos",

lib/tools/inventory.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Inventory for tools scripts.
2+
"""
3+
import tomllib
4+
from pathlib import Path
5+
6+
from lib.common import HostAddress
7+
8+
def load_inventory(inventory_path: Path) -> dict:
9+
"""Create an inventory object from loaded inventory file."""
10+
inventory = {}
11+
12+
with open(inventory_path, "rb") as f:
13+
data = tomllib.load(f)
14+
15+
all = data.get("all", {})
16+
servers = data.get("servers", [])
17+
18+
for server, config in servers.items():
19+
repos = config.get("enablerepos", [])
20+
host = {
21+
"enablerepos": repos or all.get("enablerepos", [])
22+
}
23+
inventory[server] = host
24+
25+
return inventory
26+
27+
def into_inventory(hosts: list[HostAddress], enablerepos: list[str]) -> dict:
28+
"""Create an inventory object from arguments.
29+
30+
Basically, it is used as compatibility when we don't want inventory from file.
31+
"""
32+
inventory = {}
33+
34+
for h in hosts:
35+
host = {
36+
"enablerepos": enablerepos or [],
37+
}
38+
inventory[h] = host
39+
40+
return inventory

lib/tools/tasks/update.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,28 @@
44
"""
55
from concurrent.futures import ThreadPoolExecutor
66

7-
from lib.common import HostAddress
87
from lib.host import Host
98
from lib.pool import Pool
109

1110
from .. import logger
1211

13-
def update_all(master_hosts: list[HostAddress], enablerepos: list[str]) -> None:
12+
def update_all(inventory: dict) -> None:
1413
"""Updates all master (primary) hosts.
1514
1615
.. note:: Host must be a master
1716
1817
Throws error if hosts are not master (primary).
1918
20-
:param :py:class:`list[lib.common.HostAddress]` master_hosts:
21-
A list of hosts to update.
22-
:param list[str] enablerepos:
23-
Repositories to enable when updating.
19+
:param dict inventory:
20+
Each host (key) holds its own config data (values).
2421
"""
25-
logger.debug(f"[{master_hosts}] enablerepos: {enablerepos}")
22+
logger.debug(f"Inventory: {inventory}")
2623
# init related pools
27-
pools = [Pool(h) for h in master_hosts]
24+
pools = [Pool(h) for h in inventory]
2825

2926
with ThreadPoolExecutor() as executor:
3027
for p in pools:
31-
executor.submit(update_host, p.master, enablerepos)
28+
executor.submit(update_host, p.master, inventory[p.master.hostname_or_ip]["enablerepos"])
3229

3330

3431
def update_host(host: Host, enablerepos: list[str] = []):

0 commit comments

Comments
 (0)