ciscoyoke 0.1.0a1__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.
- ciscoyoke-0.1.0a1/.gitignore +31 -0
- ciscoyoke-0.1.0a1/CHANGELOG.md +39 -0
- ciscoyoke-0.1.0a1/CONTRIBUTING.md +65 -0
- ciscoyoke-0.1.0a1/LICENSE +21 -0
- ciscoyoke-0.1.0a1/PKG-INFO +291 -0
- ciscoyoke-0.1.0a1/README.md +235 -0
- ciscoyoke-0.1.0a1/docs/ARCHITECTURE.md +92 -0
- ciscoyoke-0.1.0a1/docs/HARDWARE-TESTING.md +85 -0
- ciscoyoke-0.1.0a1/docs/SAFETY.md +76 -0
- ciscoyoke-0.1.0a1/docs/SPEC.md +1439 -0
- ciscoyoke-0.1.0a1/docs/THREAT-MODEL.md +359 -0
- ciscoyoke-0.1.0a1/pyproject.toml +102 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/__init__.py +7 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/capture.py +192 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/capturing.py +309 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/cli.py +570 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/cli_extra.py +461 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/commands.py +465 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/credentials.py +168 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/diagnose.py +200 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/doctor.py +235 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/identify/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/identify/facts.py +229 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/identify/memory.py +72 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/identify/probe.py +147 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/bootproof.py +237 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/compat.py +212 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/drivers.py +290 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/fingerprint.py +106 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/plan.py +408 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/image/tftp.py +226 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/imaging.py +473 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/interactive.py +360 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/converge.py +186 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/lease.py +238 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/model.py +273 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/paths.py +74 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/journal/store.py +270 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/labs.py +229 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/archive.py +349 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/health.py +183 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/recover.py +179 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/rescue.py +231 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/lifecycle/reset.py +458 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/platform_profiles.py +203 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/base.py +380 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/human.py +227 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/ios_router.py +183 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/ios_switch.py +423 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/transfer.py +211 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/playbook/xmodem.py +316 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/report.py +223 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/result/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/result/exits.py +39 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/result/schema.py +112 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/session.py +273 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/stream/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/stream/buffer.py +70 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/stream/signals.py +261 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/stream/tracker.py +292 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/support.py +304 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/topology/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/topology/apply.py +194 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/topology/labfile.py +317 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/topology/verify.py +277 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/fake.py +219 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/recorder.py +60 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/replay.py +106 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/schema.py +170 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/scrub.py +302 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transcript/writer.py +98 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/__init__.py +1 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/base.py +167 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/identity.py +150 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/labels.py +169 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/rfc2217.py +152 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/transport/serial_.py +186 -0
- ciscoyoke-0.1.0a1/src/ciscoyoke/ui.py +120 -0
- ciscoyoke-0.1.0a1/tests/fixtures/README.md +55 -0
- ciscoyoke-0.1.0a1/tests/fixtures/hw-switch-2950-cold-boot-locked.ytx.pub +2 -0
- ciscoyoke-0.1.0a1/tests/fixtures/hw-switch-2950-recover-no-restore.ytx.pub +21 -0
- ciscoyoke-0.1.0a1/tests/fixtures/hw-switch-2950-recover-rename-hidden-by-syslog.ytx.pub +23 -0
- ciscoyoke-0.1.0a1/tests/fixtures/router-1760-intake.ytx.pub +11 -0
- ciscoyoke-0.1.0a1/tests/fixtures/router-1760-rommon.ytx.pub +3 -0
- ciscoyoke-0.1.0a1/tests/fixtures/switch-2950-bootloader.ytx.pub +7 -0
- ciscoyoke-0.1.0a1/tests/fixtures/switch-2950-locked.ytx.pub +4 -0
- ciscoyoke-0.1.0a1/tests/fixtures/switch-2950-recovery-disabled.ytx.pub +5 -0
- ciscoyoke-0.1.0a1/tests/test_archive.py +173 -0
- ciscoyoke-0.1.0a1/tests/test_capture.py +204 -0
- ciscoyoke-0.1.0a1/tests/test_chunk_invariance.py +107 -0
- ciscoyoke-0.1.0a1/tests/test_clean_reset.py +245 -0
- ciscoyoke-0.1.0a1/tests/test_cli.py +158 -0
- ciscoyoke-0.1.0a1/tests/test_credentials.py +167 -0
- ciscoyoke-0.1.0a1/tests/test_front_door.py +44 -0
- ciscoyoke-0.1.0a1/tests/test_hardware_fixtures.py +151 -0
- ciscoyoke-0.1.0a1/tests/test_image.py +394 -0
- ciscoyoke-0.1.0a1/tests/test_intake_replay.py +150 -0
- ciscoyoke-0.1.0a1/tests/test_interactive.py +206 -0
- ciscoyoke-0.1.0a1/tests/test_journal.py +290 -0
- ciscoyoke-0.1.0a1/tests/test_lease.py +147 -0
- ciscoyoke-0.1.0a1/tests/test_packaging.py +28 -0
- ciscoyoke-0.1.0a1/tests/test_platform_families.py +84 -0
- ciscoyoke-0.1.0a1/tests/test_playbook.py +408 -0
- ciscoyoke-0.1.0a1/tests/test_remote_support.py +290 -0
- ciscoyoke-0.1.0a1/tests/test_rescue.py +260 -0
- ciscoyoke-0.1.0a1/tests/test_round4_fixes.py +449 -0
- ciscoyoke-0.1.0a1/tests/test_round5_fixes.py +347 -0
- ciscoyoke-0.1.0a1/tests/test_scrub.py +130 -0
- ciscoyoke-0.1.0a1/tests/test_switch_recovery.py +198 -0
- ciscoyoke-0.1.0a1/tests/test_topology.py +304 -0
- ciscoyoke-0.1.0a1/tests/test_tracker.py +142 -0
- ciscoyoke-0.1.0a1/tests/test_transcript.py +161 -0
- ciscoyoke-0.1.0a1/tests/test_transport.py +135 -0
- ciscoyoke-0.1.0a1/tests/test_ui.py +87 -0
- ciscoyoke-0.1.0a1/tests/test_xmodem.py +274 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Raw transcripts are private by definition: they can contain a previous
|
|
2
|
+
# owner's credentials, addressing and keys. Only scrubbed .ytx.pub fixtures
|
|
3
|
+
# belong in the repository. CI enforces this too.
|
|
4
|
+
*.ytx
|
|
5
|
+
!*.ytx.pub
|
|
6
|
+
|
|
7
|
+
# Salvaged configuration from second-hand gear is somebody else's network.
|
|
8
|
+
archives/
|
|
9
|
+
*.cfg.salvaged
|
|
10
|
+
|
|
11
|
+
# Operational state: journals and transport leases.
|
|
12
|
+
state.db
|
|
13
|
+
state.db-wal
|
|
14
|
+
state.db-shm
|
|
15
|
+
locks/
|
|
16
|
+
|
|
17
|
+
.venv/
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.py[cod]
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
.hypothesis/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
|
|
30
|
+
# Meta-documentation about building the project is not project documentation.
|
|
31
|
+
*.docx
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes. Versions follow [PEP 440](https://peps.python.org/pep-0440/);
|
|
4
|
+
the format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.0a1] - 2026-09-30
|
|
9
|
+
|
|
10
|
+
First public alpha. Run end to end on a real Catalyst 2950; the 2960 family is
|
|
11
|
+
implemented from Cisco's documentation and wants testers.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `rescue`, `scan`, `intake`, `health`, `archive`, `doctor`, `capture`,
|
|
15
|
+
`sweep`: identify and preserve without changing anything. `scan` shows what
|
|
16
|
+
each device needs next.
|
|
17
|
+
- `recover access`: guided password recovery for Catalysts (Mode button) and
|
|
18
|
+
routers (break), with `--no-restore` for a clean device and `--platform` for
|
|
19
|
+
a locked one.
|
|
20
|
+
- `reset`: a clean lab baseline, deleting `vlan.dat` and stale configuration
|
|
21
|
+
files only after archiving them, and proving the deletions.
|
|
22
|
+
- `recover image`: XMODEM image rescue ending in boot proof.
|
|
23
|
+
- Platform profiles per Catalyst family, each labelled by the evidence behind
|
|
24
|
+
it (`hardware`, `documentation`, `generic`).
|
|
25
|
+
- `report` and `transcript replay`: a failed run on hardware nobody here owns
|
|
26
|
+
becomes a scrubbed report, then a replayed fixture and a failing test.
|
|
27
|
+
- Three scrubbed recordings from a real WS-C2950G-24-EI as test fixtures.
|
|
28
|
+
|
|
29
|
+
### Fixed (found on first contact with real hardware)
|
|
30
|
+
- Bootloader prompts ending LF-CR were not recognised.
|
|
31
|
+
- A rename over an existing `config.old` failed silently, and the switch booted
|
|
32
|
+
back into its login.
|
|
33
|
+
- A console log message landing after an IOS question hid the question.
|
|
34
|
+
- Scrubbing missed secrets split across serial reads, a RADIUS key written
|
|
35
|
+
after `auth-port`/`acct-port`, banners naming the previous owner, and serial
|
|
36
|
+
numbers and MAC addresses.
|
|
37
|
+
|
|
38
|
+
[Unreleased]: https://github.com/Ryan-Clinton/ciscoyoke/compare/v0.1.0a1...HEAD
|
|
39
|
+
[0.1.0a1]: https://github.com/Ryan-Clinton/ciscoyoke/releases/tag/v0.1.0a1
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
**You don't need to write Python to help.** ciscoyoke's weakest point is the
|
|
4
|
+
hardware nobody here owns, and the most valuable contribution is evidence from
|
|
5
|
+
yours.
|
|
6
|
+
|
|
7
|
+
## Ways to help, roughly in order of value
|
|
8
|
+
|
|
9
|
+
1. **Test on hardware you own.** Anything in the
|
|
10
|
+
[wanted table](README.md#hardware-tested-and-wanted), or anything not in it.
|
|
11
|
+
`ciscoyoke rescue PORT` is read-only and a good start.
|
|
12
|
+
2. **Send a report when it fails, or when it works on something new.**
|
|
13
|
+
`ciscoyoke report` builds one zip: the run scrubbed, with configuration output
|
|
14
|
+
removed. Read the transcript inside, then open a
|
|
15
|
+
[hardware report](https://github.com/Ryan-Clinton/ciscoyoke/issues/new?template=hardware-report.yml).
|
|
16
|
+
Include the model from the label and what the LEDs did. No recording holds
|
|
17
|
+
either.
|
|
18
|
+
3. **Add a platform profile from Cisco's documentation.** One entry in
|
|
19
|
+
`src/ciscoyoke/platform_profiles.py`: when to release Mode, whether the
|
|
20
|
+
bootloader has `load_helper`, the console port, and the link to the Cisco
|
|
21
|
+
document you took it from. It's marked `documentation` until someone runs it.
|
|
22
|
+
4. **Improve detection.** A prompt the tool didn't recognise is usually one
|
|
23
|
+
pattern in `src/ciscoyoke/stream/signals.py` plus a test built from the real
|
|
24
|
+
text.
|
|
25
|
+
5. **Docs**: anything that confused you is a bug.
|
|
26
|
+
6. **Code**: see below.
|
|
27
|
+
|
|
28
|
+
Issues labelled [good first issue](https://github.com/Ryan-Clinton/ciscoyoke/labels/good%20first%20issue)
|
|
29
|
+
and [help wanted](https://github.com/Ryan-Clinton/ciscoyoke/labels/help%20wanted)
|
|
30
|
+
are the places to start.
|
|
31
|
+
|
|
32
|
+
**Issues aren't assigned.** There's no need to ask to work on one: open a pull
|
|
33
|
+
request (a draft is fine, early is welcome) and it'll be reviewed. If two land
|
|
34
|
+
for the same issue, the first one that's ready gets merged. Hardware issues are
|
|
35
|
+
open to anyone who owns the device.
|
|
36
|
+
|
|
37
|
+
## Two rules that aren't negotiable
|
|
38
|
+
|
|
39
|
+
- **Never commit a raw recording** (`.ytx`), and never commit one that read
|
|
40
|
+
somebody's configuration, however well it scrubs. CI refuses raw ones; the
|
|
41
|
+
second is on you. See [tests/fixtures/README.md](tests/fixtures/README.md).
|
|
42
|
+
- **Never claim support that isn't earned.** A ● needs a committed hardware
|
|
43
|
+
recording; documentation-derived work is ○. See
|
|
44
|
+
[docs/HARDWARE-TESTING.md](docs/HARDWARE-TESTING.md).
|
|
45
|
+
|
|
46
|
+
## Working on the code
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/Ryan-Clinton/ciscoyoke
|
|
50
|
+
cd ciscoyoke
|
|
51
|
+
python -m venv .venv
|
|
52
|
+
.venv/bin/pip install -e ".[dev]" # Windows: .venv\Scripts\pip
|
|
53
|
+
.venv/bin/pytest # no hardware needed
|
|
54
|
+
.venv/bin/ruff check src tests && .venv/bin/mypy
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The suite runs against recorded device behaviour, so nothing needs to be plugged
|
|
58
|
+
in. A change to how the tool reads a console should come with a test built from
|
|
59
|
+
real device output where there is any: `ciscoyoke transcript replay` shows what
|
|
60
|
+
the tracker makes of a recording.
|
|
61
|
+
|
|
62
|
+
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) is the quickest orientation;
|
|
63
|
+
[docs/SPEC.md](docs/SPEC.md) is the whole design.
|
|
64
|
+
|
|
65
|
+
Be decent to each other: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Clinton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ciscoyoke
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Rescue old Cisco hardware from the serial console: identify, preserve, recover and reset second-hand Catalysts and routers, with no management IP.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Ryan-Clinton/ciscoyoke
|
|
6
|
+
Project-URL: Specification, https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/SPEC.md
|
|
7
|
+
Project-URL: Changelog, https://github.com/Ryan-Clinton/ciscoyoke/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/Ryan-Clinton/ciscoyoke/issues
|
|
9
|
+
Project-URL: Hardware reports, https://github.com/Ryan-Clinton/ciscoyoke/issues/new?template=hardware-report.yml
|
|
10
|
+
Author: Ryan Clinton
|
|
11
|
+
License: MIT License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2026 Ryan Clinton
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
in the Software without restriction, including without limitation the rights
|
|
18
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
furnished to do so, subject to the following conditions:
|
|
21
|
+
|
|
22
|
+
The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
copies or substantial portions of the Software.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Keywords: catalyst,ccna,cisco,cisco-ios,console,homelab,network-automation,password-recovery,recovery,rommon,serial,serial-console,xmodem
|
|
34
|
+
Classifier: Development Status :: 3 - Alpha
|
|
35
|
+
Classifier: Environment :: Console
|
|
36
|
+
Classifier: Intended Audience :: System Administrators
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
41
|
+
Classifier: Topic :: System :: Networking
|
|
42
|
+
Classifier: Topic :: System :: Recovery Tools
|
|
43
|
+
Requires-Python: >=3.11
|
|
44
|
+
Requires-Dist: platformdirs>=4.0
|
|
45
|
+
Requires-Dist: pyserial>=3.5
|
|
46
|
+
Requires-Dist: rich>=13.0
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
49
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
50
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
52
|
+
Requires-Dist: tftpy>=0.8; extra == 'dev'
|
|
53
|
+
Provides-Extra: tftp
|
|
54
|
+
Requires-Dist: tftpy>=0.8; extra == 'tftp'
|
|
55
|
+
Description-Content-Type: text/markdown
|
|
56
|
+
|
|
57
|
+
# ciscoyoke
|
|
58
|
+
|
|
59
|
+
[](https://github.com/Ryan-Clinton/ciscoyoke/actions/workflows/ci.yml)
|
|
60
|
+
[](https://www.python.org/downloads/)
|
|
61
|
+
[](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/LICENSE)
|
|
62
|
+
[](#hardware-tested-and-wanted)
|
|
63
|
+
|
|
64
|
+
**Rescue old Cisco hardware from the serial console.**
|
|
65
|
+
|
|
66
|
+
Bought a Catalyst on eBay? Inherited a switch with somebody else's password?
|
|
67
|
+
Found one in a store room that nobody has the login for? Got a `switch:` prompt
|
|
68
|
+
and no working IOS? Not even sure which COM port the cable is on?
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
unknown / locked / broken known-good lab device
|
|
72
|
+
│ ▲
|
|
73
|
+
└──── console cable ──► ciscoyoke ────────────┘
|
|
74
|
+
no IP address needed
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
<p align="center">
|
|
78
|
+
<img src="https://raw.githubusercontent.com/Ryan-Clinton/ciscoyoke/main/docs/demo/2950-recovery.svg" alt="A real Catalyst 2950 going from the Mode-button bootloader to an unlocked Switch# prompt, replayed from a committed hardware recording" width="100%">
|
|
79
|
+
</p>
|
|
80
|
+
<p align="center"><sub>Not a mock-up: a real WS-C2950G-24-EI, replayed from
|
|
81
|
+
<a href="https://github.com/Ryan-Clinton/ciscoyoke/blob/main/tests/fixtures/hw-switch-2950-recover-no-restore.ytx.pub">its committed recording</a>
|
|
82
|
+
with the long silences shortened. Serial numbers and MAC scrubbed.</sub></p>
|
|
83
|
+
|
|
84
|
+
> **Early alpha.** Run end to end on a real Catalyst 2950; the 2960 family is
|
|
85
|
+
> implemented from Cisco's documentation and **wants testers**. See
|
|
86
|
+
> [hardware](#hardware-tested-and-wanted).
|
|
87
|
+
|
|
88
|
+
## Try it
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pipx install git+https://github.com/Ryan-Clinton/ciscoyoke # works today
|
|
92
|
+
# pipx install ciscoyoke # from PyPI, once 0.1.0a1 is released
|
|
93
|
+
|
|
94
|
+
ciscoyoke doctor # is the cable and adapter OK?
|
|
95
|
+
ciscoyoke scan # what is on every serial port?
|
|
96
|
+
ciscoyoke rescue COM4 # what is this device, and what does it need?
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Most people only need `ciscoyoke rescue`.** It identifies the device, works
|
|
100
|
+
out its state, preserves whatever it can read, and tells you the safest next
|
|
101
|
+
command — then stops, because everything after that changes the device and is
|
|
102
|
+
your decision.
|
|
103
|
+
|
|
104
|
+
## Three real sessions
|
|
105
|
+
|
|
106
|
+
These are real output from the bench 2950, abridged.
|
|
107
|
+
|
|
108
|
+
**A switch nobody knows anything about**
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
$ ciscoyoke intake COM4
|
|
112
|
+
|
|
113
|
+
State: user_exec (observed/high)
|
|
114
|
+
Model: WS-C2950G-24-EI
|
|
115
|
+
IOS version: 12.1(9)EA1
|
|
116
|
+
Config reg: 0xF
|
|
117
|
+
|
|
118
|
+
identified from show version
|
|
119
|
+
No destructive action taken.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**A locked switch**, with a previous owner's console login and enable secret:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
$ ciscoyoke recover access COM4 --no-restore --confirm
|
|
126
|
+
|
|
127
|
+
Platform from memory: WS-C2950G-24-EI (seen on this adapter, from intake)
|
|
128
|
+
|
|
129
|
+
HUMAN ACTION REQUIRED
|
|
130
|
+
Unplug the switch. Hold the MODE button down, plug the power back in,
|
|
131
|
+
and release it when the STAT LED goes out (about 5 seconds).
|
|
132
|
+
✓ observed: bootloader
|
|
133
|
+
|
|
134
|
+
IOS loads config.text (the default); it will be renamed to config.text.ciscoyoke
|
|
135
|
+
|
|
136
|
+
Access recovered, with the previous configuration left aside as
|
|
137
|
+
flash:config.text.ciscoyoke and not loaded. The device is unconfigured and
|
|
138
|
+
at a privileged prompt.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Then a clean baseline**, with everything it deletes read into an archive first:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
$ ciscoyoke reset COM4 --confirm
|
|
145
|
+
|
|
146
|
+
✓ file_backup.cfg ✓ file_config.old ✓ file_config.text.ciscoyoke
|
|
147
|
+
|
|
148
|
+
! delete flash:vlan.dat (destroys the VLAN database)
|
|
149
|
+
! delete flash:backup.cfg (a previous owner's configuration)
|
|
150
|
+
! delete flash:config.old (a previous owner's configuration)
|
|
151
|
+
! delete flash:config.text.ciscoyoke (a previous owner's configuration)
|
|
152
|
+
dir flash: (confirm every deletion)
|
|
153
|
+
|
|
154
|
+
Reset complete.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
A fourth, **image rescue** for a device with no bootable IOS (XMODEM transfer,
|
|
158
|
+
then proof that IOS actually boots), is implemented but has not met hardware yet.
|
|
159
|
+
|
|
160
|
+
## Where it fits
|
|
161
|
+
|
|
162
|
+
ciscoyoke doesn't replace network automation. It gets equipment *into* it.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
dead / unknown / locked
|
|
166
|
+
│
|
|
167
|
+
▼
|
|
168
|
+
ciscoyoke serial console, no IP required
|
|
169
|
+
│
|
|
170
|
+
▼
|
|
171
|
+
known device with an IP
|
|
172
|
+
│
|
|
173
|
+
├── Netmiko
|
|
174
|
+
├── Nornir
|
|
175
|
+
├── Ansible
|
|
176
|
+
└── scrapli SSH / telnet, IP required
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Every mainstream tool assumes the device already has an address, a reachable
|
|
180
|
+
management interface and credentials that work. A second-hand box has none of
|
|
181
|
+
those, and everything between "arrived from eBay" and "automation can reach it"
|
|
182
|
+
is usually done by hand with a terminal emulator and a Cisco tech note from 2007.
|
|
183
|
+
|
|
184
|
+
## Hardware: tested and wanted
|
|
185
|
+
|
|
186
|
+
Support is claimed only when it's earned, and every mark says how:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
● Hardware verified run against a real device; the recording is committed
|
|
190
|
+
○ Documentation-derived implemented from Cisco's published procedure, never run
|
|
191
|
+
— Not yet attempted
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| Device | Identify | Password recovery | Reset | We need |
|
|
195
|
+
| --- | --- | --- | --- | --- |
|
|
196
|
+
| Catalyst 2950 | ● | ● | — ¹ | more variants |
|
|
197
|
+
| Catalyst 2960 | ○ | ○ | ○ | **a tester** |
|
|
198
|
+
| Catalyst 2960-S / X / Plus | ○ | ○ | ○ | **a tester** (USB console too) |
|
|
199
|
+
| Catalyst 3550 / 3560 / 3750 | — | — | — | **a tester**, or a profile from Cisco's docs ² |
|
|
200
|
+
| Cisco 1700 / 1800 / 1841 routers | — | — | — | **a tester** |
|
|
201
|
+
|
|
202
|
+
¹ Reset has run end to end on the 2950, but its only recording held a previous
|
|
203
|
+
owner's configuration, so it isn't published and the mark isn't claimed.
|
|
204
|
+
² No model-specific profile yet: these get Cisco's general procedure with
|
|
205
|
+
longer waits, and destructive commands ask for `--accept-unverified`.
|
|
206
|
+
Per-mark evidence: [docs/HARDWARE-TESTING.md](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/HARDWARE-TESTING.md).
|
|
207
|
+
|
|
208
|
+
**Got one of these?** Run `ciscoyoke rescue COM4`. If anything goes wrong:
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
ciscoyoke report # one zip: the run scrubbed, configuration output removed,
|
|
212
|
+
# what it expected, what it saw, and your adapter
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
and attach it to a [hardware report](https://github.com/Ryan-Clinton/ciscoyoke/issues/new?template=hardware-report.yml).
|
|
216
|
+
That's the most useful contribution there is, and it needs no Python.
|
|
217
|
+
|
|
218
|
+
## Real sessions become tests
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
real Cisco hardware
|
|
222
|
+
│
|
|
223
|
+
▼
|
|
224
|
+
serial transcript recorded by every destructive run
|
|
225
|
+
│
|
|
226
|
+
▼
|
|
227
|
+
scrub secrets passwords, keys, addresses, serials; config output removed
|
|
228
|
+
│
|
|
229
|
+
▼
|
|
230
|
+
fixture committed tests/fixtures/hw-*.ytx.pub
|
|
231
|
+
│
|
|
232
|
+
▼
|
|
233
|
+
CI replays it forever on Windows, macOS and Linux, with nothing plugged in
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
309 tests passed before the first real switch was connected. It still found
|
|
237
|
+
defects none of them could — LF-CR line endings, a rename that failed silently,
|
|
238
|
+
a log message hiding an IOS question — and each now has a test built from what
|
|
239
|
+
the real switch sent, most of them replaying its recording directly.
|
|
240
|
+
|
|
241
|
+
## Designed not to brick your switch
|
|
242
|
+
|
|
243
|
+
- Destructive commands are **dry runs** until you add `--confirm`.
|
|
244
|
+
- Configuration is **read into an archive before anything deletes it**, and the
|
|
245
|
+
archive says plainly what it couldn't read.
|
|
246
|
+
- Every change is **journalled before it's sent**, so an interrupted run can be
|
|
247
|
+
reconciled against the device (`ciscoyoke resolve`).
|
|
248
|
+
- Renames and deletions are **proven from a fresh flash listing**, not assumed
|
|
249
|
+
from the prompt coming back.
|
|
250
|
+
- An image rescue **isn't a success until IOS boots** the image you supplied.
|
|
251
|
+
- Recordings stay **private until scrubbed**; CI refuses a raw one.
|
|
252
|
+
|
|
253
|
+
More: [docs/SAFETY.md](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/SAFETY.md) · [threat model](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/THREAT-MODEL.md) ·
|
|
254
|
+
[architecture](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/ARCHITECTURE.md) · [specification](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/SPEC.md)
|
|
255
|
+
|
|
256
|
+
## Commands
|
|
257
|
+
|
|
258
|
+
| Look, change nothing | |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| `ciscoyoke rescue PORT` | **start here**: identify, preserve, recommend |
|
|
261
|
+
| `ciscoyoke scan` | every serial port: state, model, and what each needs next |
|
|
262
|
+
| `ciscoyoke doctor` | cable, adapter, permissions and driver checks |
|
|
263
|
+
| `ciscoyoke intake PORT` / `health PORT` / `archive PORT` | identify / check / preserve one device |
|
|
264
|
+
| `ciscoyoke capture PORT -o F` / `sweep PORT` | record a boot / find the line speed |
|
|
265
|
+
| `ciscoyoke report` | package the last run for a bug report |
|
|
266
|
+
|
|
267
|
+
| Change the device (dry run unless `--confirm`) | |
|
|
268
|
+
| --- | --- |
|
|
269
|
+
| `ciscoyoke recover access PORT` | password recovery, guided through the Mode button or break |
|
|
270
|
+
| `ciscoyoke reset PORT` | erase to a clean lab baseline |
|
|
271
|
+
| `ciscoyoke recover image PORT --image F` | XMODEM rescue for a device with no bootable IOS |
|
|
272
|
+
| `ciscoyoke lab apply LABFILE` | push per-device lab configuration |
|
|
273
|
+
|
|
274
|
+
Every option is in [docs/SAFETY.md](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/docs/SAFETY.md) and `ciscoyoke <command> --help`.
|
|
275
|
+
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
You don't need to write Python. Testing on hardware you own, sending a
|
|
279
|
+
`ciscoyoke report`, or adding a platform profile from Cisco's documentation are
|
|
280
|
+
all real contributions. See [CONTRIBUTING.md](https://github.com/Ryan-Clinton/ciscoyoke/blob/main/CONTRIBUTING.md).
|
|
281
|
+
|
|
282
|
+
## Firmware
|
|
283
|
+
|
|
284
|
+
ciscoyoke **never hosts, mirrors, searches for or redistributes Cisco IOS
|
|
285
|
+
images**, and no feature accepts a URL to fetch one from. It accepts an image
|
|
286
|
+
you supply and automates transport and verification. Lawful entitlement to any
|
|
287
|
+
image is your responsibility.
|
|
288
|
+
|
|
289
|
+
## Licence
|
|
290
|
+
|
|
291
|
+
MIT.
|