centralcli 7.2.3__tar.gz → 7.2.4__tar.gz
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.
- {centralcli-7.2.3 → centralcli-7.2.4}/PKG-INFO +1 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cache.py +24 -21
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cleaner.py +1 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clicommon.py +16 -14
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clikick.py +27 -12
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishow.py +44 -4
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clitest.py +1 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/models.py +1 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/response.py +2 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/strings.py +7 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/pyproject.toml +1 -1
- {centralcli-7.2.3 → centralcli-7.2.4}/LICENSE +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/README.md +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/__init__.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/boilerplate/README.md +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/boilerplate/_cnx_allcalls.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/boilerplate/allcalls.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/caas.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/central.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cli.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliadd.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliassign.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clibatch.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clicaas.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clicheck.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliclone.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clidel.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clidelfirmware.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliexport.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clioptions.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clirefresh.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clirename.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliset.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clisetfirmware.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowaudit.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowbandwidth.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowbranch.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowcloudauth.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowfirmware.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowmpsk.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowospf.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowoverlay.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowtshoot.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clishowwids.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/clitshoot.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliunassign.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliupdate.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/cliupgrade.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/config.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/constants.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/exceptions.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/logger.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/objects.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/render.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/setup.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/static/favicon.ico +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/typedefs.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/utils.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/vendored/csvlexer/__init__.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/vendored/csvlexer/csv.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/vscodeargs.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/wh2snow.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/wh_proxy.py +0 -0
- {centralcli-7.2.3 → centralcli-7.2.4}/centralcli/wh_proxy_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: centralcli
|
|
3
|
-
Version: 7.2.
|
|
3
|
+
Version: 7.2.4
|
|
4
4
|
Summary: A CLI for interacting with Aruba Central (Cloud Management Platform). Facilitates bulk imports, exports, reporting. A handy tool if you have devices managed by Aruba Central.
|
|
5
5
|
Home-page: https://github.com/Pack3tL0ss/central-api-cli
|
|
6
6
|
License: MIT
|
|
@@ -3227,27 +3227,30 @@ class Cache:
|
|
|
3227
3227
|
|
|
3228
3228
|
|
|
3229
3229
|
# no match found initiate cache update
|
|
3230
|
-
if retry and not match and
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
if dev_type:
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
kwargs
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3230
|
+
if retry and not match and self.responses.dev is None:
|
|
3231
|
+
if dev_type and cache_updated:
|
|
3232
|
+
... # self.responses.dev is not currently updated if dev_type provided, but cache update may have already occured in this session.
|
|
3233
|
+
else:
|
|
3234
|
+
dev_type_sfx = "" if not dev_type else f" [grey42 italic](Device Type: {utils.unlistify(dev_type)})[/]"
|
|
3235
|
+
econsole.print(f"[dark_orange3]:warning:[/] [bright_red]No Match found[/] for [cyan]{query_str}[/]{dev_type_sfx}.")
|
|
3236
|
+
if FUZZ:
|
|
3237
|
+
if dev_type:
|
|
3238
|
+
fuzz_match, fuzz_confidence = process.extract(query_str, [d["name"] for d in self.devices if d["type"] in dev_type], limit=1)[0]
|
|
3239
|
+
else:
|
|
3240
|
+
fuzz_match, fuzz_confidence = process.extract(query_str, [d["name"] for d in self.devices], limit=1)[0]
|
|
3241
|
+
confirm_str = render.rich_capture(f"Did you mean [green3]{fuzz_match}[/]?")
|
|
3242
|
+
if fuzz_confidence >= 70 and typer.confirm(confirm_str):
|
|
3243
|
+
match = self.DevDB.search(self.Q.name == fuzz_match)
|
|
3244
|
+
if not match:
|
|
3245
|
+
kwargs = {"dev_db": True}
|
|
3246
|
+
if include_inventory:
|
|
3247
|
+
_word = " & Inventory "
|
|
3248
|
+
kwargs["inv_db"] = True
|
|
3249
|
+
else:
|
|
3250
|
+
_word = " "
|
|
3251
|
+
econsole.print(f":arrows_clockwise: Updating Device{_word}Cache.")
|
|
3252
|
+
self.check_fresh(refresh=True, dev_type=dev_type, **kwargs )
|
|
3253
|
+
cache_updated = True # Need this for scenario when dev_type is the only thing refreshed, as that does not update self.responses.dev
|
|
3251
3254
|
|
|
3252
3255
|
if match:
|
|
3253
3256
|
match = [Model(dev) for dev in match]
|
|
@@ -1626,7 +1626,7 @@ def show_interfaces(data: List[dict] | dict, verbosity: int = 0, dev_type: DevTy
|
|
|
1626
1626
|
key_order = verbosity_keys[verbosity]
|
|
1627
1627
|
# send all key/value pairs through formatters
|
|
1628
1628
|
data = [
|
|
1629
|
-
dict(short_value(k, d
|
|
1629
|
+
dict(short_value(k, d[k],) for k in key_order if k in d) for d in data
|
|
1630
1630
|
]
|
|
1631
1631
|
else:
|
|
1632
1632
|
key_order = [*key_order, *data[-1].keys()]
|
|
@@ -383,8 +383,7 @@ class CLICommon:
|
|
|
383
383
|
else:
|
|
384
384
|
return default
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
def write_file(outfile: Path, outdata: str) -> None:
|
|
386
|
+
def write_file(self, outfile: Path, outdata: str) -> None:
|
|
388
387
|
"""Output data to file
|
|
389
388
|
|
|
390
389
|
Args:
|
|
@@ -407,19 +406,22 @@ class CLICommon:
|
|
|
407
406
|
|
|
408
407
|
print(f"\n[cyan]Writing output to {outfile}... ", end="")
|
|
409
408
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
409
|
+
if not outfile.parent.is_dir():
|
|
410
|
+
self.econsole.print(f"[red]Directory Not Found[/]\n[dark_orange3]:warning:[/] Unable to write output to [cyan]{outfile.name}[/].\nDirectory [cyan]{str(outfile.parent.absolute())}[/] [red]does not exist[/].")
|
|
411
|
+
else:
|
|
412
|
+
out_msg = None
|
|
413
|
+
try:
|
|
414
|
+
if isinstance(outdata, (dict, list)):
|
|
415
|
+
outdata = json.dumps(outdata, indent=4)
|
|
416
|
+
outfile.write_text(outdata) # typer.unstyle(outdata) also works
|
|
417
|
+
except Exception as e:
|
|
418
|
+
outfile.write_text(f"{outdata}")
|
|
419
|
+
out_msg = f"Error ({e.__class__.__name__}) occurred during attempt to output to file. " \
|
|
420
|
+
"Used simple string conversion"
|
|
419
421
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
422
|
+
print("[italic green]Done")
|
|
423
|
+
if out_msg:
|
|
424
|
+
log.warning(out_msg, show=True)
|
|
423
425
|
|
|
424
426
|
@staticmethod
|
|
425
427
|
def exit(msg: str = None, code: int = 1, emoji: bool = True) -> None:
|
|
@@ -8,7 +8,6 @@ from rich import print
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# Detect if called from pypi installed package or via cloned github repo (development)
|
|
11
|
-
# TODO should be able to do this in __init__
|
|
12
11
|
try:
|
|
13
12
|
from centralcli import cli
|
|
14
13
|
except (ImportError, ModuleNotFoundError) as e:
|
|
@@ -19,16 +18,18 @@ except (ImportError, ModuleNotFoundError) as e:
|
|
|
19
18
|
else:
|
|
20
19
|
print(pkg_dir.parts)
|
|
21
20
|
raise e
|
|
22
|
-
from centralcli.constants import IdenMetaVars
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
from centralcli.constants import iden_meta
|
|
23
|
+
from .cache import CacheClient
|
|
24
|
+
from .models import Clients
|
|
25
|
+
|
|
25
26
|
app = typer.Typer()
|
|
26
27
|
|
|
27
28
|
@app.command(short_help="Disconnect all WLAN clients from an AP optionally for a specific SSID",)
|
|
28
29
|
def all(
|
|
29
30
|
device: str = typer.Argument(
|
|
30
31
|
...,
|
|
31
|
-
metavar=
|
|
32
|
+
metavar=iden_meta.dev,
|
|
32
33
|
help="The AP to disconnect clients from",
|
|
33
34
|
autocompletion=cli.cache.dev_ap_completion,
|
|
34
35
|
show_default=False,
|
|
@@ -46,7 +47,7 @@ def all(
|
|
|
46
47
|
dev = cli.cache.get_dev_identifier(device)
|
|
47
48
|
_ssid_msg = "" if not ssid else f" on SSID [cyan]{ssid}[/]"
|
|
48
49
|
print(f'Kick [bright_red]ALL[/] users connected to [cyan]{dev.name}[/]{_ssid_msg}')
|
|
49
|
-
if
|
|
50
|
+
if cli.confirm(yes):
|
|
50
51
|
resp = cli.central.request(
|
|
51
52
|
cli.central.kick_users,
|
|
52
53
|
dev.serial,
|
|
@@ -56,12 +57,10 @@ def all(
|
|
|
56
57
|
cli.display_results(resp, tablefmt="action")
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
# TODO rather than drop option have cache remove users with last_connected > 30 days
|
|
60
60
|
@app.command(short_help="Disconnect a WLAN client",)
|
|
61
61
|
def client(
|
|
62
|
-
client: str = typer.Argument(..., metavar=
|
|
62
|
+
client: str = typer.Argument(..., metavar=iden_meta.client, autocompletion=cli.cache.client_completion, show_default=False),
|
|
63
63
|
refresh: bool = typer.Option(False, "--refresh", "-R", help="Cache is used to determine what AP the client is connected to, which could be [red]stale[/]. This forces a cache update."),
|
|
64
|
-
drop: bool = typer.Option(False, "--drop", "-D", help="(implies -R): Drop all users from existing cache, then refresh. By default any user that has ever connected is retained in the cache."),
|
|
65
64
|
yes: bool = cli.options.yes,
|
|
66
65
|
debug: bool = cli.options.debug,
|
|
67
66
|
default: bool = cli.options.default,
|
|
@@ -75,15 +74,31 @@ def client(
|
|
|
75
74
|
|
|
76
75
|
The [cyan]-R[/] flag can be used to force a cache refresh prior to performing the disconnect.
|
|
77
76
|
"""
|
|
78
|
-
if refresh
|
|
79
|
-
resp = cli.central.request(cli.cache.refresh_client_db, "wireless"
|
|
77
|
+
if refresh:
|
|
78
|
+
resp = cli.central.request(cli.cache.refresh_client_db, client_type="wireless")
|
|
80
79
|
if not resp:
|
|
81
80
|
cli.display_results(resp, exit_on_fail=True)
|
|
82
81
|
|
|
83
|
-
client = cli.cache.get_client_identifier(client, exit_on_fail=True)
|
|
82
|
+
client: CacheClient = cli.cache.get_client_identifier(client, exit_on_fail=True)
|
|
83
|
+
if not client.last_connected:
|
|
84
|
+
if refresh:
|
|
85
|
+
cli.exit(f"Client {client} is not connected.")
|
|
86
|
+
else:
|
|
87
|
+
client_resp = cli.central.request(cli.cache.refresh_client_db, mac=client.mac)
|
|
88
|
+
if not client_resp:
|
|
89
|
+
cli.econsole.print(f"client {client} is not online according to cache, Failure occured attempting to fetch client details from API.")
|
|
90
|
+
cli.display_results(client_resp, exit_on_fail=True)
|
|
91
|
+
|
|
92
|
+
_clients = [CacheClient(c) for c in Clients(client_resp.output)]
|
|
93
|
+
online_client = [c for c in _clients if c.last_connected is not None]
|
|
94
|
+
if online_client:
|
|
95
|
+
client = online_client[-1]
|
|
96
|
+
else:
|
|
97
|
+
client = _clients[-1]
|
|
98
|
+
cli.exit(f"Client {client} is not online: Failure Stage: {client_resp.output[-1].get('failure_stage', '')}, Reason: {client_resp.output[-1].get('failure_reason', '')}")
|
|
84
99
|
|
|
85
100
|
print(f'Kick client [cyan]{client.name}[/], currently connected to [cyan]{client.connected_name}[/]')
|
|
86
|
-
if
|
|
101
|
+
if cli.confirm(yes):
|
|
87
102
|
resp = cli.central.request(
|
|
88
103
|
cli.central.kick_users,
|
|
89
104
|
client.connected_serial,
|
|
@@ -10,6 +10,8 @@ import os
|
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
from typing import List, Iterable, Literal, Dict, Any, Tuple, TYPE_CHECKING
|
|
12
12
|
from pathlib import Path
|
|
13
|
+
import getpass
|
|
14
|
+
from jinja2 import Template
|
|
13
15
|
from rich import print
|
|
14
16
|
from rich.console import Console
|
|
15
17
|
|
|
@@ -48,6 +50,7 @@ from centralcli.constants import (
|
|
|
48
50
|
)
|
|
49
51
|
from centralcli.cache import CentralObject
|
|
50
52
|
from centralcli.objects import DateTime
|
|
53
|
+
from .strings import cron_weekly
|
|
51
54
|
|
|
52
55
|
if TYPE_CHECKING:
|
|
53
56
|
from .cache import CacheSite, CacheGroup, CacheLabel, CacheDevice
|
|
@@ -163,7 +166,7 @@ def _build_device_caption(resp: Response, *, inventory: bool = False, dev_type:
|
|
|
163
166
|
|
|
164
167
|
# Put together counts caption string
|
|
165
168
|
if status:
|
|
166
|
-
_cnt_str = ", ".join([f'[{"bright_green" if status.lower() == "up" else "red"}]{t}[/]: [cyan]{status_by_type[t]["total"]}[/]' for t in status_by_type])
|
|
169
|
+
_cnt_str = ", ".join([f'[{"bright_green" if status.lower() == "up" else "red"}]{status.capitalize()} {t if t != "ap" else "APs"}[/]: [cyan]{status_by_type[t]["total"]}[/]' for t in status_by_type])
|
|
167
170
|
elif inventory:
|
|
168
171
|
_cnt_str = f"Total in inventory: [cyan]{len(resp.output)}[/], "
|
|
169
172
|
_cnt_str = _cnt_str + ", ".join(
|
|
@@ -558,7 +561,7 @@ def aps(
|
|
|
558
561
|
if up and down:
|
|
559
562
|
... # They used both flags. ignore
|
|
560
563
|
elif up or down:
|
|
561
|
-
status = "
|
|
564
|
+
status = "down" if down else "up"
|
|
562
565
|
|
|
563
566
|
show_devices(
|
|
564
567
|
aps, dev_type="ap", include_inventory=with_inv, verbosity=verbose, outfile=outfile, update_cache=update_cache, group=group, site=site, label=label, status=status,
|
|
@@ -2009,6 +2012,7 @@ def wlans(
|
|
|
2009
2012
|
"calculate_client_count": True,
|
|
2010
2013
|
}
|
|
2011
2014
|
|
|
2015
|
+
# TODO specifying WLAN name ... is ignored if verbose
|
|
2012
2016
|
tablefmt = cli.get_format(do_json=do_json, do_yaml=do_yaml, do_csv=do_csv, do_table=do_table, default="rich")
|
|
2013
2017
|
if group: # Specifying the group implies verbose (same # of API calls either way.)
|
|
2014
2018
|
resp = central.request(central.get_full_wlan_list, group)
|
|
@@ -2032,7 +2036,7 @@ def wlans(
|
|
|
2032
2036
|
else:
|
|
2033
2037
|
resp = central.request(central.get_wlans, **params)
|
|
2034
2038
|
caption = None
|
|
2035
|
-
if resp:
|
|
2039
|
+
if resp and not name:
|
|
2036
2040
|
caption = [f'[green]{len(resp.output)}[/] SSIDs, [green]{sum([wlan.get("client_count", 0) for wlan in resp.output])}[/] Wireless Clients.']
|
|
2037
2041
|
caption += ["Summary Output, Specify the group ([cyan]--group GROUP[/])", "or use the verbose flag ([cyan]`-v`[/]) for additional details"]
|
|
2038
2042
|
cli.display_results(resp, tablefmt=tablefmt, title=title, caption=caption, pager=pager, outfile=outfile, sort_by=sort_by, reverse=reverse, cleaner=cleaner.get_wlans)
|
|
@@ -2875,7 +2879,7 @@ def portals(
|
|
|
2875
2879
|
# TODO add sort_by completion, portal completion
|
|
2876
2880
|
@app.command()
|
|
2877
2881
|
def guests(
|
|
2878
|
-
portal: str = typer.Argument(..., help="portal name", show_default=False,),
|
|
2882
|
+
portal: str = typer.Argument(..., help="portal name", autocompletion=cli.cache.portal_completion, show_default=False,),
|
|
2879
2883
|
sort_by: str = cli.options.sort_by,
|
|
2880
2884
|
reverse: bool = cli.options.reverse,
|
|
2881
2885
|
do_json: bool = cli.options.do_json,
|
|
@@ -3017,6 +3021,42 @@ def version(
|
|
|
3017
3021
|
"""
|
|
3018
3022
|
cli.version_callback()
|
|
3019
3023
|
|
|
3024
|
+
@app.command(hidden=os.name != "posix")
|
|
3025
|
+
def cron(
|
|
3026
|
+
accounts: List[str] = typer.Argument(None,),
|
|
3027
|
+
) -> None:
|
|
3028
|
+
"""Show contents of cron file that can be used to automate token refresh weekly.
|
|
3029
|
+
|
|
3030
|
+
This will keep the tokens valid, even if cencli is not used.
|
|
3031
|
+
"""
|
|
3032
|
+
if os.name != "posix":
|
|
3033
|
+
cli.econsole.print("This command is currently only supported on Linux using cron. It is possible to do the same via Windows Task Scheduler. Showing Linux cron.weekly output for reference.")
|
|
3034
|
+
|
|
3035
|
+
user = getpass.getuser()
|
|
3036
|
+
exec_path = sys.argv[0]
|
|
3037
|
+
py_path = sys.executable
|
|
3038
|
+
|
|
3039
|
+
config_data = {
|
|
3040
|
+
"user": user,
|
|
3041
|
+
"py_path": py_path,
|
|
3042
|
+
"exec_path": exec_path,
|
|
3043
|
+
"accounts": "" if not accounts else " ".join(accounts)
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
template = Template(cron_weekly)
|
|
3047
|
+
config_out = template.render(config_data)
|
|
3048
|
+
|
|
3049
|
+
cli.econsole.rule("/etc/cron.weekly/cencli file contents")
|
|
3050
|
+
cli.console.print(config_out)
|
|
3051
|
+
cli.econsole.rule()
|
|
3052
|
+
|
|
3053
|
+
cli.econsole.print(
|
|
3054
|
+
"Place the above contents into a file: /etc/cron.weekly/cencli [grey42 italic](requires sudo)[/]\n"
|
|
3055
|
+
f"Alternatively you can pipe the output directly [cyan]cencli show cron {'' if not accounts else ' '.join(accounts)} | sudo tee /etc/cron.weekly/cencli[/]"
|
|
3056
|
+
"\nThen make it executable: [cyan]sudo chmod +x /etc/cron.weekly/cencli[/]"
|
|
3057
|
+
"\n\n[cyan]cencli refresh token[/] [dark_olive_green2 italic]command will always update the tokens for the default workspace (that's the -d flag)[/]"
|
|
3058
|
+
)
|
|
3059
|
+
|
|
3020
3060
|
|
|
3021
3061
|
def _get_cencli_config() -> None:
|
|
3022
3062
|
try:
|
|
@@ -167,7 +167,7 @@ def method(
|
|
|
167
167
|
resp = _check_bool_to_str(args, kwargs, resp_str=str(e))
|
|
168
168
|
|
|
169
169
|
attrs = {
|
|
170
|
-
k: v for k, v in resp.__dict__.items() if k not in ["output", "raw"] and (log.DEBUG or not k.startswith("_"))
|
|
170
|
+
k: v for k, v in resp.__dict__.items() if k not in ["output", "raw", "data_key"] and (log.DEBUG or not k.startswith("_"))
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
req = (
|
|
@@ -411,7 +411,7 @@ class Client(BaseModel):
|
|
|
411
411
|
@classmethod
|
|
412
412
|
def pretty_dt(cls, dt: datetime) -> DateTime:
|
|
413
413
|
if dt is None: # TODO all with potential for there not to be a value need this
|
|
414
|
-
return None
|
|
414
|
+
return DateTime(None, "timediff") # resolves PydanticSerializationError when model_dump_json is called on client with None for last_connected (Failed)
|
|
415
415
|
|
|
416
416
|
return DateTime(dt.timestamp(), "timediff")
|
|
417
417
|
|
|
@@ -383,7 +383,8 @@ class Response:
|
|
|
383
383
|
r = r.replace("message: ", "").replace(self.output["message"], f'[red italic]{self.output["message"]}[/]')
|
|
384
384
|
|
|
385
385
|
r = r.replace("failed:", "[red]failed[/]:").replace("FAILED", "[red]FAILED[/red]").replace("failed_devices", "[red]failed_devices[/]").replace("INVALID", "[red]INVALID[/]")
|
|
386
|
-
r = r.replace("SUCCESS", "[bright_green]SUCCESS[/]").replace("Success", "[bright_green]Success[/]").replace("
|
|
386
|
+
r = r.replace("SUCCESS", "[bright_green]SUCCESS[/]").replace("Success", "[bright_green]Success[/]").replace("Success[/]fully", "Successfully[/]")
|
|
387
|
+
r = r.replace("Success", "[bright_green]Success[/]").replace("success", "[bright_green]success[/]").replace("succeeded_devices", "[bright_green]succeeded_devices[/]")
|
|
387
388
|
r = r.replace("invalid_device", "[red]invalid_device[/]").replace("blocked_device", "[red]blocked_device[/red]").replace("ATHENA_ERROR_DEVICE_ALREADY_EXIST", "[italic dark_orange3]Device already exists[/]")
|
|
388
389
|
|
|
389
390
|
# sanitize sensitive data for demos
|
|
@@ -589,3 +589,10 @@ class ImportExamples:
|
|
|
589
589
|
if key not in self.__dict__.keys():
|
|
590
590
|
log.error(f"An attempt was made to get {key} attr from ImportExamples which is not defined.")
|
|
591
591
|
return f":warning: [bright_red]Error[/] no str defined for [cyan]ImportExamples.{key}[/]"
|
|
592
|
+
|
|
593
|
+
cron_weekly = """#!/usr/bin/env bash
|
|
594
|
+
|
|
595
|
+
/bin/su -c "{{py_path}} {{exec_path}} refresh token -d {{accounts}}" {{user}} &&
|
|
596
|
+
logger -t centralcli "Token Refreshed via cron" ||
|
|
597
|
+
logger -t centralcli "Token Refresh returned error"
|
|
598
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "centralcli"
|
|
3
|
-
version = "7.2.
|
|
3
|
+
version = "7.2.4"
|
|
4
4
|
description = "A CLI for interacting with Aruba Central (Cloud Management Platform). Facilitates bulk imports, exports, reporting. A handy tool if you have devices managed by Aruba Central."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
authors = ["Wade Wells (Pack3tL0ss) <wade@consolepi.org>"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|