Skip to content

Commit 07389bf

Browse files
committed
scripts: add type hints
Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
1 parent 2676b0b commit 07389bf

File tree

3 files changed

+54
-43
lines changed

3 files changed

+54
-43
lines changed

scripts/install_xcpng.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
#!/usr/bin/env python3
1+
from __future__ import annotations
22

3+
#!/usr/bin/env python3
34
import argparse
45
import atexit
56
import logging
@@ -25,7 +26,8 @@
2526

2627
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
2728

28-
def generate_answerfile(directory, installer, hostname_or_ip, target_hostname, action, hdd, netinstall_gpg_check):
29+
def generate_answerfile(directory: str, installer: str, hostname_or_ip: str, target_hostname: str | None, action: str,
30+
hdd: str, netinstall_gpg_check: str) -> None:
2931
password = host_data(hostname_or_ip)['password']
3032
cmd = ['openssl', 'passwd', '-6', password]
3133
res = subprocess.run(cmd, stdout=subprocess.PIPE)
@@ -69,33 +71,33 @@ def generate_answerfile(directory, installer, hostname_or_ip, target_hostname, a
6971
else:
7072
raise Exception(f"Unknown action: `{action}`")
7173

72-
def is_ip_active(ip):
74+
def is_ip_active(ip: str) -> bool:
7375
return not os.system(f"ping -c 3 -W 10 {ip} > /dev/null 2>&1")
7476

75-
def is_ssh_up(ip):
77+
def is_ssh_up(ip: str) -> bool:
7678
try:
7779
ssh(ip, 'true', options=['-o', 'ConnectTimeout 10'])
7880
return True
7981
except SSHCommandFailed:
8082
# probably not up yet
8183
return False
8284

83-
def get_new_host_ip(mac_address):
85+
def get_new_host_ip(mac_address: str) -> str | None:
8486
candidate_ips = pxe.arp_addresses_for(mac_address)
8587
logging.debug("Candidate IPs: " + ", ".join(candidate_ips))
8688
for ip in candidate_ips:
8789
if is_ip_active(ip) and is_ssh_up(ip):
8890
return ip
8991
return None
9092

91-
def is_new_host_ready(ip_address):
93+
def is_new_host_ready(ip_address: str) -> bool:
9294
try:
9395
output = ssh(ip_address, 'xe host-list enabled=true --minimal')
9496
return is_uuid(output)
9597
except Exception:
9698
return False
9799

98-
def check_mac_address(host: Host, mac_address):
100+
def check_mac_address(host: Host, mac_address: str) -> None:
99101
bridge = host.inventory['MANAGEMENT_INTERFACE']
100102
host_mac_address = host.ssh(f'cat /sys/class/net/{bridge}/address')
101103
if mac_address != host_mac_address:
@@ -104,15 +106,15 @@ def check_mac_address(host: Host, mac_address):
104106
f"Expected: `{mac_address}`"
105107
)
106108

107-
def url_checker(url):
109+
def url_checker(url: str) -> None:
108110
try:
109111
response = requests.get(url)
110112
if not response:
111113
raise Exception(f"{url}: URL is not reachable, status_code: {response.status_code}")
112114
except requests.exceptions.RequestException as e:
113115
raise SystemExit(f"{url}: URL is not reachable\nErr: {e}")
114116

