scrapli 2.0.0a3__py3-none-musllinux_1_1_aarch64.whl → 2.0.0a5__py3-none-musllinux_1_1_aarch64.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 (59) hide show
  1. scrapli/__init__.py +5 -5
  2. scrapli/auth.py +14 -7
  3. scrapli/cli.py +334 -66
  4. scrapli/cli_parse.py +1 -1
  5. scrapli/cli_result.py +42 -0
  6. scrapli/definitions/aethra_atosnt.yaml +9 -0
  7. scrapli/definitions/alcatel_aos.yaml +9 -0
  8. scrapli/definitions/arista_eos.yaml +1 -0
  9. scrapli/definitions/aruba_aoscx.yaml +33 -0
  10. scrapli/definitions/cisco_aireos.yaml +31 -0
  11. scrapli/definitions/cisco_asa.yaml +48 -0
  12. scrapli/definitions/cisco_cbs.yaml +50 -0
  13. scrapli/definitions/cisco_ftd.yaml +42 -0
  14. scrapli/definitions/cisco_nxos.yaml +2 -0
  15. scrapli/definitions/cumulus_linux.yaml +28 -0
  16. scrapli/definitions/cumulus_vtysh.yaml +42 -0
  17. scrapli/definitions/datacom_dmos.yaml +30 -0
  18. scrapli/definitions/datacom_dmswitch.yaml +32 -0
  19. scrapli/definitions/default.yaml +9 -0
  20. scrapli/definitions/dell_emc.yaml +46 -0
  21. scrapli/definitions/dell_enterprisesonic.yaml +40 -0
  22. scrapli/definitions/dlink_os.yaml +46 -0
  23. scrapli/definitions/edgecore_ecs.yaml +35 -0
  24. scrapli/definitions/eltex_esr.yaml +43 -0
  25. scrapli/definitions/fortinet_fortios.yaml +20 -0
  26. scrapli/definitions/fortinet_wlc.yaml +33 -0
  27. scrapli/definitions/hp_comware.yaml +31 -0
  28. scrapli/definitions/huawei_smartax.yaml +61 -0
  29. scrapli/definitions/huawei_vrp.yaml +56 -0
  30. scrapli/definitions/juniper_junos.yaml +3 -0
  31. scrapli/definitions/nokia_srlinux.yaml +13 -2
  32. scrapli/definitions/nokia_sros.yaml +31 -0
  33. scrapli/definitions/nokia_sros_classic.yaml +33 -0
  34. scrapli/definitions/nokia_sros_classic_aram.yaml +25 -0
  35. scrapli/definitions/paloalto_panos.yaml +36 -0
  36. scrapli/definitions/raisecom_ros.yaml +45 -0
  37. scrapli/definitions/ruckus_fastiron.yaml +45 -0
  38. scrapli/definitions/ruckus_unleashed.yaml +64 -0
  39. scrapli/definitions/siemens_roxii.yaml +28 -0
  40. scrapli/definitions/versa_flexvnf.yaml +45 -0
  41. scrapli/definitions/vyos_vyos.yaml +35 -0
  42. scrapli/definitions/zyxel_dslam.yaml +18 -0
  43. scrapli/ffi.py +1 -1
  44. scrapli/ffi_mapping.py +92 -37
  45. scrapli/ffi_mapping_cli.py +146 -22
  46. scrapli/ffi_mapping_netconf.py +28 -60
  47. scrapli/ffi_mapping_options.py +38 -6
  48. scrapli/ffi_types.py +43 -3
  49. scrapli/helper.py +56 -0
  50. scrapli/lib/{libscrapli.0.0.1-alpha.10.dylib → libscrapli.0.0.1-alpha.17.dylib} +0 -0
  51. scrapli/lib/{libscrapli.so.0.0.1-alpha.10 → libscrapli.so.0.0.1-alpha.17} +0 -0
  52. scrapli/netconf.py +36 -147
  53. scrapli/transport.py +71 -130
  54. {scrapli-2.0.0a3.dist-info → scrapli-2.0.0a5.dist-info}/METADATA +1 -1
  55. scrapli-2.0.0a5.dist-info/RECORD +68 -0
  56. scrapli-2.0.0a3.dist-info/RECORD +0 -35
  57. {scrapli-2.0.0a3.dist-info → scrapli-2.0.0a5.dist-info}/WHEEL +0 -0
  58. {scrapli-2.0.0a3.dist-info → scrapli-2.0.0a5.dist-info}/licenses/LICENSE +0 -0
  59. {scrapli-2.0.0a3.dist-info → scrapli-2.0.0a5.dist-info}/top_level.txt +0 -0
