bt-cli 0.4.13__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.
- bt_cli/__init__.py +3 -0
- bt_cli/cli.py +830 -0
- bt_cli/commands/__init__.py +1 -0
- bt_cli/commands/configure.py +415 -0
- bt_cli/commands/learn.py +229 -0
- bt_cli/commands/quick.py +784 -0
- bt_cli/core/__init__.py +1 -0
- bt_cli/core/auth.py +213 -0
- bt_cli/core/client.py +313 -0
- bt_cli/core/config.py +393 -0
- bt_cli/core/config_file.py +420 -0
- bt_cli/core/csv_utils.py +91 -0
- bt_cli/core/errors.py +247 -0
- bt_cli/core/output.py +205 -0
- bt_cli/core/prompts.py +87 -0
- bt_cli/core/rest_debug.py +221 -0
- bt_cli/data/CLAUDE.md +94 -0
- bt_cli/data/__init__.py +0 -0
- bt_cli/data/skills/bt/SKILL.md +108 -0
- bt_cli/data/skills/entitle/SKILL.md +170 -0
- bt_cli/data/skills/epmw/SKILL.md +144 -0
- bt_cli/data/skills/pra/SKILL.md +150 -0
- bt_cli/data/skills/pws/SKILL.md +198 -0
- bt_cli/entitle/__init__.py +1 -0
- bt_cli/entitle/client/__init__.py +5 -0
- bt_cli/entitle/client/base.py +443 -0
- bt_cli/entitle/commands/__init__.py +24 -0
- bt_cli/entitle/commands/accounts.py +53 -0
- bt_cli/entitle/commands/applications.py +39 -0
- bt_cli/entitle/commands/auth.py +68 -0
- bt_cli/entitle/commands/bundles.py +218 -0
- bt_cli/entitle/commands/integrations.py +60 -0
- bt_cli/entitle/commands/permissions.py +70 -0
- bt_cli/entitle/commands/policies.py +97 -0
- bt_cli/entitle/commands/resources.py +131 -0
- bt_cli/entitle/commands/roles.py +74 -0
- bt_cli/entitle/commands/users.py +123 -0
- bt_cli/entitle/commands/workflows.py +187 -0
- bt_cli/entitle/models/__init__.py +31 -0
- bt_cli/entitle/models/bundle.py +28 -0
- bt_cli/entitle/models/common.py +37 -0
- bt_cli/entitle/models/integration.py +30 -0
- bt_cli/entitle/models/permission.py +27 -0
- bt_cli/entitle/models/policy.py +25 -0
- bt_cli/entitle/models/resource.py +29 -0
- bt_cli/entitle/models/role.py +28 -0
- bt_cli/entitle/models/user.py +24 -0
- bt_cli/entitle/models/workflow.py +55 -0
- bt_cli/epmw/__init__.py +1 -0
- bt_cli/epmw/client/__init__.py +5 -0
- bt_cli/epmw/client/base.py +848 -0
- bt_cli/epmw/commands/__init__.py +33 -0
- bt_cli/epmw/commands/audits.py +250 -0
- bt_cli/epmw/commands/auth.py +55 -0
- bt_cli/epmw/commands/computers.py +140 -0
- bt_cli/epmw/commands/events.py +233 -0
- bt_cli/epmw/commands/groups.py +215 -0
- bt_cli/epmw/commands/policies.py +673 -0
- bt_cli/epmw/commands/quick.py +348 -0
- bt_cli/epmw/commands/requests.py +224 -0
- bt_cli/epmw/commands/roles.py +78 -0
- bt_cli/epmw/commands/tasks.py +38 -0
- bt_cli/epmw/commands/users.py +219 -0
- bt_cli/epmw/models/__init__.py +1 -0
- bt_cli/pra/__init__.py +1 -0
- bt_cli/pra/client/__init__.py +5 -0
- bt_cli/pra/client/base.py +618 -0
- bt_cli/pra/commands/__init__.py +30 -0
- bt_cli/pra/commands/auth.py +55 -0
- bt_cli/pra/commands/import_export.py +442 -0
- bt_cli/pra/commands/jump_clients.py +139 -0
- bt_cli/pra/commands/jump_groups.py +146 -0
- bt_cli/pra/commands/jump_items.py +638 -0
- bt_cli/pra/commands/jumpoints.py +95 -0
- bt_cli/pra/commands/policies.py +197 -0
- bt_cli/pra/commands/quick.py +470 -0
- bt_cli/pra/commands/teams.py +81 -0
- bt_cli/pra/commands/users.py +87 -0
- bt_cli/pra/commands/vault.py +564 -0
- bt_cli/pra/models/__init__.py +27 -0
- bt_cli/pra/models/common.py +12 -0
- bt_cli/pra/models/jump_client.py +25 -0
- bt_cli/pra/models/jump_group.py +15 -0
- bt_cli/pra/models/jump_item.py +72 -0
- bt_cli/pra/models/jumpoint.py +19 -0
- bt_cli/pra/models/team.py +14 -0
- bt_cli/pra/models/user.py +17 -0
- bt_cli/pra/models/vault.py +45 -0
- bt_cli/pws/__init__.py +1 -0
- bt_cli/pws/client/__init__.py +5 -0
- bt_cli/pws/client/base.py +356 -0
- bt_cli/pws/client/beyondinsight.py +869 -0
- bt_cli/pws/client/passwordsafe.py +1786 -0
- bt_cli/pws/commands/__init__.py +33 -0
- bt_cli/pws/commands/accounts.py +372 -0
- bt_cli/pws/commands/assets.py +311 -0
- bt_cli/pws/commands/auth.py +166 -0
- bt_cli/pws/commands/clouds.py +221 -0
- bt_cli/pws/commands/config.py +344 -0
- bt_cli/pws/commands/credentials.py +347 -0
- bt_cli/pws/commands/databases.py +306 -0
- bt_cli/pws/commands/directories.py +199 -0
- bt_cli/pws/commands/functional.py +298 -0
- bt_cli/pws/commands/import_export.py +452 -0
- bt_cli/pws/commands/platforms.py +118 -0
- bt_cli/pws/commands/quick.py +1646 -0
- bt_cli/pws/commands/search.py +256 -0
- bt_cli/pws/commands/secrets.py +1343 -0
- bt_cli/pws/commands/systems.py +389 -0
- bt_cli/pws/commands/users.py +415 -0
- bt_cli/pws/commands/workgroups.py +166 -0
- bt_cli/pws/config.py +18 -0
- bt_cli/pws/models/__init__.py +19 -0
- bt_cli/pws/models/account.py +186 -0
- bt_cli/pws/models/asset.py +102 -0
- bt_cli/pws/models/common.py +132 -0
- bt_cli/pws/models/system.py +121 -0
- bt_cli-0.4.13.dist-info/METADATA +417 -0
- bt_cli-0.4.13.dist-info/RECORD +121 -0
- bt_cli-0.4.13.dist-info/WHEEL +4 -0
- bt_cli-0.4.13.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""EPMW events commands."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from bt_cli.core.output import OutputFormat, print_api_error, print_json, print_table, print_info
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(no_args_is_help=True, help="EPM Windows events")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_iso_date(hours_ago: int = 24) -> str:
|
|
15
|
+
"""Get ISO format date string for N hours ago."""
|
|
16
|
+
dt = datetime.now(timezone.utc) - timedelta(hours=hours_ago)
|
|
17
|
+
return dt.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_iso_now() -> str:
|
|
21
|
+
"""Get current time in ISO format."""
|
|
22
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_nested(obj: Dict[str, Any], path: str, default: str = "-") -> str:
|
|
26
|
+
"""Get a nested value from a dict using dot notation."""
|
|
27
|
+
keys = path.split(".")
|
|
28
|
+
value = obj
|
|
29
|
+
for key in keys:
|
|
30
|
+
if isinstance(value, dict):
|
|
31
|
+
value = value.get(key)
|
|
32
|
+
else:
|
|
33
|
+
return default
|
|
34
|
+
if value is None:
|
|
35
|
+
return default
|
|
36
|
+
return str(value) if value is not None else default
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _flatten_events(events: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
40
|
+
"""Flatten nested event structure for table display."""
|
|
41
|
+
flattened = []
|
|
42
|
+
for event in events:
|
|
43
|
+
flat = {
|
|
44
|
+
"timestamp": _get_nested(event, "@timestamp"),
|
|
45
|
+
"hostname": _get_nested(event, "host.hostname"),
|
|
46
|
+
"user": _get_nested(event, "user.name"),
|
|
47
|
+
"action": _get_nested(event, "event.action"),
|
|
48
|
+
"code": _get_nested(event, "event.code"),
|
|
49
|
+
"reason": _get_nested(event, "event.reason"),
|
|
50
|
+
"application": _get_nested(event, "file.name"),
|
|
51
|
+
"path": _get_nested(event, "file.path"),
|
|
52
|
+
"os": _get_nested(event, "host.os.name"),
|
|
53
|
+
}
|
|
54
|
+
flattened.append(flat)
|
|
55
|
+
return flattened
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@app.command("list")
|
|
59
|
+
def list_events(
|
|
60
|
+
hours: int = typer.Option(
|
|
61
|
+
24, "--hours", "-h", help="Hours to look back (default: 24)"
|
|
62
|
+
),
|
|
63
|
+
limit: int = typer.Option(
|
|
64
|
+
1000, "--limit", "-l", help="Max records to return (1-1000)"
|
|
65
|
+
),
|
|
66
|
+
output: OutputFormat = typer.Option(
|
|
67
|
+
OutputFormat.TABLE, "--output", "-o", help="Output format"
|
|
68
|
+
),
|
|
69
|
+
):
|
|
70
|
+
"""List events from the last N hours (default: 24).
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
|
|
74
|
+
# Last 24 hours (default)
|
|
75
|
+
bt epmw events list
|
|
76
|
+
|
|
77
|
+
# Last 4 hours
|
|
78
|
+
bt epmw events list --hours 4
|
|
79
|
+
|
|
80
|
+
# Last 48 hours, max 500 records
|
|
81
|
+
bt epmw events list --hours 48 --limit 500
|
|
82
|
+
"""
|
|
83
|
+
from bt_cli.epmw.client import get_client
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
start_date = _get_iso_date(hours)
|
|
87
|
+
print_info(f"Fetching events since {start_date} (last {hours} hours)...")
|
|
88
|
+
|
|
89
|
+
client = get_client()
|
|
90
|
+
events = client.list_events_from_date(start_date, record_size=limit)
|
|
91
|
+
|
|
92
|
+
if output == OutputFormat.JSON:
|
|
93
|
+
print_json(events)
|
|
94
|
+
else:
|
|
95
|
+
if not events:
|
|
96
|
+
print_info("No events found in the specified time period.")
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
# Flatten nested structure for table display
|
|
100
|
+
flat_events = _flatten_events(events)
|
|
101
|
+
|
|
102
|
+
columns = [
|
|
103
|
+
("Time", "timestamp"),
|
|
104
|
+
("Computer", "hostname"),
|
|
105
|
+
("User", "user"),
|
|
106
|
+
("Action", "action"),
|
|
107
|
+
("Application", "application"),
|
|
108
|
+
("Reason", "reason"),
|
|
109
|
+
]
|
|
110
|
+
print_table(flat_events, columns, title=f"Events (last {hours} hours)")
|
|
111
|
+
print_info(f"Total: {len(events)} events")
|
|
112
|
+
except httpx.HTTPStatusError as e:
|
|
113
|
+
print_api_error(e, "list events")
|
|
114
|
+
raise typer.Exit(1)
|
|
115
|
+
except httpx.RequestError as e:
|
|
116
|
+
print_api_error(e, "list events")
|
|
117
|
+
raise typer.Exit(1)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print_api_error(e, "list events")
|
|
120
|
+
raise typer.Exit(1)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@app.command("search")
|
|
124
|
+
def search_events(
|
|
125
|
+
hours: int = typer.Option(
|
|
126
|
+
24, "--hours", "-h", help="Hours to look back (default: 24)"
|
|
127
|
+
),
|
|
128
|
+
hostname: Optional[str] = typer.Option(
|
|
129
|
+
None, "--hostname", help="Filter by computer hostname"
|
|
130
|
+
),
|
|
131
|
+
username: Optional[str] = typer.Option(
|
|
132
|
+
None, "--username", "-u", help="Filter by username"
|
|
133
|
+
),
|
|
134
|
+
event_type: Optional[str] = typer.Option(
|
|
135
|
+
None, "--event-type", "-t", help="Filter by event type"
|
|
136
|
+
),
|
|
137
|
+
event_action: Optional[str] = typer.Option(
|
|
138
|
+
None, "--event-action", "-a", help="Filter by event action"
|
|
139
|
+
),
|
|
140
|
+
os_filter: Optional[str] = typer.Option(
|
|
141
|
+
None, "--os", help="Filter by operating system"
|
|
142
|
+
),
|
|
143
|
+
workstyle: Optional[str] = typer.Option(
|
|
144
|
+
None, "--workstyle", help="Filter by policy workstyle name"
|
|
145
|
+
),
|
|
146
|
+
page_size: int = typer.Option(
|
|
147
|
+
100, "--page-size", help="Records per page (max 200)"
|
|
148
|
+
),
|
|
149
|
+
page: int = typer.Option(
|
|
150
|
+
1, "--page", "-p", help="Page number"
|
|
151
|
+
),
|
|
152
|
+
output: OutputFormat = typer.Option(
|
|
153
|
+
OutputFormat.TABLE, "--output", "-o", help="Output format"
|
|
154
|
+
),
|
|
155
|
+
):
|
|
156
|
+
"""Search events with filters.
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
|
|
160
|
+
# Search last 24 hours for a specific user
|
|
161
|
+
bt epmw events search --username "john.doe"
|
|
162
|
+
|
|
163
|
+
# Search last 4 hours for a specific computer
|
|
164
|
+
bt epmw events search --hours 4 --hostname "WORKSTATION01"
|
|
165
|
+
|
|
166
|
+
# Search with multiple filters
|
|
167
|
+
bt epmw events search --hours 12 --event-type "ApplicationRun" --os "Windows"
|
|
168
|
+
|
|
169
|
+
# Get second page of results
|
|
170
|
+
bt epmw events search --page 2 --page-size 50
|
|
171
|
+
"""
|
|
172
|
+
from bt_cli.epmw.client import get_client
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
start_date = _get_iso_date(hours)
|
|
176
|
+
end_date = _get_iso_now()
|
|
177
|
+
print_info(f"Searching events from {start_date} to {end_date}...")
|
|
178
|
+
|
|
179
|
+
client = get_client()
|
|
180
|
+
|
|
181
|
+
# Build filter lists
|
|
182
|
+
event_types = [event_type] if event_type else None
|
|
183
|
+
event_actions = [event_action] if event_action else None
|
|
184
|
+
|
|
185
|
+
result = client.search_events(
|
|
186
|
+
start_date=start_date,
|
|
187
|
+
end_date=end_date,
|
|
188
|
+
operating_system=os_filter,
|
|
189
|
+
event_types=event_types,
|
|
190
|
+
event_actions=event_actions,
|
|
191
|
+
hostname=hostname,
|
|
192
|
+
username=username,
|
|
193
|
+
workstyle_name=workstyle,
|
|
194
|
+
page_size=page_size,
|
|
195
|
+
page_number=page,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if output == OutputFormat.JSON:
|
|
199
|
+
print_json(result)
|
|
200
|
+
else:
|
|
201
|
+
# Handle different response structures
|
|
202
|
+
events = result if isinstance(result, list) else result.get("data", result.get("events", []))
|
|
203
|
+
|
|
204
|
+
if not events:
|
|
205
|
+
print_info("No events found matching the filters.")
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
# Flatten nested structure for table display
|
|
209
|
+
flat_events = _flatten_events(events)
|
|
210
|
+
|
|
211
|
+
columns = [
|
|
212
|
+
("Time", "timestamp"),
|
|
213
|
+
("Computer", "hostname"),
|
|
214
|
+
("User", "user"),
|
|
215
|
+
("Action", "action"),
|
|
216
|
+
("Application", "application"),
|
|
217
|
+
("Reason", "reason"),
|
|
218
|
+
]
|
|
219
|
+
print_table(flat_events, columns, title=f"Events Search Results (page {page})")
|
|
220
|
+
|
|
221
|
+
# Show pagination info if available
|
|
222
|
+
if isinstance(result, dict):
|
|
223
|
+
total = result.get("totalCount", len(events))
|
|
224
|
+
print_info(f"Showing {len(events)} of {total} total events")
|
|
225
|
+
except httpx.HTTPStatusError as e:
|
|
226
|
+
print_api_error(e, "search events")
|
|
227
|
+
raise typer.Exit(1)
|
|
228
|
+
except httpx.RequestError as e:
|
|
229
|
+
print_api_error(e, "search events")
|
|
230
|
+
raise typer.Exit(1)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
print_api_error(e, "search events")
|
|
233
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""EPMW groups commands."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from bt_cli.core.output import OutputFormat, print_api_error, print_error, print_json, print_table
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(no_args_is_help=True, help="Computer groups management")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command("list")
|
|
14
|
+
def list_groups(
|
|
15
|
+
output: OutputFormat = typer.Option(
|
|
16
|
+
OutputFormat.TABLE, "--output", "-o", help="Output format"
|
|
17
|
+
),
|
|
18
|
+
):
|
|
19
|
+
"""List all groups."""
|
|
20
|
+
from bt_cli.epmw.client import get_client
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
client = get_client()
|
|
24
|
+
groups = client.list_groups()
|
|
25
|
+
|
|
26
|
+
if output == OutputFormat.JSON:
|
|
27
|
+
print_json(groups)
|
|
28
|
+
else:
|
|
29
|
+
columns = [
|
|
30
|
+
("ID", "id"),
|
|
31
|
+
("Name", "name"),
|
|
32
|
+
("Computers", "computerCount"),
|
|
33
|
+
("Active", "activeComputers"),
|
|
34
|
+
("Policy", "policyName"),
|
|
35
|
+
("Revision", "revision"),
|
|
36
|
+
]
|
|
37
|
+
print_table(groups, columns, title="Groups")
|
|
38
|
+
except httpx.HTTPStatusError as e:
|
|
39
|
+
print_api_error(e, "list groups")
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
except httpx.RequestError as e:
|
|
42
|
+
print_api_error(e, "list groups")
|
|
43
|
+
raise typer.Exit(1)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print_api_error(e, "list groups")
|
|
46
|
+
raise typer.Exit(1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command("get")
|
|
50
|
+
def get_group(
|
|
51
|
+
group_id: str = typer.Argument(..., help="Group ID"),
|
|
52
|
+
output: OutputFormat = typer.Option(
|
|
53
|
+
OutputFormat.JSON, "--output", "-o", help="Output format"
|
|
54
|
+
),
|
|
55
|
+
):
|
|
56
|
+
"""Get group details."""
|
|
57
|
+
from bt_cli.epmw.client import get_client
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
client = get_client()
|
|
61
|
+
group = client.get_group(group_id)
|
|
62
|
+
|
|
63
|
+
if output == OutputFormat.JSON:
|
|
64
|
+
print_json(group)
|
|
65
|
+
else:
|
|
66
|
+
for key, value in group.items():
|
|
67
|
+
typer.echo(f"{key}: {value}")
|
|
68
|
+
except httpx.HTTPStatusError as e:
|
|
69
|
+
print_api_error(e, "get group")
|
|
70
|
+
raise typer.Exit(1)
|
|
71
|
+
except httpx.RequestError as e:
|
|
72
|
+
print_api_error(e, "get group")
|
|
73
|
+
raise typer.Exit(1)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print_api_error(e, "get group")
|
|
76
|
+
raise typer.Exit(1)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.command("create")
|
|
80
|
+
def create_group(
|
|
81
|
+
name: str = typer.Option(..., "--name", "-n", help="Group name"),
|
|
82
|
+
description: Optional[str] = typer.Option(None, "--description", "-d", help="Group description"),
|
|
83
|
+
output: OutputFormat = typer.Option(
|
|
84
|
+
OutputFormat.JSON, "--output", "-o", help="Output format"
|
|
85
|
+
),
|
|
86
|
+
):
|
|
87
|
+
"""Create a new group."""
|
|
88
|
+
from bt_cli.epmw.client import get_client
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
client = get_client()
|
|
92
|
+
data = {"name": name}
|
|
93
|
+
if description:
|
|
94
|
+
data["description"] = description
|
|
95
|
+
group = client.create_group(data)
|
|
96
|
+
|
|
97
|
+
typer.echo(f"Created group: {group['name']} (ID: {group['id']})")
|
|
98
|
+
if output == OutputFormat.JSON:
|
|
99
|
+
print_json(group)
|
|
100
|
+
except httpx.HTTPStatusError as e:
|
|
101
|
+
print_api_error(e, "create group")
|
|
102
|
+
raise typer.Exit(1)
|
|
103
|
+
except httpx.RequestError as e:
|
|
104
|
+
print_api_error(e, "create group")
|
|
105
|
+
raise typer.Exit(1)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print_api_error(e, "create group")
|
|
108
|
+
raise typer.Exit(1)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@app.command("update")
|
|
112
|
+
def update_group(
|
|
113
|
+
group_id: str = typer.Argument(..., help="Group ID"),
|
|
114
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="New group name"),
|
|
115
|
+
description: Optional[str] = typer.Option(None, "--description", "-d", help="New description"),
|
|
116
|
+
output: OutputFormat = typer.Option(
|
|
117
|
+
OutputFormat.JSON, "--output", "-o", help="Output format"
|
|
118
|
+
),
|
|
119
|
+
):
|
|
120
|
+
"""Update a group."""
|
|
121
|
+
from bt_cli.epmw.client import get_client
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
client = get_client()
|
|
125
|
+
data = {}
|
|
126
|
+
if name:
|
|
127
|
+
data["name"] = name
|
|
128
|
+
if description:
|
|
129
|
+
data["description"] = description
|
|
130
|
+
if not data:
|
|
131
|
+
print_error("No updates specified")
|
|
132
|
+
raise typer.Exit(1)
|
|
133
|
+
|
|
134
|
+
group = client.update_group(group_id, data)
|
|
135
|
+
typer.echo(f"Updated group: {group_id}")
|
|
136
|
+
if output == OutputFormat.JSON and group:
|
|
137
|
+
print_json(group)
|
|
138
|
+
except httpx.HTTPStatusError as e:
|
|
139
|
+
print_api_error(e, "update group")
|
|
140
|
+
raise typer.Exit(1)
|
|
141
|
+
except httpx.RequestError as e:
|
|
142
|
+
print_api_error(e, "update group")
|
|
143
|
+
raise typer.Exit(1)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print_api_error(e, "update group")
|
|
146
|
+
raise typer.Exit(1)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.command("delete")
|
|
150
|
+
def delete_group(
|
|
151
|
+
group_id: str = typer.Argument(..., help="Group ID"),
|
|
152
|
+
):
|
|
153
|
+
"""Delete a group."""
|
|
154
|
+
from bt_cli.epmw.client import get_client
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
client = get_client()
|
|
158
|
+
client.delete_group(group_id)
|
|
159
|
+
typer.echo(f"Deleted group: {group_id}")
|
|
160
|
+
except httpx.HTTPStatusError as e:
|
|
161
|
+
print_api_error(e, "delete group")
|
|
162
|
+
raise typer.Exit(1)
|
|
163
|
+
except httpx.RequestError as e:
|
|
164
|
+
print_api_error(e, "delete group")
|
|
165
|
+
raise typer.Exit(1)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
print_api_error(e, "delete group")
|
|
168
|
+
raise typer.Exit(1)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@app.command("assign-policy")
|
|
172
|
+
def assign_policy(
|
|
173
|
+
group_id: str = typer.Argument(..., help="Group ID"),
|
|
174
|
+
policy_revision_id: str = typer.Option(..., "--policy-revision", "-p", help="Policy revision ID"),
|
|
175
|
+
):
|
|
176
|
+
"""Assign a policy revision to a group."""
|
|
177
|
+
from bt_cli.epmw.client import get_client
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
client = get_client()
|
|
181
|
+
client.assign_policy_to_group(group_id, policy_revision_id)
|
|
182
|
+
typer.echo(f"Assigned policy revision {policy_revision_id} to group {group_id}")
|
|
183
|
+
except httpx.HTTPStatusError as e:
|
|
184
|
+
print_api_error(e, "assign policy")
|
|
185
|
+
raise typer.Exit(1)
|
|
186
|
+
except httpx.RequestError as e:
|
|
187
|
+
print_api_error(e, "assign policy")
|
|
188
|
+
raise typer.Exit(1)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
print_api_error(e, "assign policy")
|
|
191
|
+
raise typer.Exit(1)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@app.command("assign-computers")
|
|
195
|
+
def assign_computers(
|
|
196
|
+
group_id: str = typer.Argument(..., help="Group ID"),
|
|
197
|
+
computer_ids: str = typer.Option(..., "--computers", "-c", help="Comma-separated computer IDs"),
|
|
198
|
+
):
|
|
199
|
+
"""Assign computers to a group."""
|
|
200
|
+
from bt_cli.epmw.client import get_client
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
client = get_client()
|
|
204
|
+
ids = [c.strip() for c in computer_ids.split(",")]
|
|
205
|
+
client.assign_computers_to_group(group_id, ids)
|
|
206
|
+
typer.echo(f"Assigned {len(ids)} computer(s) to group {group_id}")
|
|
207
|
+
except httpx.HTTPStatusError as e:
|
|
208
|
+
print_api_error(e, "assign computers")
|
|
209
|
+
raise typer.Exit(1)
|
|
210
|
+
except httpx.RequestError as e:
|
|
211
|
+
print_api_error(e, "assign computers")
|
|
212
|
+
raise typer.Exit(1)
|
|
213
|
+
except Exception as e:
|
|
214
|
+
print_api_error(e, "assign computers")
|
|
215
|
+
raise typer.Exit(1)
|