sansible 0.2.0__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.
Files changed (64) hide show
  1. sansible/__init__.py +27 -0
  2. sansible/cli/__init__.py +1 -0
  3. sansible/cli/inventory.py +136 -0
  4. sansible/cli/main.py +113 -0
  5. sansible/cli/playbook.py +213 -0
  6. sansible/compat_scan.py +470 -0
  7. sansible/connections/__init__.py +15 -0
  8. sansible/connections/base.py +176 -0
  9. sansible/connections/local.py +193 -0
  10. sansible/connections/ssh_asyncssh.py +269 -0
  11. sansible/connections/winrm_psrp.py +389 -0
  12. sansible/engine/__init__.py +35 -0
  13. sansible/engine/errors.py +170 -0
  14. sansible/engine/inventory.py +552 -0
  15. sansible/engine/playbook.py +631 -0
  16. sansible/engine/results.py +199 -0
  17. sansible/engine/runner.py +614 -0
  18. sansible/engine/scheduler.py +450 -0
  19. sansible/engine/templating.py +333 -0
  20. sansible/inventory/__init__.py +13 -0
  21. sansible/inventory/group.py +93 -0
  22. sansible/inventory/host.py +68 -0
  23. sansible/inventory/parser.py +316 -0
  24. sansible/modules/__init__.py +14 -0
  25. sansible/modules/base.py +300 -0
  26. sansible/modules/builtin_assert.py +69 -0
  27. sansible/modules/builtin_command.py +91 -0
  28. sansible/modules/builtin_copy.py +193 -0
  29. sansible/modules/builtin_debug.py +48 -0
  30. sansible/modules/builtin_fail.py +32 -0
  31. sansible/modules/builtin_file.py +250 -0
  32. sansible/modules/builtin_lineinfile.py +159 -0
  33. sansible/modules/builtin_raw.py +54 -0
  34. sansible/modules/builtin_set_fact.py +39 -0
  35. sansible/modules/builtin_setup.py +171 -0
  36. sansible/modules/builtin_shell.py +92 -0
  37. sansible/modules/builtin_stat.py +69 -0
  38. sansible/modules/builtin_template.py +159 -0
  39. sansible/modules/builtin_wait_for.py +129 -0
  40. sansible/modules/win_command.py +82 -0
  41. sansible/modules/win_copy.py +168 -0
  42. sansible/modules/win_file.py +205 -0
  43. sansible/modules/win_lineinfile.py +155 -0
  44. sansible/modules/win_service.py +156 -0
  45. sansible/modules/win_shell.py +81 -0
  46. sansible/modules/win_stat.py +96 -0
  47. sansible/modules/win_wait_for.py +130 -0
  48. sansible/platform/__init__.py +16 -0
  49. sansible/platform/concurrency.py +232 -0
  50. sansible/platform/fs.py +216 -0
  51. sansible/platform/locks.py +187 -0
  52. sansible/platform/paths.py +132 -0
  53. sansible/platform/proc.py +247 -0
  54. sansible/platform/tty.py +198 -0
  55. sansible/platform/users.py +161 -0
  56. sansible/py.typed +0 -0
  57. sansible/release.py +13 -0
  58. sansible/san_cli.py +206 -0
  59. sansible-0.2.0.dist-info/METADATA +320 -0
  60. sansible-0.2.0.dist-info/RECORD +64 -0
  61. sansible-0.2.0.dist-info/WHEEL +5 -0
  62. sansible-0.2.0.dist-info/entry_points.txt +6 -0
  63. sansible-0.2.0.dist-info/licenses/LICENSE +21 -0
  64. sansible-0.2.0.dist-info/top_level.txt +1 -0