115-
def main():
117+
def main() -> None:
116118
parser = argparse.ArgumentParser()
117119
parser.add_argument(
118120
"host",

scripts/xcpng-fs-diff.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
import tempfile
4848
from enum import StrEnum, auto
4949
from fnmatch import fnmatch
50+
from subprocess import CompletedProcess
51+
52+
from typing import Any, cast
5053

5154
class DataType(StrEnum):
5255
FILE = auto()
@@ -55,10 +58,10 @@ class DataType(StrEnum):
5558
BROKEN_SYMLINK = auto()
5659
PACKAGE = auto()
5760

58-
def ignore_file(filename, ignored_files):
61+
def ignore_file(filename: str, ignored_files: list[str]) -> bool:
5962
return any(fnmatch(filename, i) for i in ignored_files)
6063

61-
def ssh_cmd(host, cmd):
64+
def ssh_cmd(host: str, cmd: str) -> str:
6265
args = ["ssh", f"root@{host}", cmd]
6366

6467
cmdres = subprocess.run(args, capture_output=True, text=True)
@@ -67,10 +70,10 @@ def ssh_cmd(host, cmd):
6770

6871
return cmdres.stdout
6972

70-
def ssh_get_files(host, file_type, folders):
73+
def ssh_get_files(host: str, file_type: DataType, folders: list[str]) -> dict[str, str] | None:
7174
md5sum = False
7275
readlink = False
73-
folders = " ".join(folders)
76+
folders_concatenated = " ".join(folders)
7477

7578
match file_type:
7679
case DataType.FILE:
@@ -89,7 +92,7 @@ def ssh_get_files(host, file_type, folders):
8992
print("Unknown file type: ", file=sys.stderr)
9093
return None
9194

92-
find_cmd = f"find {folders} {find_type}"
95+
find_cmd = f"find {folders_concatenated} {find_type}"
9396
if readlink:
9497
find_cmd += " -exec readlink -n {} \\; -exec echo -n ' ' \\; -print"
9598
elif md5sum:
@@ -99,14 +102,14 @@ def ssh_get_files(host, file_type, folders):
99102

100103
rawres = ssh_cmd(host, find_cmd)
101104

102-
res = dict()
105+
res: dict[str, str] = dict()
103106
for line in rawres.splitlines():
104107
entry = line.split(' ', 1)
105108
res[entry[1].strip()] = entry[0].strip()
106109

107110
return res
108111

109-
def ssh_get_packages(host):
112+
def ssh_get_packages(host: str) -> dict[str, str]:
110113
packages = dict()
111114

112115
res = ssh_cmd(host, "rpm -qa --queryformat '%{NAME} %{VERSION}\n'")
@@ -116,8 +119,8 @@ def ssh_get_packages(host):
116119

117120
return packages
118121

119-
def get_data(host, folders):
120-
ref_data = dict()
122+
def get_data(host: str, folders: list[str]) -> dict[DataType, dict[str, str] | None]:
123+
ref_data: dict[DataType, dict[str, str] | None] = dict()
121124

122125
try:
123126
ref_data[DataType.FILE] = ssh_get_files(host, DataType.FILE, folders)
@@ -131,7 +134,7 @@ def get_data(host, folders):
131134

132135
return ref_data
133136

134-
def sftp_get(host, remote_file, local_file):
137+
def sftp_get(host: str, remote_file: str, local_file: str) -> CompletedProcess[bytes]:
135138
opts = '-o "StrictHostKeyChecking no" -o "LogLevel ERROR" -o "UserKnownHostsFile /dev/null"'
136139

137140
args = f"sftp {opts} -b - root@{host}"
@@ -150,7 +153,7 @@ def sftp_get(host, remote_file, local_file):
150153

151154
return res
152155

153-
def remote_diff(host_ref, host_test, filename):
156+
def remote_diff(host_ref: str, host_test: str, filename: str) -> None:
154157
file_ref = None
155158
file_test = None
156159
try:
@@ -189,7 +192,7 @@ def remote_diff(host_ref, host_test, filename):
189192
if file_test is not None and os.path.exists(file_test):
190193
os.remove(file_test)
191194

192-
def print_results(results, show_diff, show_ignored):
195+
def print_results(results: dict[str, Any], show_diff: bool, show_ignored: bool) -> None:
193196
# Print what differs
194197
for dtype in DataType:
195198
if dtype == DataType.PACKAGE:
@@ -213,11 +216,13 @@ def print_results(results, show_diff, show_ignored):
213216
for f in results['ignored_files']:
214217
print(f"{f}")
215218

216-
def compare_data(ref, test, ignored_file_patterns):
219+
def compare_data(
220+
ref: dict[str, Any], test: dict[str, Any], ignored_file_patterns: list[str]
221+
) -> tuple[dict[str, Any], int]:
217222
ref_data = ref['data']
218223
test_data = test['data']
219224
err = 0
220-
results = {
225+
results: dict[str | DataType, Any] = {
221226
'host': {
222227
'ref': ref['host'],
223228
'test': test['host']
@@ -282,27 +287,27 @@ def compare_data(ref, test, ignored_file_patterns):
282287
return results, err
283288

284289
# Load a previously saved json file containing reference data
285-
def load_reference_files(filename):
290+
def load_reference_files(filename: str) -> dict[DataType, dict[str, str] | None]:
286291
try:
287292
with open(filename, 'r') as fd:
288-
return json.load(fd)
293+
return cast(dict[DataType, dict[str, str] | None], json.load(fd))
289294
except Exception as e:
290295
print(f"Error: {e}", file=sys.stderr)
291296
exit(-1)
292297

293298
# Save files from a reference host in json format
294-
def save_reference_data(files, filename):
299+
def save_reference_data(files: dict[DataType, dict[str, str] | None], filename: str) -> None:
295300
try:
296301
with open(filename, 'w') as fd:
297302
json.dump(files, fd, indent=4)
298303
except Exception as e:
299304
print(f"Error: {e}", file=sys.stderr)
300305
exit(-1)
301306

302-
def main():
303-
ref_data = None
304-
folders = ["/boot", "/etc", "/opt", "/usr"]
305-
ignored_file_patterns = [
307+
def main() -> int:
308+
ref_data: dict[DataType, dict[str, str] | None] | None = None
309+
folders: list[str] = ["/boot", "/etc", "/opt", "/usr"]
310+
ignored_file_patterns: list[str] = [
306311
'/boot/initrd-*',
307312
'/boot/efi/*',
308313
'/boot/grub/*',
@@ -361,13 +366,13 @@ def main():
361366
]
362367

363368
parser = argparse.ArgumentParser(description='Spot filesystem differences between 2 XCP-ng hosts')
364-
parser.add_argument('--reference-host', '-r', dest='ref_host',
369+
parser.add_argument('--reference-host', '-r', dest='ref_host', type=str,
365370
help='The XCP-ng host used as reference')
366-
parser.add_argument('--test-host', '-t', dest='test_host',
371+
parser.add_argument('--test-host', '-t', dest='test_host', type=str,
367372
help='The XCP-ng host to be tested after install or upgrade')
368-
parser.add_argument('--save-reference', '-s', dest='save_ref',
373+
parser.add_argument('--save-reference', '-s', dest='save_ref', type=str,
369374
help='Save filesystem information of the reference host to a file')
370-
parser.add_argument('--load-reference', '-l', dest='load_ref',
375+
parser.add_argument('--load-reference', '-l', dest='load_ref', type=str,
371376
help='Load reference filesystem information from a file')
372377
parser.add_argument('--show-diff', '-d', action='store_true', dest='show_diff',
373378
help='Show diff of text files that differ. A reference host must be supplied with -r')
@@ -412,8 +417,8 @@ def main():
412417
print(f"Get test host data from {args.test_host}")
413418
test_data = get_data(args.test_host, args.folders)
414419

415-
ref = dict([('data', ref_data), ('host', args.ref_host)])
416-
test = dict([('data', test_data), ('host', args.test_host)])
420+
ref: dict[str, Any] = dict([('data', ref_data), ('host', args.ref_host)])
421+
test: dict[str, Any] = dict([('data', test_data), ('host', args.test_host)])
417422

418423
results, err = compare_data(ref, test, args.ignored_file_patterns)
419424

scripts/xva_bridge.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,25 @@
1111
import libarchive # type: ignore
1212
import libarchive.ffi # type: ignore
1313

14+
from typing import Generator
15+
1416
class XvaHeaderMember:
1517
def __init__(self, member: minidom.Element):
1618
self.member = member
1719

18-
def get_name(self):
20+
def get_name(self) -> str | None:
1921
for child in self.member.childNodes:
2022
if child.nodeType == minidom.Node.ELEMENT_NODE and child.tagName == "name" and child.firstChild:
2123
return child.firstChild.nodeValue
2224
return None
2325

24-
def get_value(self):
26+
def get_value(self) -> str | None:
2527
for child in self.member.childNodes:
2628
if child.nodeType == minidom.Node.ELEMENT_NODE and child.tagName == "value" and child.firstChild:
2729
return child.firstChild.nodeValue
2830
return None
2931

30-
def set_value(self, value: str):
32+
def set_value(self, value: str) -> None:
3133
for child in self.member.childNodes:
3234
if child.nodeType == minidom.Node.ELEMENT_NODE and child.tagName == "value" and child.firstChild:
3335
child.firstChild.nodeValue = value # type: ignore
@@ -38,18 +40,20 @@ class XvaHeader:
3840
def __init__(self, header_bytes: bytes):
3941
self.xml = minidom.parseString(header_bytes.decode())
4042

41-
def members(self):
43+
def members(self) -> Generator[XvaHeaderMember, None, None]:
4244
for member in self.xml.getElementsByTagName("member"):
4345
if member.nodeType == minidom.Node.ELEMENT_NODE:
4446
yield XvaHeaderMember(member)
4547

46-
def get_bridge(self):
48+
def get_bridge(self) -> str:
4749
for member in self.members():
4850
if member.get_name() == "bridge":
49-
return member.get_value()
51+
v = member.get_value()
52+
assert v is not None
53+
return v
5054
raise ValueError("Could not find bridge value in XVA header")
5155

52-
def set_bridge(self, bridge: str):
56+
def set_bridge(self, bridge: str) -> None:
5357
for member in self.members():
5458
if member.get_name() == "bridge":
5559
member.set_value(bridge)

0 commit comments

Comments
 (0)