python-rundeck 0.1.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.
- python_rundeck-0.1.0.dist-info/METADATA +454 -0
- python_rundeck-0.1.0.dist-info/RECORD +28 -0
- python_rundeck-0.1.0.dist-info/WHEEL +4 -0
- python_rundeck-0.1.0.dist-info/licenses/LICENSE +21 -0
- rundeck/__init__.py +39 -0
- rundeck/base.py +231 -0
- rundeck/client.py +341 -0
- rundeck/config.py +166 -0
- rundeck/const.py +45 -0
- rundeck/exceptions.py +164 -0
- rundeck/py.typed +1 -0
- rundeck/v1/__init__.py +1 -0
- rundeck/v1/objects/__init__.py +1 -0
- rundeck/v1/objects/adhoc.py +93 -0
- rundeck/v1/objects/config_management.py +51 -0
- rundeck/v1/objects/executions.py +327 -0
- rundeck/v1/objects/features.py +44 -0
- rundeck/v1/objects/jobs.py +329 -0
- rundeck/v1/objects/key_storage.py +79 -0
- rundeck/v1/objects/metrics.py +37 -0
- rundeck/v1/objects/plugins.py +38 -0
- rundeck/v1/objects/projects.py +421 -0
- rundeck/v1/objects/scheduler.py +50 -0
- rundeck/v1/objects/scm.py +229 -0
- rundeck/v1/objects/system.py +140 -0
- rundeck/v1/objects/tokens.py +102 -0
- rundeck/v1/objects/users.py +137 -0
- rundeck/v1/objects/webhooks.py +104 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gestion des endpoints SCM (import/export) pour les projets et jobs.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rundeck.base import RundeckObjectManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _ProjectSCMBaseManager(RundeckObjectManager):
|
|
11
|
+
"""Base pour les sous-managers SCM projet (intègre l'intégration import/export)."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, rd: Any, parent: Any, integration: str) -> None:
|
|
14
|
+
super().__init__(rd, parent)
|
|
15
|
+
self.integration = integration
|
|
16
|
+
|
|
17
|
+
def _base_path(self) -> str:
|
|
18
|
+
if not self.parent:
|
|
19
|
+
raise ValueError("Parent projet requis pour SCM")
|
|
20
|
+
return f"/project/{self.parent.id}/scm/{self.integration}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ProjectSCMPluginsManager(_ProjectSCMBaseManager):
|
|
24
|
+
"""Découverte des plugins SCM pour un projet."""
|
|
25
|
+
|
|
26
|
+
def list(self) -> Any:
|
|
27
|
+
"""Liste les plugins SCM disponibles pour l'intégration."""
|
|
28
|
+
path = self._build_path("plugins")
|
|
29
|
+
return self.rd.http_get(path)
|
|
30
|
+
|
|
31
|
+
def input_fields(self, plugin_type: str) -> Any:
|
|
32
|
+
"""Champs de saisie pour un plugin donné."""
|
|
33
|
+
path = self._build_path(f"plugin/{plugin_type}/input")
|
|
34
|
+
return self.rd.http_get(path)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ProjectSCMConfigManager(_ProjectSCMBaseManager):
|
|
38
|
+
"""Configuration et cycle de vie d'un plugin SCM pour un projet."""
|
|
39
|
+
|
|
40
|
+
def setup(self, plugin_type: str, config: dict[str, Any]) -> Any:
|
|
41
|
+
"""Configure un plugin SCM pour le projet."""
|
|
42
|
+
path = self._build_path(f"plugin/{plugin_type}/setup")
|
|
43
|
+
payload = {"config": config}
|
|
44
|
+
return self.rd.http_post(path, json=payload)
|
|
45
|
+
|
|
46
|
+
def enable(self, plugin_type: str) -> Any:
|
|
47
|
+
"""Active le plugin SCM (idempotent)."""
|
|
48
|
+
path = self._build_path(f"plugin/{plugin_type}/enable")
|
|
49
|
+
return self.rd.http_post(path)
|
|
50
|
+
|
|
51
|
+
def disable(self, plugin_type: str) -> Any:
|
|
52
|
+
"""Désactive le plugin SCM (idempotent)."""
|
|
53
|
+
path = self._build_path(f"plugin/{plugin_type}/disable")
|
|
54
|
+
return self.rd.http_post(path)
|
|
55
|
+
|
|
56
|
+
def get(self) -> Any:
|
|
57
|
+
"""Récupère la configuration SCM du projet pour l'intégration."""
|
|
58
|
+
path = self._build_path("config")
|
|
59
|
+
return self.rd.http_get(path)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ProjectSCMActionsManager(_ProjectSCMBaseManager):
|
|
63
|
+
"""Statut et actions SCM pour un projet."""
|
|
64
|
+
|
|
65
|
+
def status(self) -> Any:
|
|
66
|
+
"""Statut SCM du projet pour l'intégration."""
|
|
67
|
+
path = self._build_path("status")
|
|
68
|
+
return self.rd.http_get(path)
|
|
69
|
+
|
|
70
|
+
def input_fields(self, action_id: str) -> Any:
|
|
71
|
+
"""Champs attendus pour une action SCM projet."""
|
|
72
|
+
path = self._build_path(f"action/{action_id}/input")
|
|
73
|
+
return self.rd.http_get(path)
|
|
74
|
+
|
|
75
|
+
def perform(
|
|
76
|
+
self,
|
|
77
|
+
action_id: str,
|
|
78
|
+
input_values: dict[str, Any] | None = None,
|
|
79
|
+
jobs: list[str] | None = None,
|
|
80
|
+
items: list[str] | None = None,
|
|
81
|
+
deleted: list[str] | None = None,
|
|
82
|
+
deleted_jobs: list[str] | None = None,
|
|
83
|
+
) -> Any:
|
|
84
|
+
"""Exécute une action SCM projet (ex: commit/import/sync)."""
|
|
85
|
+
payload: dict[str, Any] = {}
|
|
86
|
+
if input_values is not None:
|
|
87
|
+
payload["input"] = input_values
|
|
88
|
+
if jobs:
|
|
89
|
+
payload["jobs"] = jobs
|
|
90
|
+
if items:
|
|
91
|
+
payload["items"] = items
|
|
92
|
+
if deleted:
|
|
93
|
+
payload["deleted"] = deleted
|
|
94
|
+
if deleted_jobs:
|
|
95
|
+
payload["deletedJobs"] = deleted_jobs
|
|
96
|
+
|
|
97
|
+
path = self._build_path(f"action/{action_id}")
|
|
98
|
+
return self.rd.http_post(path, json=payload or None)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ProjectSCMIntegrationManager(RundeckObjectManager):
|
|
102
|
+
"""Regroupe les sous-managers SCM projet pour une intégration (import/export)."""
|
|
103
|
+
|
|
104
|
+
def __init__(self, rd: Any, parent: Any, integration: str) -> None:
|
|
105
|
+
super().__init__(rd, parent)
|
|
106
|
+
self.integration = integration
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def plugins(self) -> ProjectSCMPluginsManager:
|
|
110
|
+
return ProjectSCMPluginsManager(
|
|
111
|
+
self.rd, parent=self.parent, integration=self.integration
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def config(self) -> ProjectSCMConfigManager:
|
|
116
|
+
return ProjectSCMConfigManager(
|
|
117
|
+
self.rd, parent=self.parent, integration=self.integration
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def actions(self) -> ProjectSCMActionsManager:
|
|
122
|
+
return ProjectSCMActionsManager(
|
|
123
|
+
self.rd, parent=self.parent, integration=self.integration
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ProjectSCMManager(RundeckObjectManager):
|
|
128
|
+
"""Manager racine SCM pour un projet, expose import/export."""
|
|
129
|
+
|
|
130
|
+
def __init__(self, rd: Any, parent: Any) -> None:
|
|
131
|
+
if parent is None:
|
|
132
|
+
raise ValueError("Parent projet requis pour SCM")
|
|
133
|
+
super().__init__(rd, parent)
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def import_(self) -> ProjectSCMIntegrationManager:
|
|
137
|
+
"""SCM import pour le projet."""
|
|
138
|
+
return ProjectSCMIntegrationManager(
|
|
139
|
+
self.rd, parent=self.parent, integration="import"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def export(self) -> ProjectSCMIntegrationManager:
|
|
144
|
+
"""SCM export pour le projet."""
|
|
145
|
+
return ProjectSCMIntegrationManager(
|
|
146
|
+
self.rd, parent=self.parent, integration="export"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def __getattr__(self, name: str) -> Any:
|
|
150
|
+
if name == "import":
|
|
151
|
+
return self.import_
|
|
152
|
+
return super().__getattribute__(name)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class JobSCMManager(RundeckObjectManager):
|
|
156
|
+
"""Manager racine SCM pour un job, expose import/export."""
|
|
157
|
+
|
|
158
|
+
def __init__(self, rd: Any, parent: Any) -> None:
|
|
159
|
+
if parent is None:
|
|
160
|
+
raise ValueError("Parent job requis pour SCM")
|
|
161
|
+
super().__init__(rd, parent)
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def import_(self) -> "JobSCMIntegrationManager":
|
|
165
|
+
"""SCM import pour le job."""
|
|
166
|
+
return JobSCMIntegrationManager(
|
|
167
|
+
self.rd, parent=self.parent, integration="import"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def export(self) -> "JobSCMIntegrationManager":
|
|
172
|
+
"""SCM export pour le job."""
|
|
173
|
+
return JobSCMIntegrationManager(
|
|
174
|
+
self.rd, parent=self.parent, integration="export"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def __getattr__(self, name: str) -> Any:
|
|
178
|
+
if name == "import":
|
|
179
|
+
return self.import_
|
|
180
|
+
return super().__getattribute__(name)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class JobSCMIntegrationManager(RundeckObjectManager):
|
|
184
|
+
"""Opérations SCM pour un job donné et une intégration (import/export)."""
|
|
185
|
+
|
|
186
|
+
def __init__(self, rd: Any, parent: Any, integration: str) -> None:
|
|
187
|
+
super().__init__(rd, parent)
|
|
188
|
+
self.integration = integration
|
|
189
|
+
|
|
190
|
+
def _base_path(self) -> str:
|
|
191
|
+
if not self.parent:
|
|
192
|
+
raise ValueError("Parent job requis pour SCM")
|
|
193
|
+
return f"/job/{self.parent.id}/scm/{self.integration}"
|
|
194
|
+
|
|
195
|
+
def status(self) -> Any:
|
|
196
|
+
"""Statut SCM du job pour l'intégration."""
|
|
197
|
+
path = self._build_path("status")
|
|
198
|
+
return self.rd.http_get(path)
|
|
199
|
+
|
|
200
|
+
def diff(self) -> Any:
|
|
201
|
+
"""Diff SCM pour le job (si applicable)."""
|
|
202
|
+
path = self._build_path("diff")
|
|
203
|
+
return self.rd.http_get(path)
|
|
204
|
+
|
|
205
|
+
def input_fields(self, action_id: str) -> Any:
|
|
206
|
+
"""Champs attendus pour une action SCM job."""
|
|
207
|
+
path = self._build_path(f"action/{action_id}/input")
|
|
208
|
+
return self.rd.http_get(path)
|
|
209
|
+
|
|
210
|
+
def perform(
|
|
211
|
+
self, action_id: str, input_values: dict[str, Any] | None = None
|
|
212
|
+
) -> Any:
|
|
213
|
+
"""Exécute une action SCM job."""
|
|
214
|
+
payload: dict[str, Any] = {}
|
|
215
|
+
if input_values is not None:
|
|
216
|
+
payload["input"] = input_values
|
|
217
|
+
path = self._build_path(f"action/{action_id}")
|
|
218
|
+
return self.rd.http_post(path, json=payload or None)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
__all__ = [
|
|
222
|
+
"ProjectSCMManager",
|
|
223
|
+
"ProjectSCMIntegrationManager",
|
|
224
|
+
"ProjectSCMPluginsManager",
|
|
225
|
+
"ProjectSCMConfigManager",
|
|
226
|
+
"ProjectSCMActionsManager",
|
|
227
|
+
"JobSCMManager",
|
|
228
|
+
"JobSCMIntegrationManager",
|
|
229
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gestion du système Rundeck
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rundeck.base import RundeckObject, RundeckObjectManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SystemExecutionsManager(RundeckObjectManager):
|
|
11
|
+
"""Sous-manager pour les endpoints executions du système."""
|
|
12
|
+
|
|
13
|
+
_path = "/system/executions"
|
|
14
|
+
|
|
15
|
+
def enable(self) -> dict[str, Any]:
|
|
16
|
+
"""Active le mode d'exécution."""
|
|
17
|
+
path = self._build_path("enable")
|
|
18
|
+
return self.rd.http_post(path)
|
|
19
|
+
|
|
20
|
+
def disable(self) -> dict[str, Any]:
|
|
21
|
+
"""Désactive le mode d'exécution (mode passif)."""
|
|
22
|
+
path = self._build_path("disable")
|
|
23
|
+
return self.rd.http_post(path)
|
|
24
|
+
|
|
25
|
+
def status(self) -> dict[str, Any]:
|
|
26
|
+
"""Statut actuel du mode d'exécution."""
|
|
27
|
+
path = self._build_path("status")
|
|
28
|
+
return self.rd.http_get(path)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SystemACL(RundeckObject):
|
|
32
|
+
"""Représente une policy ACL système."""
|
|
33
|
+
|
|
34
|
+
_id_attr = "name"
|
|
35
|
+
_repr_attr = "name"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SystemACLManager(RundeckObjectManager[SystemACL]):
|
|
39
|
+
"""Sous-manager pour les endpoints ACL système."""
|
|
40
|
+
|
|
41
|
+
_path = "/system/acl"
|
|
42
|
+
_obj_cls = SystemACL
|
|
43
|
+
|
|
44
|
+
def list(self) -> list[SystemACL]:
|
|
45
|
+
"""Liste les politiques ACL système."""
|
|
46
|
+
path = self._build_path("")
|
|
47
|
+
result = self.rd.http_get(path)
|
|
48
|
+
if isinstance(result, dict):
|
|
49
|
+
resources = result.get("resources") or []
|
|
50
|
+
items = []
|
|
51
|
+
for entry in resources:
|
|
52
|
+
name = entry.get("name") or entry.get("path")
|
|
53
|
+
if name:
|
|
54
|
+
data = dict(entry)
|
|
55
|
+
data.setdefault("name", name)
|
|
56
|
+
items.append(data)
|
|
57
|
+
result = items
|
|
58
|
+
return self._wrap_list(result or [])
|
|
59
|
+
|
|
60
|
+
def get(self, policy_name: str) -> SystemACL:
|
|
61
|
+
"""Récupère une politique ACL (contenu texte)."""
|
|
62
|
+
path = self._build_path(policy_name)
|
|
63
|
+
response = self.rd.http_get(path, raw=True)
|
|
64
|
+
return self._wrap({"name": policy_name, "content": response.text})
|
|
65
|
+
|
|
66
|
+
def create(self, policy_name: str, content: str) -> SystemACL:
|
|
67
|
+
"""Crée une politique ACL système."""
|
|
68
|
+
path = self._build_path(policy_name)
|
|
69
|
+
headers = {"Content-Type": "application/yaml"}
|
|
70
|
+
result = self.rd.http_post(path, data=content, headers=headers) or {}
|
|
71
|
+
if "name" not in result:
|
|
72
|
+
result["name"] = policy_name
|
|
73
|
+
return self._wrap(result)
|
|
74
|
+
|
|
75
|
+
def update(self, policy_name: str, content: str) -> SystemACL:
|
|
76
|
+
"""Met à jour une politique ACL système."""
|
|
77
|
+
path = self._build_path(policy_name)
|
|
78
|
+
headers = {"Content-Type": "application/yaml"}
|
|
79
|
+
result = self.rd.http_put(path, data=content, headers=headers) or {}
|
|
80
|
+
if "name" not in result:
|
|
81
|
+
result["name"] = policy_name
|
|
82
|
+
return self._wrap(result)
|
|
83
|
+
|
|
84
|
+
def delete(self, policy_name: str) -> None:
|
|
85
|
+
"""Supprime une politique ACL système."""
|
|
86
|
+
path = self._build_path(policy_name)
|
|
87
|
+
self.rd.http_delete(path)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SystemLogStorageManager(RundeckObjectManager):
|
|
91
|
+
"""Sous-manager pour les endpoints logstorage du système."""
|
|
92
|
+
|
|
93
|
+
_path = "/system/logstorage"
|
|
94
|
+
|
|
95
|
+
def info(self) -> dict[str, Any]:
|
|
96
|
+
"""Informations sur le stockage des logs."""
|
|
97
|
+
path = self._build_path()
|
|
98
|
+
return self.rd.http_get(path)
|
|
99
|
+
|
|
100
|
+
def incomplete(self, max: int = 20, offset: int = 0) -> dict[str, Any]:
|
|
101
|
+
"""Liste des exécutions avec stockage incomplet."""
|
|
102
|
+
params = {"max": max, "offset": offset}
|
|
103
|
+
path = self._build_path("incomplete")
|
|
104
|
+
return self.rd.http_get(path, params=params)
|
|
105
|
+
|
|
106
|
+
def incomplete_resume(self) -> dict[str, Any]:
|
|
107
|
+
"""Reprise du traitement des logs incomplets."""
|
|
108
|
+
path = self._build_path("incomplete/resume")
|
|
109
|
+
return self.rd.http_post(path)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SystemManager(RundeckObjectManager):
|
|
113
|
+
"""Manager pour les opérations système"""
|
|
114
|
+
|
|
115
|
+
_path = "/system"
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def executions(self) -> SystemExecutionsManager:
|
|
119
|
+
"""Accès aux endpoints /system/executions/*."""
|
|
120
|
+
return SystemExecutionsManager(self.rd)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def acl(self) -> SystemACLManager:
|
|
124
|
+
"""Accès aux endpoints ACL système /system/acl/*."""
|
|
125
|
+
return SystemACLManager(self.rd)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def logstorage(self) -> SystemLogStorageManager:
|
|
129
|
+
"""Accès aux endpoints /system/logstorage/*."""
|
|
130
|
+
return SystemLogStorageManager(self.rd)
|
|
131
|
+
|
|
132
|
+
def info(self) -> dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Récupère les informations système
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Informations système
|
|
138
|
+
"""
|
|
139
|
+
path = self._build_path("info")
|
|
140
|
+
return self.rd.http_get(path)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gestion des tokens Rundeck
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rundeck.base import RundeckObject, RundeckObjectManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Token(RundeckObject):
|
|
11
|
+
"""Représente un token API Rundeck"""
|
|
12
|
+
|
|
13
|
+
_id_attr = "id"
|
|
14
|
+
_repr_attr = "user"
|
|
15
|
+
|
|
16
|
+
def delete(self) -> None:
|
|
17
|
+
"""Supprime le token"""
|
|
18
|
+
self.manager.delete(self.id)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TokenManager(RundeckObjectManager):
|
|
22
|
+
"""Manager pour les tokens"""
|
|
23
|
+
|
|
24
|
+
_path = "/tokens"
|
|
25
|
+
_obj_cls = Token
|
|
26
|
+
|
|
27
|
+
_single_path = "/token/{id}"
|
|
28
|
+
|
|
29
|
+
def _token_path(self, token_id: str) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Chemin pour un token unitaire (/token/{id}).
|
|
32
|
+
L'API Rundeck mélange /tokens (collection) et /token/{id} (single),
|
|
33
|
+
on centralise ici pour rester cohérent.
|
|
34
|
+
"""
|
|
35
|
+
return self._single_path.format(id=token_id)
|
|
36
|
+
|
|
37
|
+
def list(self, user: str | None = None) -> "list[Token]":
|
|
38
|
+
"""
|
|
39
|
+
Liste les tokens
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
user: Filtre par utilisateur
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Liste de tokens
|
|
46
|
+
"""
|
|
47
|
+
path = self._build_path(user) if user else self._build_path()
|
|
48
|
+
return self._list(path=path)
|
|
49
|
+
|
|
50
|
+
def get(self, token_id: str) -> Token:
|
|
51
|
+
"""
|
|
52
|
+
Récupère un token par son ID
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
token_id: ID du token
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Token
|
|
59
|
+
"""
|
|
60
|
+
path = self._token_path(token_id)
|
|
61
|
+
return self._get(token_id, path=path)
|
|
62
|
+
|
|
63
|
+
def create(
|
|
64
|
+
self,
|
|
65
|
+
user: str,
|
|
66
|
+
roles: "list[str]",
|
|
67
|
+
duration: str | None = None,
|
|
68
|
+
name: str | None = None,
|
|
69
|
+
) -> Token:
|
|
70
|
+
"""
|
|
71
|
+
Crée un nouveau token
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
user: Nom d'utilisateur
|
|
75
|
+
roles: Liste des rôles
|
|
76
|
+
duration: Durée de validité (ex: "120d")
|
|
77
|
+
name: Nom du token (API v37+)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Token créé
|
|
81
|
+
"""
|
|
82
|
+
data: dict[str, Any] = {
|
|
83
|
+
"user": user,
|
|
84
|
+
"roles": roles,
|
|
85
|
+
}
|
|
86
|
+
if duration:
|
|
87
|
+
data["duration"] = duration
|
|
88
|
+
if name:
|
|
89
|
+
data["name"] = name
|
|
90
|
+
|
|
91
|
+
path = self._build_path(user)
|
|
92
|
+
return self._create(json=data, path=path)
|
|
93
|
+
|
|
94
|
+
def delete(self, token_id: str) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Supprime un token
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
token_id: ID du token
|
|
100
|
+
"""
|
|
101
|
+
path = self._token_path(token_id)
|
|
102
|
+
self._delete(token_id, path=path)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gestion des utilisateurs Rundeck
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from rundeck.base import RundeckObject, RundeckObjectManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class User(RundeckObject):
|
|
11
|
+
"""Représente un utilisateur Rundeck"""
|
|
12
|
+
|
|
13
|
+
_id_attr = "login"
|
|
14
|
+
_repr_attr = "login"
|
|
15
|
+
|
|
16
|
+
def update(
|
|
17
|
+
self,
|
|
18
|
+
firstName: str | None = None,
|
|
19
|
+
lastName: str | None = None,
|
|
20
|
+
email: str | None = None,
|
|
21
|
+
) -> "User":
|
|
22
|
+
"""
|
|
23
|
+
Met à jour les informations de l'utilisateur
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
firstName: Prénom
|
|
27
|
+
lastName: Nom
|
|
28
|
+
email: Email
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Utilisateur mis à jour
|
|
32
|
+
"""
|
|
33
|
+
data: dict[str, Any] = {}
|
|
34
|
+
if firstName:
|
|
35
|
+
data["firstName"] = firstName
|
|
36
|
+
if lastName:
|
|
37
|
+
data["lastName"] = lastName
|
|
38
|
+
if email:
|
|
39
|
+
data["email"] = email
|
|
40
|
+
|
|
41
|
+
path = self.manager._build_path(f"info/{self.login}")
|
|
42
|
+
result = self.manager.rd.http_post(path, json=data)
|
|
43
|
+
self._attrs.update(result)
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
def roles(self) -> list[str]:
|
|
47
|
+
"""
|
|
48
|
+
Récupère les rôles de l'utilisateur
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Liste des rôles
|
|
52
|
+
"""
|
|
53
|
+
path = self.manager._build_path("roles")
|
|
54
|
+
result = self.manager.rd.http_get(path)
|
|
55
|
+
return result.get("roles", [])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class UserManager(RundeckObjectManager):
|
|
59
|
+
"""Manager pour les utilisateurs"""
|
|
60
|
+
|
|
61
|
+
_path = "/user"
|
|
62
|
+
_obj_cls = User
|
|
63
|
+
|
|
64
|
+
def list(self) -> "list[User]":
|
|
65
|
+
"""
|
|
66
|
+
Liste tous les utilisateurs
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Liste d'utilisateurs
|
|
70
|
+
"""
|
|
71
|
+
path = self._build_path("list")
|
|
72
|
+
return self._list(path=path)
|
|
73
|
+
|
|
74
|
+
def get(self, login: str) -> User:
|
|
75
|
+
"""
|
|
76
|
+
Récupère un utilisateur par son login
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
login: Login de l'utilisateur
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Utilisateur
|
|
83
|
+
"""
|
|
84
|
+
path = self._build_path(f"info/{login}")
|
|
85
|
+
return self._get(login, path=path)
|
|
86
|
+
|
|
87
|
+
def get_current(self) -> User:
|
|
88
|
+
"""
|
|
89
|
+
Récupère l'utilisateur courant
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Utilisateur courant
|
|
93
|
+
"""
|
|
94
|
+
path = self._build_path("info")
|
|
95
|
+
return self._get("current", path=path)
|
|
96
|
+
|
|
97
|
+
def update(
|
|
98
|
+
self,
|
|
99
|
+
login: str,
|
|
100
|
+
firstName: str | None = None,
|
|
101
|
+
lastName: str | None = None,
|
|
102
|
+
email: str | None = None,
|
|
103
|
+
) -> User:
|
|
104
|
+
"""
|
|
105
|
+
Met à jour un utilisateur
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
login: Login de l'utilisateur
|
|
109
|
+
firstName: Prénom
|
|
110
|
+
lastName: Nom
|
|
111
|
+
email: Email
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Utilisateur mis à jour
|
|
115
|
+
"""
|
|
116
|
+
data: dict[str, Any] = {}
|
|
117
|
+
if firstName:
|
|
118
|
+
data["firstName"] = firstName
|
|
119
|
+
if lastName:
|
|
120
|
+
data["lastName"] = lastName
|
|
121
|
+
if email:
|
|
122
|
+
data["email"] = email
|
|
123
|
+
|
|
124
|
+
path = self._build_path(f"info/{login}")
|
|
125
|
+
result = self.rd.http_post(path, json=data)
|
|
126
|
+
return self._obj_cls(self, result)
|
|
127
|
+
|
|
128
|
+
def current_roles(self) -> "list[str]":
|
|
129
|
+
"""
|
|
130
|
+
Récupère les rôles de l'utilisateur courant
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Liste des rôles
|
|
134
|
+
"""
|
|
135
|
+
path = self._build_path("roles")
|
|
136
|
+
result = self.rd.http_get(path)
|
|
137
|
+
return result.get("roles", [])
|