Skip to content

Commit 54086df

Browse files
authored
Merge pull request #459 from xcp-ng/ohu/update-tools/snapshot-2
tools/update - add a new arg to handle hosting pool (physical)
2 parents 5f36331 + 3bb2f95 commit 54086df

5 files changed

Lines changed: 65 additions & 39 deletions

File tree

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -641,17 +641,17 @@ uv run scripts/tools.py -h
641641
This command performs an update operation on remote targets.
642642

643643
```bash
644-
uv run scripts/tools.py update -H primary1 primary2
644+
uv run scripts/tools.py update -H master1 master2
645645
```
646646

647647
For each pool target :
648648

649-
1. Update master (primary) host of the pool:
649+
1. Update master host of the pool:
650650
* Clean cached metadata
651651
* Update with repository manager (yum): Optionally enables repositories
652652
* Reboot
653-
2. Get attached secondary hosts of the pool
654-
* Repeat step `1.` for each secondary
653+
2. Get other hosts of the pool
654+
* Repeat step `1.` for each host
655655

656656
**Inventory file**
657657

@@ -671,8 +671,9 @@ Take a look at an example inventory file:
671671
```toml
672672
# my_inventory.toml
673673

674-
[all]
674+
[default]
675675
repositories = ["xcp-ng-base"]
676+
hosting_pool = "A"
676677

677678
[hosts]
678679

@@ -681,12 +682,14 @@ repositories = ["xcp-ng-base"]
681682
[hosts."ip_or_hostname-2"]
682683

683684
repositories = ["xcp-ng-updates"]
685+
hosting_pool = "B"
684686
```
685687

686688
> [!IMPORTANT]
687-
> Config values under `servers` override values under `all`. For instance, the above inventory would produce
689+
> * `default` is applied to all hosts
690+
> * Config values under `hosts` override values under `default`. For instance, the above inventory would produce
688691
> the following python dict:
689692
>
690-
> `{'ip_or_hostname-1': {'repositories': ['xcp-ng-base']}, 'ip_or_hostname-2': {'repositories': ['xcp-ng-updates']}}`
693+
> `{'ip_or_hostname-1': {'repositories': ['xcp-ng-base'], 'hosting_pool': 'A'}, 'ip_or_hostname-2': {'repositories': ['xcp-ng-updates'], 'hosting_pool': 'B'}}`
691694
>
692-
> Using *enablerepo flag* `-e` with inventory is still possible, it won't be used though.
695+
> * When `--inventory` flag is present, repos passed to `-e` flag won't be considered.

lib/pool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
class Pool:
1818
"""Pool
1919
20-
:raises NotAMasterHostError: if initial host is not a master (primary)
20+
:raises NotAMasterHostError: if initial host is not a master
2121
"""
2222
xe_prefix = "pool"
2323

@@ -300,4 +300,4 @@ def network_named(self, network_name: str) -> str:
300300
return self.master.xe('network-list', {'name-label': network_name}, minimal=True)
301301

302302
class NotAMasterHostError(Exception):
303-
"""Host must be a master (primary)."""
303+
"""Host must be a master."""

lib/tools/cli.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def _command_update(args: argparse.Namespace) -> None:
1717
if args.inventory:
1818
inventory = load_inventory(args.inventory)
1919
else:
20-
inventory = into_inventory(args.hosts, args.repos)
20+
inventory = into_inventory(args.hosts, args.repos, args.hosting_pool)
2121

2222
update_pools(inventory)
2323

@@ -53,6 +53,12 @@ def cli() -> None:
5353
dest="repos",
5454
help="repositories to enable when updating",
5555
)
56+
subparser_cmd_update.add_argument(
57+
"-P",
58+
"--hosting-pool",
59+
type=HostAddress,
60+
help="Address (hostname|ip) of hosting pool's master host (nested context)",
61+
)
5662
subparser_cmd_update.set_defaults(func=_command_update)
5763

5864
args = parser.parse_args()

lib/tools/inventory.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,52 @@
1111

1212
class HostConfig(TypedDict):
1313
repositories: list[str]
14+
hosting_pool: HostAddress | None
1415

1516

16-
Inventory: TypeAlias = dict[HostAddress, HostConfig]
17+
HostConfigs: TypeAlias = dict[HostAddress, HostConfig]
1718

19+
class Inventory(TypedDict):
20+
hosts: HostConfigs
1821