scrapli/cli.py CHANGED
@@ -1,18 +1,21 @@
1
1
  """scrapli.cli"""
2
2
 
3
3
  import importlib.resources
4
- from collections.abc import Callable
4
+ from collections.abc import Awaitable, Callable
5
5
  from ctypes import (
6
6
  c_bool,
7
7
  c_char_p,
8
8
  c_int,
9
9
  c_uint,
10
10
  c_uint64,
11
+ pointer,
11
12
  )
13
+ from dataclasses import dataclass
12
14
  from enum import Enum
13
15
  from logging import getLogger
14
16
  from os import environ
15
17
  from pathlib import Path
18
+ from time import time_ns
16
19
  from types import TracebackType
17
20
  from typing import Any
18
21
 
@@ -31,14 +34,13 @@ from scrapli.exceptions import (
31
34
  )
32
35
  from scrapli.ffi_mapping import LibScrapliMapping
33
36
  from scrapli.ffi_types import (
34
- CancelPointer,
35
37
  DriverPointer,
36
38
  IntPointer,
37
- LogFuncCallback,
38
39
  OperationIdPointer,
39
40
  U64Pointer,
40
41
  ZigSlice,
41
42
  ZigU64Slice,
43
+ ffi_logger_wrapper,
42
44
  to_c_string,
43
45
  )
