17
17
import json
18
18
import os
19
19
import tempfile
20
- from typing import Any , Dict , List , Optional , Union
20
+ from typing import Any , Callable , Iterator , Optional , Union
21
21
22
22
import testinfra
23
+ import testinfra .host
23
24
24
25
__all__ = ["AnsibleRunner" ]
25
26
26
27
local = testinfra .get_host ("local://" )
27
28
28
29
29
- def get_ansible_config ():
30
+ def get_ansible_config () -> configparser . ConfigParser :
30
31
fname = os .environ .get ("ANSIBLE_CONFIG" )
31
32
if not fname :
32
33
for possible in (
@@ -44,18 +45,29 @@ def get_ansible_config():
44
45
return config
45
46
46
47
47
- def get_ansible_inventory (config , inventory_file ):
48
+ Inventory = dict [str , Any ]
49
+
50
+
51
+ def get_ansible_inventory (
52
+ config : configparser .ConfigParser , inventory_file : Optional [str ]
53
+ ) -> Inventory :
48
54
# Disable ansible verbosity to avoid
49
55
# https://github.com/ansible/ansible/issues/59973
50
56
cmd = "ANSIBLE_VERBOSITY=0 ansible-inventory --list"
51
57
args = []
52
58
if inventory_file :
53
59
cmd += " -i %s"
54
60
args += [inventory_file ]
55
- return json .loads (local .check_output (cmd , * args ))
61
+ return json .loads (local .check_output (cmd , * args )) # type: ignore[no-any-return]
56
62
57
63
58
- def get_ansible_host (config , inventory , host , ssh_config = None , ssh_identity_file = None ):
64
+ def get_ansible_host (
65
+ config : configparser .ConfigParser ,
66
+ inventory : Inventory ,
67
+ host : str ,
68
+ ssh_config : Optional [str ] = None ,
69
+ ssh_identity_file : Optional [str ] = None ,
70
+ ) -> Optional [testinfra .host .Host ]:
59
71
if is_empty_inventory (inventory ):
60
72
if host == "localhost" :
61
73
return testinfra .get_host ("local://" )
@@ -81,7 +93,7 @@ def get_ansible_host(config, inventory, host, ssh_config=None, ssh_identity_file
81
93
"smart" : "ssh" ,
82
94
}.get (connection , connection )
83
95
84
- options : Dict [str , Any ] = {
96
+ options : dict [str , Any ] = {
85
97
"ansible_become" : {
86
98
"ini" : {
87
99
"section" : "privilege_escalation" ,
@@ -126,7 +138,9 @@ def get_ansible_host(config, inventory, host, ssh_config=None, ssh_identity_file
126
138
},
127
139
}
128
140
129
- def get_config (name , default = None ):
141
+ def get_config (
142
+ name : str , default : Union [None , bool , str ] = None
143
+ ) -> Union [None , bool , str ]:
130
144
value = default
131
145
option = options .get (name , {})
132
146
@@ -144,11 +158,12 @@ def get_config(name, default=None):
144
158
return value
145
159
146
160
testinfra_host = get_config ("ansible_host" , host )
161
+ assert isinstance (testinfra_host , str ), testinfra_host
147
162
user = get_config ("ansible_user" )
148
163
password = get_config ("ansible_ssh_pass" )
149
164
port = get_config ("ansible_port" )
150
165
151
- kwargs : Dict [str , Union [str , bool ]] = {}
166
+ kwargs : dict [str , Union [None , str , bool ]] = {}
152
167
if get_config ("ansible_become" , False ):
153
168
kwargs ["sudo" ] = True
154
169
kwargs ["sudo_user" ] = get_config ("ansible_become_user" )
@@ -165,8 +180,8 @@ def get_config(name, default=None):
165
180
kwargs ["ssh_extra_args" ] = " " .join (
166
181
[
167
182
config .get ("ssh_connection" , "ssh_args" , fallback = "" ),
168
- get_config ("ansible_ssh_common_args" , "" ),
169
- get_config ("ansible_ssh_extra_args" , "" ),
183
+ get_config ("ansible_ssh_common_args" , "" ), # type: ignore[list-item]
184
+ get_config ("ansible_ssh_extra_args" , "" ), # type: ignore[list-item]
170
185
]
171
186
).strip ()
172
187
@@ -191,20 +206,20 @@ def get_config(name, default=None):
191
206
return testinfra .get_host (spec , ** kwargs )
192
207
193
208
194
- def itergroup (inventory , group ) :
209
+ def itergroup (inventory : Inventory , group : str ) -> Iterator [ str ] :
195
210
for host in inventory .get (group , {}).get ("hosts" , []):
196
211
yield host
197
212
for g in inventory .get (group , {}).get ("children" , []):
198
213
for host in itergroup (inventory , g ):
199
214
yield host
200
215
201
216
202
- def is_empty_inventory (inventory ) :
217
+ def is_empty_inventory (inventory : Inventory ) -> bool :
203
218
return not any (True for _ in itergroup (inventory , "all" ))
204
219
205
220
206
221
class AnsibleRunner :
207
- _runners : Dict [Optional [str ], "AnsibleRunner" ] = {}
222
+ _runners : dict [Optional [str ], "AnsibleRunner" ] = {}
208
223
_known_options = {
209
224
# Boolean arguments.
210
225
"become" : {
@@ -243,12 +258,12 @@ class AnsibleRunner:
243
258
},
244
259
}
245
260
246
- def __init__ (self , inventory_file = None ):
261
+ def __init__ (self , inventory_file : Optional [ str ] = None ):
247
262
self .inventory_file = inventory_file
248
- self ._host_cache = {}
263
+ self ._host_cache : dict [ str , Optional [ testinfra . host . Host ]] = {}
249
264
super ().__init__ ()
250
265
251
- def get_hosts (self , pattern = "all" ):
266
+ def get_hosts (self , pattern : str = "all" ) -> list [ str ] :
252
267
inventory = self .inventory
253
268
result = set ()
254
269
if is_empty_inventory (inventory ):
@@ -271,18 +286,18 @@ def get_hosts(self, pattern="all"):
271
286
return sorted (result )
272
287
273
288
@functools .cached_property
274
- def inventory (self ):
289
+ def inventory (self ) -> Inventory :
275
290
return get_ansible_inventory (self .ansible_config , self .inventory_file )
276
291
277
292
@functools .cached_property
278
- def ansible_config (self ):
293
+ def ansible_config (self ) -> configparser . ConfigParser :
279
294
return get_ansible_config ()
280
295
281
- def get_variables (self , host ) :
296
+ def get_variables (self , host : str ) -> dict [ str , Any ] :
282
297
inventory = self .inventory
283
298
# inventory_hostname, group_names and groups are for backward
284
299
# compatibility with testinfra 2.X
285
- hostvars = inventory ["_meta" ].get ("hostvars" , {}).get (host , {})
300
+ hostvars : dict [ str , Any ] = inventory ["_meta" ].get ("hostvars" , {}).get (host , {})
286
301
hostvars .setdefault ("inventory_hostname" , host )
287
302
group_names = []
288
303
groups = {}
@@ -296,7 +311,7 @@ def get_variables(self, host):
296
311
hostvars .setdefault ("groups" , groups )
297
312
return hostvars
298
313
299
- def get_host (self , host , ** kwargs ) :
314
+ def get_host (self , host : str , ** kwargs : Any ) -> Optional [ testinfra . host . Host ] :
300
315
try :
301
316
return self ._host_cache [host ]
302
317
except KeyError :
@@ -305,14 +320,14 @@ def get_host(self, host, **kwargs):
305
320
)
306
321
return self ._host_cache [host ]
307
322
308
- def options_to_cli (self , options ) :
323
+ def options_to_cli (self , options : dict [ str , Any ]) -> tuple [ str , list [ str ]] :
309
324
verbose = options .pop ("verbose" , 0 )
310
325
311
326
args = {"become" : False , "check" : True }
312
327
args .update (options )
313
328
314
- cli : List [str ] = []
315
- cli_args : List [str ] = []
329
+ cli : list [str ] = []
330
+ cli_args : list [str ] = []
316
331
if verbose :
317
332
cli .append ("-" + "v" * verbose )
318
333
for arg_name , value in args .items ():
@@ -334,7 +349,14 @@ def options_to_cli(self, options):
334
349
raise TypeError ("Unsupported argument type '{}'." .format (opt_type ))
335
350
return " " .join (cli ), cli_args
336
351
337
- def run_module (self , host , module_name , module_args , get_encoding = None , ** options ):
352
+ def run_module (
353
+ self ,
354
+ host : str ,
355
+ module_name : str ,
356
+ module_args : str ,
357
+ get_encoding : Optional [Callable [[], str ]] = None ,
358
+ ** options : Any ,
359
+ ) -> Any :
338
360
cmd , args = "ansible --tree %s" , []
339
361
if self .inventory_file :
340
362
cmd += " -i %s"
@@ -375,7 +397,7 @@ def run_module(self, host, module_name, module_args, get_encoding=None, **option
375
397
return json .load (f )
376
398
377
399
@classmethod
378
- def get_runner (cls , inventory ) :
400
+ def get_runner (cls , inventory : Optional [ str ]) -> "AnsibleRunner" :
379
401
try :
380
402
return cls ._runners [inventory ]
381
403
except KeyError :
0 commit comments