sansible/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2024 Sansible Contributors
2
+ # MIT License
3
+
4
+ """
5
+ Sansible: Minimal Ansible-compatible playbook runner.
6
+
7
+ A lightweight, pure-Python alternative to Ansible for running simple playbooks
8
+ on Windows and Linux hosts via SSH and WinRM.
9
+
10
+ Features:
11
+ - Pure Python wheel (py3-none-any) — no compiled extensions
12
+ - Windows-native control node — no WSL required
13
+ - SSH (asyncssh) and WinRM (pypsrp) connections
14
+ - Subset of Ansible module and feature support
15
+
16
+ This package exposes the main CLI entry point and release metadata.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from sansible.release import __version__, __author__, __codename__
22
+
23
+ __all__ = [
24
+ "__version__",
25
+ "__author__",
26
+ "__codename__",
27
+ ]
@@ -0,0 +1 @@
1
+ """CLI package for sansible commands."""
@@ -0,0 +1,136 @@
1
+ """
2
+ Inventory CLI entrypoint for sansible-inventory.
3
+
4
+ Usage:
5
+ sansible-inventory --version
6
+ sansible-inventory --help
7
+ sansible-inventory -i inventory --list
8
+ sansible-inventory -i inventory --host <hostname>
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import sys
14
+ import platform
15
+
16
+ from sansible import __version__
17
+
18
+
19
+ def get_version_string() -> str:
20
+ """Generate a detailed version string."""
21
+ python_version = platform.python_version()
22
+ os_info = f"{platform.system()} {platform.release()}"
23
+ return (
24
+ f"sansible-inventory {__version__}\n"
25
+ f" python: {python_version}\n"
26
+ f" platform: {os_info}\n"
27
+ f" pure-python: yes (no compiled extensions)"
28
+ )
29
+
30
+
31
+ def create_parser() -> argparse.ArgumentParser:
32
+ """Create the argument parser for sansible-inventory."""
33
+ parser = argparse.ArgumentParser(
34
+ prog="sansible-inventory",
35
+ description="Show Ansible inventory information (pure-Python, Windows-native)",
36
+ formatter_class=argparse.RawDescriptionHelpFormatter,
37
+ epilog="""
38
+ Examples:
39
+ sansible-inventory -i inventory.ini --list
40
+ sansible-inventory -i hosts --host webserver1
41
+ sansible-inventory -i inventory/ --graph
42
+ """,
43
+ )
44
+
45
+ parser.add_argument(
46
+ "--version",
47
+ action="version",
48
+ version=get_version_string(),
49
+ )
50
+
51
+ parser.add_argument(
52
+ "-i", "--inventory",
53
+ dest="inventory",
54
+ default=None,
55
+ help="Inventory file or directory",
56
+ )
57
+
58
+ parser.add_argument(
59
+ "--list",
60
+ action="store_true",
61
+ dest="list_hosts",
62
+ help="Output all hosts info (JSON)",
63
+ )
64
+
65
+ parser.add_argument(
66
+ "--host",
67
+ dest="host",
68
+ default=None,
69
+ help="Output specific host info (JSON)",
70
+ )
71
+
72
+ parser.add_argument(
73
+ "--graph",
74
+ action="store_true",
75
+ help="Output inventory graph",
76
+ )
77
+
78
+ parser.add_argument(
79
+ "-y", "--yaml",
80
+ action="store_true",
81
+ help="Output in YAML format",
82
+ )
83
+
84
+ parser.add_argument(
85
+ "-v", "--verbose",
86
+ action="count",
87
+ default=0,
88
+ help="Increase verbosity",
89
+ )
90
+
91
+ return parser
92
+
93
+
94
+ def main(args: list[str] | None = None) -> int:
95
+ """Main entrypoint for sansible-inventory CLI."""
96
+ parser = create_parser()
97
+ parsed = parser.parse_args(args)
98
+
99
+ # If no action specified, show help
100
+ if not parsed.list_hosts and not parsed.host and not parsed.graph:
101
+ parser.print_help()
102
+ return 0
103
+
104
+ # TODO: Implement inventory parsing and output
105
+ if parsed.list_hosts:
106
+ # Return empty inventory structure for now
107
+ inventory = {
108
+ "_meta": {
109
+ "hostvars": {}
110
+ },
111
+ "all": {
112
+ "children": ["ungrouped"]
113
+ },
114
+ "ungrouped": {
115
+ "hosts": []
116
+ }
117
+ }
118
+ print(json.dumps(inventory, indent=2))
119
+ return 0
120
+
121
+ if parsed.host:
122
+ # Return empty hostvars for now
123
+ hostvars: dict[str, str] = {}
124
+ print(json.dumps(hostvars, indent=2))
125
+ return 0
126
+
127
+ if parsed.graph:
128
+ print("@all:")
129
+ print(" |--@ungrouped:")
130
+ return 0
131
+
132
+ return 0
133
+
134
+
135
+ if __name__ == "__main__":
136
+ sys.exit(main())
sansible/cli/main.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ Main CLI entrypoint for sansible.
3
+
4
+ Usage:
5
+ sansible --version
6
+ sansible --help
7
+ sansible <host-pattern> -m <module> [-a <args>]
8
+ """
9
+
10
+ import argparse
11
+ import sys
12
+ import platform
13
+
14
+ from sansible import __version__
15
+
16
+
17
+ def get_version_string() -> str:
18
+ """Generate a detailed version string."""
19
+ python_version = platform.python_version()
20
+ os_info = f"{platform.system()} {platform.release()}"
21
+ return (
22
+ f"sansible {__version__}\n"
23
+ f" python: {python_version}\n"
24
+ f" platform: {os_info}\n"
25
+ f" pure-python: yes (no compiled extensions)"
26
+ )
27
+
28
+
29
+ def create_parser() -> argparse.ArgumentParser:
30
+ """Create the argument parser for sansible."""
31
+ parser = argparse.ArgumentParser(
32
+ prog="sansible",
33
+ description="Run ad-hoc Ansible commands (pure-Python, Windows-native)",
34
+ formatter_class=argparse.RawDescriptionHelpFormatter,
35
+ epilog="""
36
+ Examples:
37
+ sansible all -m ping
38
+ sansible webservers -m command -a "uptime"
39
+ sansible localhost -m setup
40
+ """,
41
+ )
42
+
43
+ parser.add_argument(
44
+ "--version",
45
+ action="version",
46
+ version=get_version_string(),
47
+ )
48
+
49
+ parser.add_argument(
50
+ "pattern",
51
+ nargs="?",
52
+ default=None,
53
+ help="Host pattern to target",
54
+ )
55
+
56
+ parser.add_argument(
57
+ "-i", "--inventory",
58
+ dest="inventory",
59
+ default=None,
60
+ help="Inventory file or directory",
61
+ )
62
+
63
+ parser.add_argument(
64
+ "-m", "--module-name",
65
+ dest="module",
66
+ default="command",
67
+ help="Module to execute (default: command)",
68
+ )
69
+
70
+ parser.add_argument(
71
+ "-a", "--args",
72
+ dest="module_args",
73
+ default="",
74
+ help="Module arguments",
75
+ )
76
+
77
+ parser.add_argument(
78
+ "-v", "--verbose",
79
+ action="count",
80
+ default=0,
81
+ help="Increase verbosity (-v, -vv, -vvv)",
82
+ )
83
+
84
+ parser.add_argument(
85
+ "-C", "--check",
86
+ action="store_true",
87
+ help="Run in check mode (dry run)",
88
+ )
89
+
90
+ return parser
91
+
92
+
93
+ def main(args: list[str] | None = None) -> int:
94
+ """Main entrypoint for sansible CLI."""
95
+ parser = create_parser()
96
+ parsed = parser.parse_args(args)
97
+
98
+ # If no pattern provided, just show help
99
+ if parsed.pattern is None:
100
+ parser.print_help()
101
+ return 0
102
+
103
+ # TODO: Implement ad-hoc command execution
104
+ print(f"[sansible] Target: {parsed.pattern}")
105
+ print(f"[sansible] Module: {parsed.module}")
106
+ print(f"[sansible] Args: {parsed.module_args}")
107
+ print(f"[sansible] Status: Not yet implemented - M1 milestone in progress")
108
+
109
+ return 0
110
+
111
+
112
+ if __name__ == "__main__":
113
+ sys.exit(main())
@@ -0,0 +1,213 @@
1
+ """
2
+ Playbook CLI entrypoint for sansible-playbook.
3
+
4
+ Usage:
5
+ sansible-playbook --version
6
+ sansible-playbook --help
7
+ sansible-playbook -i inventory playbook.yml
8
+ """
9
+
10
+ import argparse
11
+ import sys
12
+ import platform
13
+
14
+ from sansible import __version__
15
+
16
+
17
+ def get_version_string() -> str:
18
+ """Generate a detailed version string."""
19
+ python_version = platform.python_version()
20
+ os_info = f"{platform.system()} {platform.release()}"
21
+ return (
22
+ f"sansible-playbook {__version__}\n"
23
+ f" python: {python_version}\n"
24
+ f" platform: {os_info}\n"
25
+ f" pure-python: yes (no compiled extensions)"
26
+ )
27
+
28
+
29
+ def create_parser() -> argparse.ArgumentParser:
30
+ """Create the argument parser for sansible-playbook."""
31
+ parser = argparse.ArgumentParser(
32
+ prog="sansible-playbook",
33
+ description="Run Ansible playbooks (pure-Python, Windows-native)",
34
+ formatter_class=argparse.RawDescriptionHelpFormatter,
35
+ epilog="""
36
+ Examples:
37
+ sansible-playbook -i inventory.ini site.yml
38
+ sansible-playbook -i hosts playbook.yml --check
39
+ sansible-playbook -i inventory/ deploy.yml -v
40
+ """,
41
+ )
42
+
43
+ parser.add_argument(
44
+ "--version",
45
+ action="version",
46
+ version=get_version_string(),
47
+ )
48
+
49
+ parser.add_argument(
50
+ "playbook",
51
+ nargs="*",
52
+ help="Playbook file(s) to run",
53
+ )
54
+
55
+ parser.add_argument(
56
+ "-i", "--inventory",
57
+ dest="inventory",
58
+ default=None,
59
+ help="Inventory file or directory",
60
+ )
61
+
62
+ parser.add_argument(
63
+ "-v", "--verbose",
64
+ action="count",
65
+ default=0,
66
+ help="Increase verbosity (-v, -vv, -vvv)",
67
+ )
68
+
69
+ parser.add_argument(
70
+ "-C", "--check",
71
+ action="store_true",
72
+ help="Run in check mode (dry run)",
73
+ )
74
+
75
+ parser.add_argument(
76
+ "--diff",
77
+ action="store_true",
78
+ help="Show differences when changing files",
79
+ )
80
+
81
+ parser.add_argument(
82
+ "-l", "--limit",
83
+ dest="limit",
84
+ default=None,
85
+ help="Limit to specific hosts/groups",
86
+ )
87
+
88
+ parser.add_argument(
89
+ "-t", "--tags",
90
+ dest="tags",
91
+ default=None,
92
+ help="Only run plays and tasks tagged with these values",
93
+ )
94
+
95
+ parser.add_argument(
96
+ "--skip-tags",
97
+ dest="skip_tags",
98
+ default=None,
99
+ help="Skip plays and tasks tagged with these values",
100
+ )
101
+
102
+ parser.add_argument(
103
+ "-f", "--forks",
104
+ dest="forks",
105
+ type=int,
106
+ default=5,
107
+ help="Number of parallel processes (default: 5)",
108
+ )
109
+
110
+ parser.add_argument(
111
+ "-e", "--extra-vars",
112
+ dest="extra_vars",
113
+ action="append",
114
+ default=[],
115
+ help="Extra variables as key=value or JSON (can be repeated)",
116
+ )
117
+
118
+ parser.add_argument(
119
+ "--artifacts-dir",
120
+ dest="artifacts_dir",
121
+ default=None,
122
+ help="Directory to write run artifacts (default: ./artifacts/<timestamp>)",
123
+ )
124
+
125
+ parser.add_argument(
126
+ "--json",
127
+ action="store_true",
128
+ help="Output results in JSON format",
129
+ )
130
+
131
+ return parser
132
+
133
+
134
+ def _parse_extra_vars(extra_vars_list: list[str]) -> dict:
135
+ """Parse extra vars from command line."""
136
+ import json
137
+
138
+ result = {}
139
+ for item in extra_vars_list:
140
+ item = item.strip()
141
+
142
+ # Try JSON first
143
+ if item.startswith('{'):
144
+ try:
145
+ result.update(json.loads(item))
146
+ continue
147
+ except json.JSONDecodeError:
148
+ pass
149
+
150
+ # Try key=value format
151
+ if '=' in item:
152
+ key, _, value = item.partition('=')
153
+ key = key.strip()
154
+ value = value.strip()
155
+
156
+ # Try to parse value as JSON for complex types
157
+ try:
158
+ value = json.loads(value)
159
+ except json.JSONDecodeError:
160
+ pass # Keep as string
161
+
162
+ result[key] = value
163
+ else:
164
+ # Maybe it's a file path?
165
+ from pathlib import Path
166
+ if Path(item).exists():
167
+ import yaml
168
+ with open(item) as f:
169
+ file_vars = yaml.safe_load(f)
170
+ if isinstance(file_vars, dict):
171
+ result.update(file_vars)
172
+
173
+ return result
174
+
175
+
176
+ def main(args: list[str] | None = None) -> int:
177
+ """Main entrypoint for sansible-playbook CLI."""
178
+ parser = create_parser()
179
+ parsed = parser.parse_args(args)
180
+
181
+ # If no playbook provided, show help
182
+ if not parsed.playbook:
183
+ parser.print_help()
184
+ return 0
185
+
186
+ # Validate inventory is provided
187
+ if not parsed.inventory:
188
+ print("ERROR: Inventory (-i/--inventory) is required", file=sys.stderr)
189
+ return 3
190
+
191
+ # Parse extra vars
192
+ extra_vars = _parse_extra_vars(parsed.extra_vars)
193
+
194
+ # Create and run the playbook runner
195
+ from sansible.engine.runner import PlaybookRunner
196
+
197
+ runner = PlaybookRunner(
198
+ inventory_source=parsed.inventory,
199
+ playbook_paths=parsed.playbook,
200
+ forks=parsed.forks,
201
+ limit=parsed.limit,
202
+ check_mode=parsed.check,
203
+ diff_mode=parsed.diff,
204
+ verbosity=parsed.verbose,
205
+ extra_vars=extra_vars,
206
+ json_output=parsed.json,
207
+ )
208
+
209
+ return runner.run()
210
+
211
+
212
+ if __name__ == "__main__":
213
+ sys.exit(main())