44
46
  from scrapli.helper import (
@@ -47,6 +49,7 @@ from scrapli.helper import (
47
49
  wait_for_available_operation_result_async,
48
50
  )
49
51
  from scrapli.session import Options as SessionOptions
52
+ from scrapli.transport import BinOptions as TransportBinOptions
50
53
  from scrapli.transport import Options as TransportOptions
51
54
 
52
55
  CLI_DEFINITIONS_PATH_OVERRIDE_ENV = "SCRAPLI_DEFINITIONS_PATH"
@@ -73,6 +76,50 @@ class InputHandling(str, Enum):
73
76
  IGNORE = "ignore"
74
77
 
75
78
 
79
+ @dataclass
80
+ class ReadCallback:
81
+ """
82
+ ReadCallback represents a callback and how it should be executed in a read_with_callbacks op.
83
+
84
+ Args:
85
+ name: friendly name for the callback
86
+ contains: string that when contained in the buf being processed indicates this callback
87
+ should be executed
88
+ contains_pattern: string representing a pcre2 pattern that, if found in the buf being
89
+ processed indicates this callback should be executed -- note: ignored if contains is set
90
+ not_contains: a string that if found in the buf being processed nullifies the containment
91
+ check
92
+ once: bool indicating if this is an "only once" callback or if it can be executed multiple
93
+ times
94
+ completes: bool indicated if, after execution, this callback should "complete" (end) the
95
+ read_with_callbacks operation
96
+ callback: the callback func to execute
97
+
98
+ Returns:
99
+ None
100
+
101
+ Raises:
102
+ N/A
103
+
104
+ """
105
+
106
+ name: str
107
+ contains: str = ""
108
+ contains_pattern: str = ""
109
+ not_contains: str = ""
110
+ once: bool = False
111
+ completes: bool = False
112
+ callback: Callable[["Cli"], None] | None = None
113
+ callback_async: Callable[["Cli"], Awaitable[None]] | None = None
114
+
115
+ def __post_init__(self) -> None:
116
+ if not self.contains and not self.contains_pattern:
117
+ raise OperationException("one of 'contains' or 'contains_pattern' must be set")
118
+
119
+ if self.callback is None and self.callback_async is None:
120
+ raise OperationException("one of 'callback' or 'callback_async' must be set")
121
+
122
+
76
123
  class Cli:
77
124
  """
78
125
  Cli represents a cli connection object.
@@ -93,7 +140,6 @@ class Cli:
93
140
  definition_file_or_name: str,
94
141
  host: str,
95
142
  *,
96
- logger_callback: Callable[[int, str], None] | None = None,
97
143
  port: int = 22,
98
144
  auth_options: AuthOptions | None = None,
99
145
  session_options: SessionOptions | None = None,
@@ -105,6 +151,7 @@ class Cli:
105
151
  logger_name += f":{logging_uid}"
106
152
 
107
153
  self.logger = getLogger(logger_name)
154
+ self.logger_callback = ffi_logger_wrapper(logger=self.logger)
108
155
  self._logging_uid = logging_uid
109
156
 
110
157
  self.ffi_mapping = LibScrapliMapping()
@@ -117,18 +164,13 @@ class Cli:
117
164
  # why. in this case we also just store the host since thats cheap and we need it as a string
118
165
  # in places too
119
166
  self.host = host
120
- self._host = to_c_string(host)
121
-
122
- self.logger_callback = (
123
- LogFuncCallback(logger_callback) if logger_callback else LogFuncCallback(0)
124
- )
125
- self._logger_callback = logger_callback
167
+ self._host = to_c_string(s=host)
126
168
 
127
169
  self.port = port
128
170
 
129
171
  self.auth_options = auth_options or AuthOptions()
130
172
  self.session_options = session_options or SessionOptions()
131
- self.transport_options = transport_options or TransportOptions()
173
+ self.transport_options = transport_options or TransportBinOptions()
132
174
 
133
175
  self.ptr: DriverPointer | None = None
134
176
  self.poll_fd: int = 0
@@ -264,7 +306,6 @@ class Cli:
264
306
  return Cli(
265
307
  host=self.host,
266
308
  definition_file_or_name=self.definition_file_or_name,
267
- logger_callback=self._logger_callback,
268
309
  port=self.port,
269
310
  auth_options=self.auth_options,
270
311
  session_options=self.session_options,
@@ -289,6 +330,8 @@ class Cli:
289
330
  with open(definition_file_or_name, "rb") as f:
290
331
  self.definition_string = f.read()
291
332
 
333
+ return
334
+
292
335
  raise OptionsException(
293
336
  f"definition platform name or filename '{definition_file_or_name}' not found"
294
337
  )
@@ -307,7 +350,7 @@ class Cli:
307
350
  logger_callback=self.logger_callback,
308
351
  host=self._host,
309
352
  port=c_int(self.port),
310
- transport_kind=c_char_p(self.transport_options.get_transport_kind()),
353
+ transport_kind=c_char_p(self.transport_options.transport_kind.encode(encoding="utf-8")),
311
354
  )
312
355
  if ptr == 0: # type: ignore[comparison-overlap]
313
356
  raise AllocationException("failed to allocate cli")
@@ -319,7 +362,7 @@ class Cli:
319
362
  ptr=self._ptr_or_exception(),
320
363
  )
321
364
  )
322
- if poll_fd == 0:
365
+ if poll_fd <= 0:
323
366
  raise AllocationException("failed to allocate cli")
324
367
 
325
368
  self.poll_fd = poll_fd
@@ -393,9 +436,9 @@ class Cli:
393
436
 
394
437
  def _open(
395
438
  self,
439
+ *,
396
440
  operation_id: OperationIdPointer,
397
- cancel: CancelPointer,
398
- ) -> c_uint:
441
+ ) -> None:
399
442
  self._alloc()
400
443
 
401
444
  self.auth_options.apply(self.ffi_mapping, self._ptr_or_exception())
@@ -405,15 +448,12 @@ class Cli:
405
448
  status = self.ffi_mapping.cli_mapping.open(
406
449
  ptr=self._ptr_or_exception(),
407
450
  operation_id=operation_id,
408
- cancel=cancel,
409
451
  )
410
452
  if status != 0:
411
453
  self._free()
412
454
 
413
455
  raise OpenException("failed to submit open operation")
414
456
 
415
- return c_uint(operation_id.contents.value)
416
-
417
457
  def open(
418
458
  self,
419
459
  ) -> Result:
@@ -431,11 +471,10 @@ class Cli:
431
471
 
432
472
  """
433
473
  operation_id = OperationIdPointer(c_uint(0))
434
- cancel = CancelPointer(c_bool(False))
435
474
 
436
- operation_id = self._open(operation_id=operation_id, cancel=cancel)
475
+ self._open(operation_id=operation_id)
437
476
 
438
- return self._get_result(operation_id=operation_id)
477
+ return self._get_result(operation_id=operation_id.contents.value)
439
478
 
440
479
  async def open_async(self) -> Result:
441
480
  """
@@ -452,24 +491,23 @@ class Cli:
452
491
 
453
492
  """
454
493
  operation_id = OperationIdPointer(c_uint(0))
455
- cancel = CancelPointer(c_bool(False))
456
494
 
457
- operation_id = self._open(operation_id=operation_id, cancel=cancel)
495
+ self._open(operation_id=operation_id)
458
496
 
459
- return await self._get_result_async(operation_id=operation_id)
460
-
461
- def _close(self) -> c_uint:
462
- operation_id = OperationIdPointer(c_uint(0))
463
- cancel = CancelPointer(c_bool(False))
497
+ return await self._get_result_async(operation_id=operation_id.contents.value)
464
498
 
499
+ def _close(
500
+ self,
501
+ *,
502
+ operation_id: OperationIdPointer,
503
+ ) -> None:
465
504
  status = self.ffi_mapping.cli_mapping.close(
466
- ptr=self._ptr_or_exception(), operation_id=operation_id, cancel=cancel
505
+ ptr=self._ptr_or_exception(),
506
+ operation_id=operation_id,
467
507
  )
468
508
  if status != 0:
469
509
  raise CloseException("submitting close operation")
470
510
 
471
- return c_uint(operation_id.contents.value)
472
-
473
511
  def close(
474
512
  self,
475
513
  ) -> Result:
@@ -487,9 +525,11 @@ class Cli:
487
525
  CloseException: if the operation fails
488
526
 
489
527
  """
490
- operation_id = self._close()
528
+ operation_id = OperationIdPointer(c_uint(0))
491
529
 
492
- result = self._get_result(operation_id=operation_id)
530
+ self._close(operation_id=operation_id)
531
+
532
+ result = self._get_result(operation_id=operation_id.contents.value)
493
533
 
494
534
  self._free()
495
535
 
@@ -512,14 +552,70 @@ class Cli:
512
552
  CloseException: if the operation fails
513
553
 
514
554
  """
515
- operation_id = self._close()
555
+ operation_id = OperationIdPointer(c_uint(0))
556
+
557
+ self._close(operation_id=operation_id)
516
558
 
517
- result = await self._get_result_async(operation_id=operation_id)
559
+ result = await self._get_result_async(operation_id=operation_id.contents.value)
518
560
 
519
561
  self._free()
520
562
 
521
563
  return result
522
564
 
565
+ def write(self, input_: str) -> None:
566
+ """
567
+ Write the given input.
568
+
569
+ Args:
570
+ input_: the input to write
571
+
572
+ Returns:
573
+ Result: a Result object representing the operation
574
+
575
+ Raises:
576
+ NotOpenedException: if the ptr to the cli object is None (via _ptr_or_exception)
577
+ SubmitOperationException: if the operation fails
578
+
579
+ """
580
+ _input = to_c_string(s=input_)
581
+
582
+ status = self.ffi_mapping.session_mapping.write(
583
+ ptr=self._ptr_or_exception(),
584
+ input_=_input,
585
+ redacted=c_bool(False),
586
+ )
587
+ if status != 0:
588
+ raise SubmitOperationException("executing write and return operation failed")
589
+
590
+ _ = _input
591
+
592
+ def write_and_return(self, input_: str) -> None:
593
+ """
594
+ Write the given input then send a return character.
595
+
596
+ Args:
597
+ input_: the input to write
598
+
599
+ Returns:
600
+ Result: a Result object representing the operation
601
+
602
+ Raises:
603
+ NotOpenedException: if the ptr to the cli object is None (via _ptr_or_exception)
604
+ SubmitOperationException: if the operation fails
605
+
606
+ """
607
+ _input = to_c_string(s=input_)
608
+
609
+ status = self.ffi_mapping.session_mapping.write_and_return(
610
+ ptr=self._ptr_or_exception(),
611
+ input_=_input,
612
+ redacted=c_bool(False),
613
+ )
614
+ if status != 0:
615
+ raise SubmitOperationException("executing write and return operation failed")
616
+
617
+ _ = _input
618
+
523
619
  def _get_result(
524
620
  self,
525
621
  operation_id: c_uint,
@@ -658,13 +754,11 @@ class Cli:
658
754
  self,
659
755
  *,
660
756
  operation_id: OperationIdPointer,
661
- cancel: CancelPointer,
662
757
  requested_mode: c_char_p,
663
758
  ) -> c_uint:
664
759
  status = self.ffi_mapping.cli_mapping.enter_mode(
665
760
  ptr=self._ptr_or_exception(),
666
761
  operation_id=operation_id,
667
- cancel=cancel,
668
762
  requested_mode=requested_mode,
669
763
  )
670
764
  if status != 0:
@@ -698,12 +792,12 @@ class Cli:
698
792
  _ = operation_timeout_ns
699
793
 
700
794
  operation_id = OperationIdPointer(c_uint(0))
701
- cancel = CancelPointer(c_bool(False))
702
795
 
703
796
  _requested_mode = to_c_string(requested_mode)
704
797
 
705
798
  operation_id = self._enter_mode(
706
- operation_id=operation_id, cancel=cancel, requested_mode=_requested_mode
799
+ operation_id=operation_id,
800
+ requested_mode=_requested_mode,
707
801
  )
708
802
 
709
803
  return self._get_result(operation_id=operation_id)
@@ -734,12 +828,12 @@ class Cli:
734
828
  _ = operation_timeout_ns
735
829
 
736
830
  operation_id = OperationIdPointer(c_uint(0))
737
- cancel = CancelPointer(c_bool(False))
738
831
 
739
832
  _requested_mode = to_c_string(requested_mode)
740
833
 
741
834
  operation_id = self._enter_mode(
742
- operation_id=operation_id, cancel=cancel, requested_mode=_requested_mode
835
+ operation_id=operation_id,
836
+ requested_mode=_requested_mode,
743
837
  )
744
838
 
745
839
  return await self._get_result_async(operation_id=operation_id)
@@ -748,12 +842,10 @@ class Cli:
748
842
  self,
749
843
  *,
750
844
  operation_id: OperationIdPointer,
751
- cancel: CancelPointer,
752
845
  ) -> c_uint:
