pyinfra 3.0.dev0__py2.py3-none-any.whl → 3.0.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra/api/__init__.py +3 -0
- pyinfra/api/arguments.py +115 -97
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +5 -3
- pyinfra/api/config.py +139 -39
- pyinfra/api/connectors.py +5 -2
- pyinfra/api/deploy.py +19 -19
- pyinfra/api/exceptions.py +35 -4
- pyinfra/api/facts.py +62 -86
- pyinfra/api/host.py +102 -15
- pyinfra/api/inventory.py +4 -0
- pyinfra/api/operation.py +184 -118
- pyinfra/api/operations.py +66 -113
- pyinfra/api/state.py +53 -34
- pyinfra/api/util.py +64 -33
- pyinfra/connectors/base.py +65 -20
- pyinfra/connectors/chroot.py +15 -13
- pyinfra/connectors/docker.py +62 -72
- pyinfra/connectors/dockerssh.py +20 -19
- pyinfra/connectors/local.py +32 -22
- pyinfra/connectors/ssh.py +162 -86
- pyinfra/connectors/sshuserclient/client.py +1 -1
- pyinfra/connectors/terraform.py +57 -39
- pyinfra/connectors/util.py +26 -27
- pyinfra/connectors/vagrant.py +27 -26
- pyinfra/context.py +1 -0
- pyinfra/facts/apk.py +7 -2
- pyinfra/facts/apt.py +15 -7
- pyinfra/facts/brew.py +28 -13
- pyinfra/facts/bsdinit.py +9 -6
- pyinfra/facts/cargo.py +6 -3
- pyinfra/facts/choco.py +8 -4
- pyinfra/facts/deb.py +21 -9
- pyinfra/facts/dnf.py +11 -6
- pyinfra/facts/docker.py +30 -5
- pyinfra/facts/files.py +49 -33
- pyinfra/facts/gem.py +7 -2
- pyinfra/facts/git.py +14 -21
- pyinfra/facts/gpg.py +4 -1
- pyinfra/facts/hardware.py +186 -138
- pyinfra/facts/launchd.py +7 -2
- pyinfra/facts/lxd.py +8 -2
- pyinfra/facts/mysql.py +19 -12
- pyinfra/facts/npm.py +3 -1
- pyinfra/facts/openrc.py +8 -2
- pyinfra/facts/pacman.py +13 -5
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +5 -1
- pyinfra/facts/pkgin.py +7 -2
- pyinfra/facts/postgres.py +170 -0
- pyinfra/facts/postgresql.py +5 -162
- pyinfra/facts/rpm.py +21 -15
- pyinfra/facts/runit.py +70 -0
- pyinfra/facts/selinux.py +12 -4
- pyinfra/facts/server.py +240 -82
- pyinfra/facts/snap.py +8 -2
- pyinfra/facts/systemd.py +37 -13
- pyinfra/facts/sysvinit.py +7 -4
- pyinfra/facts/upstart.py +7 -2
- pyinfra/facts/util/packaging.py +3 -2
- pyinfra/facts/vzctl.py +8 -4
- pyinfra/facts/xbps.py +7 -2
- pyinfra/facts/yum.py +10 -5
- pyinfra/facts/zypper.py +9 -4
- pyinfra/operations/apk.py +5 -3
- pyinfra/operations/apt.py +28 -25
- pyinfra/operations/brew.py +60 -29
- pyinfra/operations/bsdinit.py +6 -4
- pyinfra/operations/cargo.py +3 -1
- pyinfra/operations/choco.py +3 -1
- pyinfra/operations/dnf.py +16 -20
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +187 -168
- pyinfra/operations/gem.py +3 -1
- pyinfra/operations/git.py +23 -25
- pyinfra/operations/iptables.py +33 -25
- pyinfra/operations/launchd.py +5 -6
- pyinfra/operations/lxd.py +7 -4
- pyinfra/operations/mysql.py +59 -55
- pyinfra/operations/npm.py +8 -1
- pyinfra/operations/openrc.py +5 -3
- pyinfra/operations/pacman.py +6 -7
- pyinfra/operations/pip.py +19 -12
- pyinfra/operations/pkg.py +3 -1
- pyinfra/operations/pkgin.py +5 -3
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -335
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -29
- pyinfra/operations/server.py +138 -67
- pyinfra/operations/snap.py +3 -1
- pyinfra/operations/ssh.py +18 -16
- pyinfra/operations/systemd.py +18 -12
- pyinfra/operations/sysvinit.py +7 -5
- pyinfra/operations/upstart.py +7 -5
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +54 -38
- pyinfra/operations/util/service.py +39 -47
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +5 -3
- pyinfra/operations/yum.py +15 -19
- pyinfra/operations/zypper.py +9 -10
- pyinfra/version.py +5 -2
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/METADATA +51 -58
- pyinfra-3.0.1.dist-info/RECORD +168 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/WHEEL +1 -1
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/entry_points.txt +0 -3
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +3 -2
- pyinfra_cli/exceptions.py +75 -43
- pyinfra_cli/inventory.py +52 -31
- pyinfra_cli/log.py +10 -2
- pyinfra_cli/main.py +88 -65
- pyinfra_cli/prints.py +37 -109
- pyinfra_cli/util.py +15 -10
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +9 -9
- tests/test_api/test_api_deploys.py +15 -19
- tests/test_api/test_api_facts.py +4 -5
- tests/test_api/test_api_operations.py +18 -20
- tests/test_api/test_api_util.py +41 -2
- tests/test_cli/test_cli.py +14 -50
- tests/test_cli/test_cli_deploy.py +10 -12
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/test_cli_inventory.py +66 -0
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_dockerssh.py +11 -8
- tests/test_connectors/test_ssh.py +88 -23
- tests/test_connectors/test_sshuserclient.py +1 -1
- tests/test_connectors/test_terraform.py +11 -8
- tests/test_connectors/test_vagrant.py +6 -6
- pyinfra/connectors/ansible.py +0 -175
- pyinfra/connectors/mech.py +0 -189
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -312
- pyinfra/facts/windows.py +0 -366
- pyinfra/facts/windows_files.py +0 -90
- pyinfra/operations/windows.py +0 -59
- pyinfra/operations/windows_files.py +0 -538
- pyinfra-3.0.dev0.dist-info/RECORD +0 -170
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/top_level.txt +0 -0
pyinfra/connectors/vagrant.py
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
The ``@vagrant`` connector reads the current Vagrant status and generates an
|
|
3
|
-
inventory for any running VMs.
|
|
4
|
-
|
|
5
|
-
.. code:: shell
|
|
6
|
-
|
|
7
|
-
# Run on all hosts
|
|
8
|
-
pyinfra @vagrant ...
|
|
9
|
-
|
|
10
|
-
# Run on a specific VM
|
|
11
|
-
pyinfra @vagrant/my-vm-name ...
|
|
12
|
-
|
|
13
|
-
# Run on multiple named VMs
|
|
14
|
-
pyinfra @vagrant/my-vm-name,@vagrant/another-vm-name ...
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
1
|
import json
|
|
18
2
|
from os import path
|
|
19
3
|
from queue import Queue
|
|
@@ -109,13 +93,13 @@ def _make_name_data(host):
|
|
|
109
93
|
"ssh_hostname": host["HostName"],
|
|
110
94
|
}
|
|
111
95
|
|
|
112
|
-
for config_key, data_key in (
|
|
113
|
-
("Port", "ssh_port"),
|
|
114
|
-
("User", "ssh_user"),
|
|
115
|
-
("IdentityFile", "ssh_key"),
|
|
96
|
+
for config_key, data_key, data_cast in (
|
|
97
|
+
("Port", "ssh_port", int),
|
|
98
|
+
("User", "ssh_user", str),
|
|
99
|
+
("IdentityFile", "ssh_key", str),
|
|
116
100
|
):
|
|
117
101
|
if config_key in host:
|
|
118
|
-
data[data_key] = host[config_key]
|
|
102
|
+
data[data_key] = data_cast(host[config_key])
|
|
119
103
|
|
|
120
104
|
# Update any configured JSON data
|
|
121
105
|
if vagrant_host in vagrant_options.get("data", {}):
|
|
@@ -131,9 +115,25 @@ def _make_name_data(host):
|
|
|
131
115
|
|
|
132
116
|
|
|
133
117
|
class VagrantInventoryConnector(BaseConnector):
|
|
118
|
+
"""
|
|
119
|
+
The ``@vagrant`` connector reads the current Vagrant status and generates an
|
|
120
|
+
inventory for any running VMs.
|
|
121
|
+
|
|
122
|
+
.. code:: shell
|
|
123
|
+
|
|
124
|
+
# Run on all hosts
|
|
125
|
+
pyinfra @vagrant ...
|
|
126
|
+
|
|
127
|
+
# Run on a specific VM
|
|
128
|
+
pyinfra @vagrant/my-vm-name ...
|
|
129
|
+
|
|
130
|
+
# Run on multiple named VMs
|
|
131
|
+
pyinfra @vagrant/my-vm-name,@vagrant/another-vm-name ...
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
134
|
@staticmethod
|
|
135
|
-
def make_names_data(
|
|
136
|
-
vagrant_ssh_info = get_vagrant_config(
|
|
135
|
+
def make_names_data(name=None):
|
|
136
|
+
vagrant_ssh_info = get_vagrant_config(name)
|
|
137
137
|
|
|
138
138
|
logger.debug("Got Vagrant SSH info: \n%s", vagrant_ssh_info)
|
|
139
139
|
|
|
@@ -170,10 +170,11 @@ class VagrantInventoryConnector(BaseConnector):
|
|
|
170
170
|
hosts.append(_make_name_data(current_host))
|
|
171
171
|
|
|
172
172
|
if not hosts:
|
|
173
|
-
if
|
|
173
|
+
if name:
|
|
174
174
|
raise InventoryError(
|
|
175
|
-
"No running Vagrant instances matching `{0}` found!".format(
|
|
175
|
+
"No running Vagrant instances matching `{0}` found!".format(name)
|
|
176
176
|
)
|
|
177
177
|
raise InventoryError("No running Vagrant instances found!")
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
for host in hosts:
|
|
180
|
+
yield host
|
pyinfra/context.py
CHANGED
pyinfra/facts/apk.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pyinfra.api import FactBase
|
|
2
4
|
|
|
3
5
|
from .util.packaging import parse_packages
|
|
@@ -16,8 +18,11 @@ class ApkPackages(FactBase):
|
|
|
16
18
|
}
|
|
17
19
|
"""
|
|
18
20
|
|
|
19
|
-
command
|
|
20
|
-
|
|
21
|
+
def command(self) -> str:
|
|
22
|
+
return "apk list --installed"
|
|
23
|
+
|
|
24
|
+
def requires_command(self) -> str:
|
|
25
|
+
return "apk"
|
|
21
26
|
|
|
22
27
|
default = dict
|
|
23
28
|
|
pyinfra/facts/apt.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
2
4
|
|
|
3
5
|
from pyinfra.api import FactBase
|
|
@@ -50,11 +52,14 @@ class AptSources(FactBase):
|
|
|
50
52
|
]
|
|
51
53
|
"""
|
|
52
54
|
|
|
53
|
-
command
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
def command(self) -> str:
|
|
56
|
+
return make_cat_files_command(
|
|
57
|
+
"/etc/apt/sources.list",
|
|
58
|
+
"/etc/apt/sources.list.d/*.list",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def requires_command(self) -> str:
|
|
62
|
+
return "apt" # if apt installed, above should exist
|
|
58
63
|
|
|
59
64
|
default = list
|
|
60
65
|
|
|
@@ -84,5 +89,8 @@ class AptKeys(GpgFactBase):
|
|
|
84
89
|
"""
|
|
85
90
|
|
|
86
91
|
# This requires both apt-key *and* apt-key itself requires gpg
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
def command(self) -> str:
|
|
93
|
+
return "! command -v gpg || apt-key list --with-colons"
|
|
94
|
+
|
|
95
|
+
def requires_command(self) -> str:
|
|
96
|
+
return "apt-key"
|
pyinfra/facts/brew.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
2
4
|
|
|
3
5
|
from pyinfra import logger
|
|
@@ -35,18 +37,22 @@ class BrewVersion(FactBase):
|
|
|
35
37
|
|
|
36
38
|
"""
|
|
37
39
|
|
|
38
|
-
command
|
|
39
|
-
|
|
40
|
+
def command(self) -> str:
|
|
41
|
+
return "brew --version"
|
|
42
|
+
|
|
43
|
+
def requires_command(self) -> str:
|
|
44
|
+
return "brew"
|
|
40
45
|
|
|
41
46
|
@staticmethod
|
|
42
47
|
def default():
|
|
43
48
|
return [0, 0, 0]
|
|
44
49
|
|
|
45
50
|
def process(self, output):
|
|
46
|
-
|
|
51
|
+
out = list(output)[0]
|
|
52
|
+
m = VERSION_MATCHER.match(out)
|
|
47
53
|
if m is not None:
|
|
48
54
|
return [int(m.group(key)) for key in ["major", "minor", "patch"]]
|
|
49
|
-
logger.warning("could not parse version string from brew: %s",
|
|
55
|
+
logger.warning("could not parse version string from brew: %s", out)
|
|
50
56
|
return self.default()
|
|
51
57
|
|
|
52
58
|
|
|
@@ -61,8 +67,11 @@ class BrewPackages(FactBase):
|
|
|
61
67
|
}
|
|
62
68
|
"""
|
|
63
69
|
|
|
64
|
-
command
|
|
65
|
-
|
|
70
|
+
def command(self) -> str:
|
|
71
|
+
return "brew list --versions"
|
|
72
|
+
|
|
73
|
+
def requires_command(self) -> str:
|
|
74
|
+
return "brew"
|
|
66
75
|
|
|
67
76
|
default = dict
|
|
68
77
|
|
|
@@ -81,11 +90,14 @@ class BrewCasks(BrewPackages):
|
|
|
81
90
|
}
|
|
82
91
|
"""
|
|
83
92
|
|
|
84
|
-
command
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
def command(self) -> str:
|
|
94
|
+
return (
|
|
95
|
+
r'if brew --version | grep -q -e "Homebrew\ +(1\.|2\.[0-5]).*" 1>/dev/null;'
|
|
96
|
+
r"then brew cask list --versions; else brew list --cask --versions; fi"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def requires_command(self) -> str:
|
|
100
|
+
return "brew"
|
|
89
101
|
|
|
90
102
|
|
|
91
103
|
class BrewTaps(FactBase):
|
|
@@ -93,8 +105,11 @@ class BrewTaps(FactBase):
|
|
|
93
105
|
Returns a list of brew taps.
|
|
94
106
|
"""
|
|
95
107
|
|
|
96
|
-
command
|
|
97
|
-
|
|
108
|
+
def command(self) -> str:
|
|
109
|
+
return "brew tap"
|
|
110
|
+
|
|
111
|
+
def requires_command(self) -> str:
|
|
112
|
+
return "brew"
|
|
98
113
|
|
|
99
114
|
default = list
|
|
100
115
|
|
pyinfra/facts/bsdinit.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from .sysvinit import InitdStatus
|
|
2
4
|
|
|
3
5
|
|
|
@@ -7,11 +9,12 @@ class RcdStatus(InitdStatus):
|
|
|
7
9
|
BSD init scripts are well behaved and as such their output can be trusted.
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
|
-
command
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
def command(self) -> str:
|
|
13
|
+
return """
|
|
14
|
+
for SERVICE in `find /etc/rc.d /usr/local/etc/rc.d -type f`; do
|
|
15
|
+
$SERVICE status 2> /dev/null || $SERVICE check 2> /dev/null
|
|
16
|
+
echo "`basename $SERVICE`=$?"
|
|
17
|
+
done
|
|
18
|
+
"""
|
|
16
19
|
|
|
17
20
|
default = dict
|
pyinfra/facts/cargo.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# encoding: utf8
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
from pyinfra.api import FactBase
|
|
4
6
|
|
|
5
7
|
from .util.packaging import parse_packages
|
|
@@ -20,10 +22,11 @@ class CargoPackages(FactBase):
|
|
|
20
22
|
|
|
21
23
|
default = dict
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def command(self):
|
|
25
|
+
def command(self) -> str:
|
|
26
26
|
return "cargo install --list"
|
|
27
27
|
|
|
28
|
+
def requires_command(self) -> str:
|
|
29
|
+
return "cargo"
|
|
30
|
+
|
|
28
31
|
def process(self, output):
|
|
29
32
|
return parse_packages(CARGO_REGEX, output)
|
pyinfra/facts/choco.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pyinfra.api import FactBase
|
|
2
4
|
|
|
3
5
|
from .util.packaging import parse_packages
|
|
@@ -16,7 +18,9 @@ class ChocoPackages(FactBase):
|
|
|
16
18
|
}
|
|
17
19
|
"""
|
|
18
20
|
|
|
19
|
-
command
|
|
21
|
+
def command(self) -> str:
|
|
22
|
+
return "choco list"
|
|
23
|
+
|
|
20
24
|
shell_executable = "ps"
|
|
21
25
|
|
|
22
26
|
default = dict
|
|
@@ -30,8 +34,8 @@ class ChocoVersion(FactBase):
|
|
|
30
34
|
Returns the choco (Chocolatey) version.
|
|
31
35
|
"""
|
|
32
36
|
|
|
33
|
-
command
|
|
37
|
+
def command(self) -> str:
|
|
38
|
+
return "choco --version"
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
def process(output):
|
|
40
|
+
def process(self, output):
|
|
37
41
|
return "".join(output).replace("\n", "")
|
pyinfra/facts/deb.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
4
|
+
import shlex
|
|
2
5
|
|
|
3
6
|
from pyinfra.api import FactBase
|
|
4
7
|
|
|
@@ -13,8 +16,11 @@ class DebArch(FactBase):
|
|
|
13
16
|
Returns the architecture string used in apt repository sources, eg ``amd64``.
|
|
14
17
|
"""
|
|
15
18
|
|
|
16
|
-
command
|
|
17
|
-
|
|
19
|
+
def command(self) -> str:
|
|
20
|
+
return "dpkg --print-architecture"
|
|
21
|
+
|
|
22
|
+
def requires_command(self) -> str:
|
|
23
|
+
return "dpkg"
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
class DebPackages(FactBase):
|
|
@@ -28,8 +34,11 @@ class DebPackages(FactBase):
|
|
|
28
34
|
}
|
|
29
35
|
"""
|
|
30
36
|
|
|
31
|
-
command
|
|
32
|
-
|
|
37
|
+
def command(self) -> str:
|
|
38
|
+
return "dpkg -l"
|
|
39
|
+
|
|
40
|
+
def requires_command(self) -> str:
|
|
41
|
+
return "dpkg"
|
|
33
42
|
|
|
34
43
|
default = dict
|
|
35
44
|
|
|
@@ -48,14 +57,17 @@ class DebPackage(FactBase):
|
|
|
48
57
|
"""
|
|
49
58
|
|
|
50
59
|
_regexes = {
|
|
51
|
-
"name": r"^Package
|
|
52
|
-
"version": r"^Version
|
|
60
|
+
"name": r"^Package:\s+({0})$".format(DEB_PACKAGE_NAME_REGEX),
|
|
61
|
+
"version": r"^Version:\s+({0})$".format(DEB_PACKAGE_VERSION_REGEX),
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
requires_command
|
|
64
|
+
def requires_command(self, package) -> str:
|
|
65
|
+
return "dpkg"
|
|
56
66
|
|
|
57
|
-
def command(self,
|
|
58
|
-
return "! test -e {0} && (dpkg -s {0} 2>/dev/null || true) || dpkg -I {0}".format(
|
|
67
|
+
def command(self, package):
|
|
68
|
+
return "! test -e {0} && (dpkg -s {0} 2>/dev/null || true) || dpkg -I {0}".format(
|
|
69
|
+
shlex.quote(package)
|
|
70
|
+
)
|
|
59
71
|
|
|
60
72
|
def process(self, output):
|
|
61
73
|
data = {}
|
pyinfra/facts/dnf.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pyinfra.api import FactBase
|
|
2
4
|
|
|
3
5
|
from .util import make_cat_files_command
|
|
@@ -21,12 +23,15 @@ class DnfRepositories(FactBase):
|
|
|
21
23
|
]
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
command
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
def command(self) -> str:
|
|
27
|
+
return make_cat_files_command(
|
|
28
|
+
"/etc/dnf.conf",
|
|
29
|
+
"/etc/dnf.repos.d/*.repo",
|
|
30
|
+
"/etc/yum.repos.d/*.repo",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def requires_command(self) -> str:
|
|
34
|
+
return "dnf"
|
|
30
35
|
|
|
31
36
|
default = list
|
|
32
37
|
|
pyinfra/facts/docker.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import json
|
|
2
4
|
|
|
3
5
|
from pyinfra.api import FactBase
|
|
@@ -7,7 +9,9 @@ class DockerFactBase(FactBase):
|
|
|
7
9
|
abstract = True
|
|
8
10
|
|
|
9
11
|
docker_type: str
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
14
|
+
return "docker"
|
|
11
15
|
|
|
12
16
|
def process(self, output):
|
|
13
17
|
output = "".join(output)
|
|
@@ -19,7 +23,8 @@ class DockerSystemInfo(DockerFactBase):
|
|
|
19
23
|
Returns ``docker system info`` output in JSON format.
|
|
20
24
|
"""
|
|
21
25
|
|
|
22
|
-
command
|
|
26
|
+
def command(self) -> str:
|
|
27
|
+
return 'docker system info --format="{{json .}}"'
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
# All Docker objects
|
|
@@ -31,7 +36,8 @@ class DockerContainers(DockerFactBase):
|
|
|
31
36
|
Returns ``docker inspect`` output for all Docker containers.
|
|
32
37
|
"""
|
|
33
38
|
|
|
34
|
-
command
|
|
39
|
+
def command(self) -> str:
|
|
40
|
+
return "docker container inspect `docker ps -qa`"
|
|
35
41
|
|
|
36
42
|
|
|
37
43
|
class DockerImages(DockerFactBase):
|
|
@@ -39,7 +45,8 @@ class DockerImages(DockerFactBase):
|
|
|
39
45
|
Returns ``docker inspect`` output for all Docker images.
|
|
40
46
|
"""
|
|
41
47
|
|
|
42
|
-
command
|
|
48
|
+
def command(self) -> str:
|
|
49
|
+
return "docker image inspect `docker images -q`"
|
|
43
50
|
|
|
44
51
|
|
|
45
52
|
class DockerNetworks(DockerFactBase):
|
|
@@ -47,7 +54,8 @@ class DockerNetworks(DockerFactBase):
|
|
|
47
54
|
Returns ``docker inspect`` output for all Docker networks.
|
|
48
55
|
"""
|
|
49
56
|
|
|
50
|
-
command
|
|
57
|
+
def command(self) -> str:
|
|
58
|
+
return "docker network inspect `docker network ls -q`"
|
|
51
59
|
|
|
52
60
|
|
|
53
61
|
# Single Docker objects
|
|
@@ -84,3 +92,20 @@ class DockerNetwork(DockerSingleMixin):
|
|
|
84
92
|
"""
|
|
85
93
|
|
|
86
94
|
docker_type = "network"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DockerVolumes(DockerFactBase):
|
|
98
|
+
"""
|
|
99
|
+
Returns ``docker inspect`` output for all Docker volumes.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def command(self) -> str:
|
|
103
|
+
return "docker volume inspect `docker volume ls -q`"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class DockerVolume(DockerSingleMixin):
|
|
107
|
+
"""
|
|
108
|
+
Returns ``docker inspect`` output for a single Docker container.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
docker_type = "volume"
|
pyinfra/facts/files.py
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
The files facts provide information about the filesystem and it's contents on the target host.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import re
|
|
6
8
|
import stat
|
|
7
9
|
from datetime import datetime
|
|
8
|
-
from typing import List, Tuple
|
|
10
|
+
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
|
11
|
+
|
|
12
|
+
from typing_extensions import Literal, NotRequired, TypedDict
|
|
9
13
|
|
|
10
14
|
from pyinfra.api.command import QuoteString, make_formatted_string_command
|
|
11
15
|
from pyinfra.api.facts import FactBase
|
|
@@ -64,7 +68,25 @@ def _parse_mode(mode: str) -> int:
|
|
|
64
68
|
return int(oct(out)[2:])
|
|
65
69
|
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
def _parse_datetime(value: str) -> Optional[datetime]:
|
|
72
|
+
value = try_int(value)
|
|
73
|
+
if isinstance(value, int):
|
|
74
|
+
return datetime.utcfromtimestamp(value)
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class FileDict(TypedDict):
|
|
79
|
+
mode: int
|
|
80
|
+
size: Union[int, str]
|
|
81
|
+
atime: Optional[datetime]
|
|
82
|
+
mtime: Optional[datetime]
|
|
83
|
+
ctime: Optional[datetime]
|
|
84
|
+
user: str
|
|
85
|
+
group: str
|
|
86
|
+
link_target: NotRequired[str]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class File(FactBase[Union[FileDict, Literal[False], None]]):
|
|
68
90
|
"""
|
|
69
91
|
Returns information about a file on the remote system:
|
|
70
92
|
|
|
@@ -98,36 +120,23 @@ class File(FactBase):
|
|
|
98
120
|
bsd_stat_command=BSD_STAT_COMMAND,
|
|
99
121
|
)
|
|
100
122
|
|
|
101
|
-
def process(self, output):
|
|
123
|
+
def process(self, output) -> Union[FileDict, Literal[False], None]:
|
|
102
124
|
match = re.match(STAT_REGEX, output[0])
|
|
103
125
|
if not match:
|
|
104
126
|
return None
|
|
105
127
|
|
|
106
|
-
|
|
107
|
-
path_type =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if key == "mode":
|
|
119
|
-
path_type = FLAG_TO_TYPE[value[0]]
|
|
120
|
-
value = _parse_mode(value[1:])
|
|
121
|
-
|
|
122
|
-
elif key == "size":
|
|
123
|
-
value = try_int(value)
|
|
124
|
-
|
|
125
|
-
elif key in ("atime", "mtime", "ctime"):
|
|
126
|
-
value = try_int(value)
|
|
127
|
-
if isinstance(value, int):
|
|
128
|
-
value = datetime.utcfromtimestamp(value)
|
|
129
|
-
|
|
130
|
-
data[key] = value
|
|
128
|
+
mode = match.group(3)
|
|
129
|
+
path_type = FLAG_TO_TYPE[mode[0]]
|
|
130
|
+
|
|
131
|
+
data: FileDict = {
|
|
132
|
+
"user": match.group(1),
|
|
133
|
+
"group": match.group(2),
|
|
134
|
+
"mode": _parse_mode(mode[1:]),
|
|
135
|
+
"atime": _parse_datetime(match.group(4)),
|
|
136
|
+
"mtime": _parse_datetime(match.group(5)),
|
|
137
|
+
"ctime": _parse_datetime(match.group(6)),
|
|
138
|
+
"size": try_int(match.group(7)),
|
|
139
|
+
}
|
|
131
140
|
|
|
132
141
|
if path_type != self.type:
|
|
133
142
|
return False
|
|
@@ -205,7 +214,13 @@ class Socket(File):
|
|
|
205
214
|
type = "socket"
|
|
206
215
|
|
|
207
216
|
|
|
208
|
-
|
|
217
|
+
if TYPE_CHECKING:
|
|
218
|
+
FactBaseOptionalStr = FactBase[Optional[str]]
|
|
219
|
+
else:
|
|
220
|
+
FactBaseOptionalStr = FactBase
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class HashFileFactBase(FactBaseOptionalStr):
|
|
209
224
|
_raw_cmd: str
|
|
210
225
|
_regexes: Tuple[str, str]
|
|
211
226
|
|
|
@@ -229,13 +244,14 @@ class HashFileFactBase(FactBase):
|
|
|
229
244
|
self.path = path
|
|
230
245
|
return make_formatted_string_command(self._raw_cmd, QuoteString(path))
|
|
231
246
|
|
|
232
|
-
def process(self, output):
|
|
247
|
+
def process(self, output) -> Optional[str]:
|
|
233
248
|
output = output[0]
|
|
234
249
|
escaped_path = re.escape(self.path)
|
|
235
250
|
for regex in self._regexes:
|
|
236
251
|
matches = re.match(regex % escaped_path, output)
|
|
237
252
|
if matches:
|
|
238
253
|
return matches.group(1)
|
|
254
|
+
return None
|
|
239
255
|
|
|
240
256
|
|
|
241
257
|
class Sha1File(HashFileFactBase, digits=40, cmds=["sha1sum", "shasum", "sha1"]):
|
|
@@ -296,8 +312,7 @@ class FindFilesBase(FactBase):
|
|
|
296
312
|
default = list
|
|
297
313
|
type_flag: str
|
|
298
314
|
|
|
299
|
-
|
|
300
|
-
def process(output):
|
|
315
|
+
def process(self, output):
|
|
301
316
|
return output
|
|
302
317
|
|
|
303
318
|
def command(self, path, quote_path=True):
|
|
@@ -337,7 +352,8 @@ class Flags(FactBase):
|
|
|
337
352
|
Returns a list of the file flags set for the specified file or directory.
|
|
338
353
|
"""
|
|
339
354
|
|
|
340
|
-
requires_command
|
|
355
|
+
def requires_command(self, path) -> str:
|
|
356
|
+
return "chflags" # don't try to retrieve them if we can't set them
|
|
341
357
|
|
|
342
358
|
def command(self, path):
|
|
343
359
|
return make_formatted_string_command(
|
pyinfra/facts/gem.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from pyinfra.api import FactBase
|
|
2
4
|
|
|
3
5
|
from .util.packaging import parse_packages
|
|
@@ -16,8 +18,11 @@ class GemPackages(FactBase):
|
|
|
16
18
|
}
|
|
17
19
|
"""
|
|
18
20
|
|
|
19
|
-
command
|
|
20
|
-
|
|
21
|
+
def command(self) -> str:
|
|
22
|
+
return "gem list --local"
|
|
23
|
+
|
|
24
|
+
def requires_command(self) -> str:
|
|
25
|
+
return "gem"
|
|
21
26
|
|
|
22
27
|
default = dict
|
|
23
28
|
|