1922
def load_inventory(inventory_path: Path) -> Inventory:
2023
"""Create an inventory object from loaded inventory file."""
21-
inventory: Inventory = {}
22-
2324
with open(inventory_path, "rb") as f:
2425
data = tomllib.load(f)
2526

26-
all = data.get("all", {})
27+
default = data.get("default", {})
2728
hosts = data.get("hosts", [])
2829

29-
for server, config in hosts.items():
30+
inventory_hosts: HostConfigs = {}
31+
for h, config in hosts.items():
3032
repos = config.get("repositories", [])
31-
host: HostConfig = {"repositories": repos or all.get("repositories", [])}
32-
inventory[server] = host
33+
hosting_pool = config.get("hosting_pool", None)
34+
if hosting_pool is None:
35+
hosting_pool = default.get("hosting_pool", None)
36+
host: HostConfig = {
37+
"repositories": repos or default.get("repositories", []),
38+
"hosting_pool": hosting_pool,
39+
}
40+
inventory_hosts[h] = host
3341

34-
return inventory
42+
return {
43+
"hosts": inventory_hosts,
44+
}
3545

3646

37-
def into_inventory(hosts: list[HostAddress], repositories: list[str]) -> Inventory:
47+
def into_inventory(hosts: list[HostAddress], repositories: list[str], hosting_pool: HostAddress) -> Inventory:
3848
"""Create an inventory object from arguments.
3949
4050
Basically, it is used as compatibility when we don't want inventory from file.
4151
"""
42-
inventory: Inventory = {}
43-
52+
inventory_hosts: HostConfigs = {}
4453
for h in hosts:
45-
host: HostConfig = {"repositories": repositories or []}
46-
inventory[h] = host
47-
48-
return inventory
54+
host: HostConfig = {
55+
"repositories": repositories or [],
56+
"hosting_pool": hosting_pool or None,
57+
}
58+
inventory_hosts[h] = host
59+
60+
return {
61+
"hosts": inventory_hosts,
62+
}

lib/tools/tasks/update.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,43 @@
77
from concurrent.futures import ThreadPoolExecutor
88

99
from lib.pool import NotAMasterHostError, Pool
10+
from lib.tools.inventory import Inventory
1011

1112
from .. import logger
1213

13-
def update_pools(inventory: dict) -> None:
14+
def update_pools(inventory: Inventory) -> None:
1415
"""Updates hosts in pool(s).
1516
1617
.. note::
1718
1819
Every non-master hosts in inventory will be ignored
1920
20-
*Update master hosts declared in inventory first, then, update secondary hosts attached to each master.*
21+
*Update each pool's master host declared in inventory first, then, update other hosts for each pool.*
2122
2223
:param dict inventory:
2324
Each host (key) holds its own config data (values, eg: `enablerepos`).
2425
"""
2526
logger.debug(f"Inventory: {inventory}")
27+
inventory_hosts = inventory["hosts"]
2628
# init related pools
27-
pools = []
28-
for h in inventory:
29+
pools: list[Pool] = []
30+
for host in inventory_hosts:
2931
try:
30-
p = Pool(h)
32+
p = Pool(host)
3133
pools.append(p)
3234
except NotAMasterHostError:
33-
logger.warning(f"[{h}] Skipping: not a master host")
35+
logger.warning(f"[{host}] Skipping: not a master host")
3436

37+
# update master hosts
3538
with ThreadPoolExecutor() as executor:
3639
for p in pools:
37-
executor.submit(p.master.update, inventory[p.master.hostname_or_ip]["enablerepos"])
40+
executor.submit(p.master.update, inventory_hosts[p.master.hostname_or_ip]["repositories"])
3841

39-
# secondary hosts
42+
# update other hosts
4043
with ThreadPoolExecutor() as executor:
4144
for p in pools:
42-
# omit first item because it is a primary (master)
43-
for h in p.hosts[1:]:
44-
# repos are the same as the primary (master)
45-
repos = inventory[p.master.hostname_or_ip]["enablerepos"]
46-
executor.submit(h.update, repos)
45+
# omit first item because it is the pool's master
46+
for other_host in p.hosts[1:]:
47+
# repos are the same as for the master host
48+
repos = inventory_hosts[p.master.hostname_or_ip]["repositories"]
49+
executor.submit(other_host.update, repos)

0 commit comments

Comments
 (0)