753
846
  status = self.ffi_mapping.cli_mapping.get_prompt(
754
847
  ptr=self._ptr_or_exception(),
755
848
  operation_id=operation_id,
756
- cancel=cancel,
757
849
  )
758
850
  if status != 0:
759
851
  raise SubmitOperationException("submitting get prompt operation failed")
@@ -784,9 +876,8 @@ class Cli:
784
876
  _ = operation_timeout_ns
785
877
 
786
878
  operation_id = OperationIdPointer(c_uint(0))
787
- cancel = CancelPointer(c_bool(False))
788
879
 
789
- operation_id = self._get_prompt(operation_id=operation_id, cancel=cancel)
880
+ operation_id = self._get_prompt(operation_id=operation_id)
790
881
 
791
882
  return self._get_result(operation_id=operation_id)
792
883
 
@@ -814,9 +905,8 @@ class Cli:
814
905
  _ = operation_timeout_ns
815
906
 
816
907
  operation_id = OperationIdPointer(c_uint(0))
817
- cancel = CancelPointer(c_bool(False))
818
908
 
819
- operation_id = self._get_prompt(operation_id=operation_id, cancel=cancel)
909
+ operation_id = self._get_prompt(operation_id=operation_id)
820
910
 
821
911
  return await self._get_result_async(operation_id=operation_id)
822
912
 
@@ -824,7 +914,6 @@ class Cli:
824
914
  self,
825
915
  *,
826
916
  operation_id: OperationIdPointer,
827
- cancel: CancelPointer,
828
917
  input_: c_char_p,
829
918
  requested_mode: c_char_p,
830
919
  input_handling: c_char_p,
@@ -834,7 +923,6 @@ class Cli:
834
923
  status = self.ffi_mapping.cli_mapping.send_input(
835
924
  ptr=self._ptr_or_exception(),
836
925
  operation_id=operation_id,
837
- cancel=cancel,
838
926
  input_=input_,
839
927
  requested_mode=requested_mode,
840
928
  input_handling=input_handling,
@@ -880,7 +968,6 @@ class Cli:
880
968
  _ = operation_timeout_ns
881
969
 
882
970
  operation_id = OperationIdPointer(c_uint(0))
883
- cancel = CancelPointer(c_bool(False))
884
971
 
885
972
  _input = to_c_string(input_)
886
973
  _requested_mode = to_c_string(requested_mode)
@@ -888,7 +975,6 @@ class Cli:
888
975
 
889
976
  operation_id = self._send_input(
890
977
  operation_id=operation_id,
891
- cancel=cancel,
892
978
  input_=_input,
893
979
  requested_mode=_requested_mode,
894
980
  input_handling=_input_handling,
@@ -932,7 +1018,6 @@ class Cli:
932
1018
  _ = operation_timeout_ns
933
1019
 
934
1020
  operation_id = OperationIdPointer(c_uint(0))
935
- cancel = CancelPointer(c_bool(False))
936
1021
 
937
1022
  _input = to_c_string(input_)
938
1023
  _requested_mode = to_c_string(requested_mode)
@@ -940,7 +1025,6 @@ class Cli:
940
1025
 
941
1026
  operation_id = self._send_input(
942
1027
  operation_id=operation_id,
943
- cancel=cancel,
944
1028
  input_=_input,
945
1029
  requested_mode=_requested_mode,
946
1030
  input_handling=_input_handling,
@@ -986,8 +1070,6 @@ class Cli:
986
1070
  # meaning all the "inputs" combined, not individually
987
1071
  _ = operation_timeout_ns
988
1072
 
989
- cancel = CancelPointer(c_bool(False))
990
-
991
1073
  result: Result | None = None
992
1074
 
993
1075
  for input_ in inputs:
@@ -999,7 +1081,6 @@ class Cli:
999
1081
 
1000
1082
  operation_id = self._send_input(
1001
1083
  operation_id=operation_id,
1002
- cancel=cancel,
1003
1084
  input_=_input,
1004
1085
  requested_mode=_requested_mode,
1005
1086
  input_handling=_input_handling,
@@ -1055,8 +1136,6 @@ class Cli:
1055
1136
  # meaning all the "inputs" combined, not individually
1056
1137
  _ = operation_timeout_ns
1057
1138
 
1058
- cancel = CancelPointer(c_bool(False))
1059
-
1060
1139
  result: Result | None = None
1061
1140
 
1062
1141
  for input_ in inputs:
@@ -1068,7 +1147,6 @@ class Cli:
1068
1147
 
1069
1148
  operation_id = self._send_input(
1070
1149
  operation_id=operation_id,
1071
- cancel=cancel,
1072
1150
  input_=_input,
1073
1151
  requested_mode=_requested_mode,
1074
1152
  input_handling=_input_handling,
@@ -1179,7 +1257,6 @@ class Cli:
1179
1257
  self,
1180
1258
  *,
1181
1259
  operation_id: OperationIdPointer,
1182
- cancel: CancelPointer,
1183
1260
  input_: c_char_p,
1184
1261
  prompt: c_char_p,
1185
1262
  prompt_pattern: c_char_p,
@@ -1193,7 +1270,6 @@ class Cli:
1193
1270
  status = self.ffi_mapping.cli_mapping.send_prompted_input(
1194
1271
  ptr=self._ptr_or_exception(),
1195
1272
  operation_id=operation_id,
1196
- cancel=cancel,
1197
1273
  input_=input_,
1198
1274
  prompt=prompt,
1199
1275
  prompt_pattern=prompt_pattern,
@@ -1252,7 +1328,6 @@ class Cli:
1252
1328
  _ = operation_timeout_ns
1253
1329
 
1254
1330
  operation_id = OperationIdPointer(c_uint(0))
1255
- cancel = CancelPointer(c_bool(False))
1256
1331
 
1257
1332
  _input = to_c_string(input_)
1258
1333
  _prompt = to_c_string(prompt)
@@ -1264,7 +1339,6 @@ class Cli:
1264
1339
 
1265
1340
  operation_id = self._send_prompted_input(
1266
1341
  operation_id=operation_id,
1267
- cancel=cancel,
1268
1342
  input_=_input,
1269
1343
  prompt=_prompt,
1270
1344
  prompt_pattern=_prompt_pattern,
@@ -1321,7 +1395,6 @@ class Cli:
1321
1395
  _ = operation_timeout_ns
1322
1396
 
1323
1397
  operation_id = OperationIdPointer(c_uint(0))
1324
- cancel = CancelPointer(c_bool(False))
1325
1398
 
1326
1399
  _input = to_c_string(input_)
1327
1400
  _prompt = to_c_string(prompt)
@@ -1333,7 +1406,6 @@ class Cli:
1333
1406
 
1334
1407
  operation_id = self._send_prompted_input(
1335
1408
  operation_id=operation_id,
1336
- cancel=cancel,
1337
1409
  input_=_input,
1338
1410
  prompt=_prompt,
1339
1411
  prompt_pattern=_prompt_pattern,
@@ -1347,6 +1419,202 @@ class Cli:
1347
1419
 
1348
1420
  return await self._get_result_async(operation_id=operation_id)
1349
1421
 
1422
+ @handle_operation_timeout
1423
+ def read_with_callbacks(
1424
+ self,
1425
+ callbacks: list[ReadCallback],
1426
+ *,
1427
+ initial_input: str = "",
1428
+ operation_timeout_ns: int | None = None,
1429
+ ) -> Result:
1430
+ """
1431
+ Read from the device and react to the output with some callback.
1432
+
1433
+ Args:
1434
+ callbacks: a list of callbacks to process when reading from the session
1435
+ initial_input: an initial input to send
1436
+ operation_timeout_ns: operation timeout in ns for this operation
1437
+
1438
+ Returns:
1439
+ Result: a Result object representing the operation
1440
+
1441
+ Raises:
1442
+ NotOpenedException: if the ptr to the cli object is None (via _ptr_or_exception)
1443
+ SubmitOperationException: if the operation fails
1444
+
1445
+ """
1446
+ # only used in the decorator
1447
+ _ = operation_timeout_ns
1448
+
1449
+ start_time = time_ns()
1450
+
1451
+ if initial_input:
1452
+ self.write_and_return(input_=initial_input)
1453
+
1454
+ pos = 0
1455
+
1456
+ result = ""
1457
+ result_raw = b""
1458
+
1459
+ executed_callbacks = set()
1460
+
1461
+ while True:
1462
+ operation_id = OperationIdPointer(c_uint(0))
1463
+
1464
+ status = self.ffi_mapping.cli_mapping.read_any(
1465
+ ptr=self._ptr_or_exception(),
1466
+ operation_id=operation_id,
1467
+ )
1468
+ if status != 0:
1469
+ raise SubmitOperationException("submitting read any operation failed")
1470
+
1471
+ intermediate_result = self._get_result(operation_id=operation_id.contents.value)
1472
+
1473
+ result += intermediate_result.result
1474
+ result_raw += intermediate_result.result_raw
1475
+
1476
+ for callback in callbacks:
1477
+ if callback.name in executed_callbacks and callback.once:
1478
+ continue
1479
+
1480
+ execute = pointer(c_bool(False))
1481
+
1482
+ status = self.ffi_mapping.cli_mapping.read_callback_should_execute(
1483
+ buf=to_c_string(result[pos:]),
1484
+ name=to_c_string(callback.name),
1485
+ contains=to_c_string(callback.contains),
1486
+ contains_pattern=to_c_string(callback.contains_pattern),
1487
+ not_contains=to_c_string(callback.not_contains),
1488
+ execute=execute,
1489
+ )
1490
+ if status != 0:
1491
+ raise OperationException("failed checking if callback should execute")
1492
+
1493
+ if execute.contents.value is False:
1494
+ continue
1495
+
1496
+ executed_callbacks.add(callback.name)
1497
+
1498
+ pos = len(result)
1499
+
1500
+ if callback.callback is None:
1501
+ raise OperationException("callback is None, cannot proceed")
1502
+ else:
1503
+ callback.callback(self)
1504
+
1505
+ if callback.completes is True:
1506
+ return Result(
1507
+ inputs=initial_input,
1508
+ host=self.host,
1509
+ port=self.port,
1510
+ start_time=start_time,
1511
+ splits=[time_ns()],
1512
+ results_raw=result_raw,
1513
+ results=result,
1514
+ results_failed_indicator="",
1515
+ textfsm_platform=self.ntc_templates_platform,
1516
+ genie_platform=self.genie_platform,
1517
+ )
1518
+
1519
+ @handle_operation_timeout_async
1520
+ async def read_with_callbacks_async(
1521
+ self,
1522
+ callbacks: list[ReadCallback],
1523
+ *,
1524
+ initial_input: str = "",
1525
+ operation_timeout_ns: int | None = None,
1526
+ ) -> Result:
1527
+ """
1528
+ Read from the device and react to the output with some callback.
1529
+
1530
+ Args:
1531
+ callbacks: a list of callbacks to process when reading from the session
1532
+ initial_input: an initial input to send
1533
+ operation_timeout_ns: operation timeout in ns for this operation
1534
+
1535
+ Returns:
1536
+ Result: a Result object representing the operation
1537
+
1538
+ Raises:
1539
+ NotOpenedException: if the ptr to the cli object is None (via _ptr_or_exception)
1540
+ SubmitOperationException: if the operation fails
1541
+
1542
+ """
1543
+ # only used in the decorator
1544
+ _ = operation_timeout_ns
1545
+
1546
+ start_time = time_ns()
1547
+
1548
+ if initial_input:
1549
+ self.write_and_return(input_=initial_input)
1550
+
1551
+ pos = 0
1552
+
1553
+ result = ""
1554
+ result_raw = b""
1555
+
1556
+ executed_callbacks = set()
1557
+
1558
+ while True:
1559
+ operation_id = OperationIdPointer(c_uint(0))
1560
+
1561
+ status = self.ffi_mapping.cli_mapping.read_any(
1562
+ ptr=self._ptr_or_exception(),
1563
+ operation_id=operation_id,
1564
+ )
1565
+ if status != 0:
1566
+ raise SubmitOperationException("submitting read any operation failed")
1567
+
1568
+ intermediate_result = await self._get_result_async(
1569
+ operation_id=operation_id.contents.value
1570
+ )
1571
+
1572
+ result += intermediate_result.result
1573
+ result_raw += intermediate_result.result_raw
1574
+
1575
+ for callback in callbacks:
1576
+ if callback.name in executed_callbacks and callback.once:
1577
+ continue
1578
+
1579
+ execute = pointer(c_bool(False))
1580
+
1581
+ status = self.ffi_mapping.cli_mapping.read_callback_should_execute(
1582
+ buf=to_c_string(result[pos:]),
1583
+ name=to_c_string(callback.name),
1584
+ contains=to_c_string(callback.contains),
1585
+ contains_pattern=to_c_string(callback.contains_pattern),
1586
+ not_contains=to_c_string(callback.not_contains),
1587
+ execute=execute,
1588
+ )
1589
+ if status != 0:
1590
+ raise OperationException("failed checking if callback should execute")
1591
+
1592
+ if execute.contents.value is False:
1593
+ continue
1594
+
1595
+ executed_callbacks.add(callback.name)
1596
+
1597
+ pos = len(result)
1598
+
1599
+ if callback.callback is None:
1600
+ raise OperationException("callback is None, cannot proceed")
1601
+ else:
1602
+ callback.callback(self)
1603
+
1604
+ if callback.completes is True:
1605
+ return Result(
1606
+ inputs=initial_input,
1607
+ host=self.host,
1608
+ port=self.port,
1609
+ start_time=start_time,
1610
+ splits=[time_ns()],
1611
+ results_raw=result_raw,
1612
+ results=result,
1613
+ results_failed_indicator="",
1614
+ textfsm_platform=self.ntc_templates_platform,
1615
+ genie_platform=self.genie_platform,
1616
+ )
1617
+
1350
1618
  @staticmethod
1351
1619
  def ___getwide___() -> None: # pragma: no cover
1352
1620
  """