mirror of
https://github.com/CiscoDevNet/cml-community.git
synced 2025-10-23 07:42:03 +00:00
added brk2tmuxp.py script (#20)
This script provides a way to turn the CML breakout labs config into tmux configuration for easy console access.
This commit is contained in:
14
scripts/brk2tmuxp/Pipfile
Normal file
14
scripts/brk2tmuxp/Pipfile
Normal file
@@ -0,0 +1,14 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
pyyaml = "*"
|
||||
|
||||
[dev-packages]
|
||||
mypy = "*"
|
||||
types-pyyaml = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
121
scripts/brk2tmuxp/Pipfile.lock
generated
Normal file
121
scripts/brk2tmuxp/Pipfile.lock
generated
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "2924ea98850621a19a9fdc0e93c8dec4fdf2af78648276c0a2f0ae8cb8994f97"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.9"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
|
||||
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
|
||||
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
|
||||
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
|
||||
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
|
||||
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:0112752a6ff07230f9ec2f71b0d3d4e088a910fdce454fdb6553e83ed0eced7d",
|
||||
"sha256:0384d9f3af49837baa92f559d3fa673e6d2652a16550a9ee07fc08c736f5e6f8",
|
||||
"sha256:1b333cfbca1762ff15808a0ef4f71b5d3eed8528b23ea1c3fb50543c867d68de",
|
||||
"sha256:1fdeb0a0f64f2a874a4c1f5271f06e40e1e9779bf55f9567f149466fc7a55038",
|
||||
"sha256:4c653e4846f287051599ed8f4b3c044b80e540e88feec76b11044ddc5612ffed",
|
||||
"sha256:563514c7dc504698fb66bb1cf897657a173a496406f1866afae73ab5b3cdb334",
|
||||
"sha256:5b231afd6a6e951381b9ef09a1223b1feabe13625388db48a8690f8daa9b71ff",
|
||||
"sha256:5ce6a09042b6da16d773d2110e44f169683d8cc8687e79ec6d1181a72cb028d2",
|
||||
"sha256:5e7647df0f8fc947388e6251d728189cfadb3b1e558407f93254e35abc026e22",
|
||||
"sha256:6003de687c13196e8a1243a5e4bcce617d79b88f83ee6625437e335d89dfebe2",
|
||||
"sha256:61504b9a5ae166ba5ecfed9e93357fd51aa693d3d434b582a925338a2ff57fd2",
|
||||
"sha256:77423570c04aca807508a492037abbd72b12a1fb25a385847d191cd50b2c9605",
|
||||
"sha256:a4d9898f46446bfb6405383b57b96737dcfd0a7f25b748e78ef3e8c576bba3cb",
|
||||
"sha256:a952b8bc0ae278fc6316e6384f67bb9a396eb30aced6ad034d3a76120ebcc519",
|
||||
"sha256:b5b5bd0ffb11b4aba2bb6d31b8643902c48f990cc92fda4e21afac658044f0c0",
|
||||
"sha256:ca75ecf2783395ca3016a5e455cb322ba26b6d33b4b413fcdedfc632e67941dc",
|
||||
"sha256:cf9c261958a769a3bd38c3e133801ebcd284ffb734ea12d01457cb09eacf7d7b",
|
||||
"sha256:dd4d670eee9610bf61c25c940e9ade2d0ed05eb44227275cce88701fee014b1f",
|
||||
"sha256:e19736af56947addedce4674c0971e5dceef1b5ec7d667fe86bcd2b07f8f9075",
|
||||
"sha256:eaea21d150fb26d7b4856766e7addcf929119dd19fc832b22e71d942835201ef",
|
||||
"sha256:eaff8156016487c1af5ffa5304c3e3fd183edcb412f3e9c72db349faf3f6e0eb",
|
||||
"sha256:ee0a36edd332ed2c5208565ae6e3a7afc0eabb53f5327e281f2ef03a6bc7687a",
|
||||
"sha256:ef7beb2a3582eb7a9f37beaf38a28acfd801988cde688760aea9e6cc4832b10b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.950"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"types-pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:59480cf44595d836aaae050f35e3c39f197f3a833679ef3978d97aa9f2fb7def",
|
||||
"sha256:7b273a34f32af9910cf9405728c9d2ad3afc4be63e4048091a1a73d76681fe67"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.0.7"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708",
|
||||
"sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.2.0"
|
||||
}
|
||||
}
|
||||
}
|
151
scripts/brk2tmuxp/README.md
Normal file
151
scripts/brk2tmuxp/README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
## CML breakout to tmuxp
|
||||
|
||||
The script reads the CML breakout `labs.yaml` and generates [tmuxp](https://github.com/tmux-python/tmuxp) sessions.
|
||||
Both YAML and JSON format are supported (default to YAML).
|
||||
|
||||
The sessions can then be loaded with `tmuxp load [labs]` and will perform the following:
|
||||
|
||||
The `windows` variant (the default) will:
|
||||
|
||||
1. create a tmux session named after the lab title
|
||||
2. the first window created will run `breakout run` for that particular lab
|
||||
3. for each node, open a node-named new window and telnet to the node
|
||||
|
||||
the `panes` variant will:
|
||||
|
||||
1. create a tmux session named after the lab title
|
||||
2. the first window created will run `breakout run` for that particular lab
|
||||
3. the second window will open a pane per node
|
||||
|
||||
<p align="right">(<a href="#top">back to top</a>)</p>
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Use your favorite package manager to install `tmux` and `tmuxp`, for example:
|
||||
|
||||
- tmux
|
||||
|
||||
```sh
|
||||
brew install tmux
|
||||
```
|
||||
|
||||
```sh
|
||||
apt install tmux
|
||||
```
|
||||
|
||||
- tmuxp
|
||||
|
||||
```sh
|
||||
brew install tmuxp
|
||||
```
|
||||
|
||||
```sh
|
||||
apt install tmuxp
|
||||
```
|
||||
|
||||
### Installation
|
||||
|
||||
Clone the repository, cd in the directory and install the requirements:
|
||||
|
||||
- pipenv
|
||||
|
||||
```sh
|
||||
pipenv install
|
||||
```
|
||||
|
||||
- pip
|
||||
|
||||
```sh
|
||||
python3 -m venv /path/to/directory
|
||||
pip install -r requirements
|
||||
```
|
||||
|
||||
Note: the only dependency is `pyyaml`.
|
||||
|
||||
## Usage
|
||||
|
||||
The following options are available:
|
||||
|
||||
```sh
|
||||
❯ python brk2tmuxp.py -h
|
||||
usage: brk2tmuxp.py [-h] [-d BRK_DIR] [-f YAML_FILE] [-p] [-l LISTEN_ADDR] [-s SLEEP] [-j]
|
||||
|
||||
reads a CML breakout labs.yaml and generates a tmuxp session files
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-d BRK_DIR, --brk-dir BRK_DIR
|
||||
path to dir containing the breakout labs YAML files (default:: current directory)
|
||||
-f YAML_FILE, --yaml-file YAML_FILE
|
||||
name of the 'labs' YAML file, (default to labs.yaml)
|
||||
-p, --panes if set, all telnet sessions will be in one window (default: each telnet session has its own window)
|
||||
-l LISTEN_ADDR, --listen_addr LISTEN_ADDR
|
||||
specify the listen address (default: ::1)
|
||||
-s SLEEP, --sleep SLEEP
|
||||
sleep time (in seconds) before initiating telnet session (default: 3)
|
||||
-j, --format-json output JSON tmuxp session files (default: YAML)
|
||||
```
|
||||
|
||||
### Use `breakout init` to fetch the labs and nodes from the controller:
|
||||
|
||||
```sh
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ breakout init
|
||||
get simplified node definitions from controller...
|
||||
get active console keys from controller...
|
||||
get active VNC keys from controller...
|
||||
get all the labs from controller...
|
||||
get all the nodes for the labs from controller...
|
||||
get nodes for lab L2L IKEv2 from controller...
|
||||
get nodes for lab cisco_isis_sr_101_v1 from controller...
|
||||
config written.
|
||||
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ ls -l
|
||||
.rwxrwxrwx 746 sgherdao 11 Mar 21:04 config.yaml
|
||||
.rwxrwxrwx 1.3k sgherdao 3 May 21:50 labs.yaml
|
||||
```
|
||||
|
||||
### Generate the "windows" variant and load it with `tmuxp`:
|
||||
|
||||
```sh
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ python brk2tmuxp.py
|
||||
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ ls -l
|
||||
.rw-r--r-- 513 sgherdao 3 May 21:55 1eaf2c3b-9207-4524-9e7c-3eeaada67886.yaml
|
||||
.rwxrwxrwx 746 sgherdao 11 Mar 21:04 config.yaml
|
||||
.rw-r--r-- 883 sgherdao 3 May 22:04 d231681f-a88c-4004-884a-4c9639ad8b07.yaml
|
||||
.rwxrwxrwx 4.3k sgherdao 3 May 22:01 labs.yaml
|
||||
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp) took 3s
|
||||
❯ tmuxp load d231681f-a88c-4004-884a-4c9639ad8b07.yaml
|
||||
[Loading] CML/d231681f-a88c-4004-884a-4c9639ad8b07.yaml
|
||||
Already inside TMUX, switch to session? yes/no
|
||||
Or (a)ppend windows in the current active session?
|
||||
[y/n/a]: y
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Generate the "panes" variant and load it with `tmuxp`:
|
||||
|
||||
```sh
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ python PythonTemp/brk2tmuxp/brk2tmuxp.py -p
|
||||
|
||||
~/CML via 🐍 v3.9.12 (brk2tmuxp)
|
||||
❯ tmuxp load 1eaf2c3b-9207-4524-9e7c-3eeaada67886.yaml
|
||||
[Loading] CML/1eaf2c3b-9207-4524-9e7c-3eeaada67886.yaml
|
||||
Already inside TMUX, switch to session? yes/no
|
||||
Or (a)ppend windows in the current active session?
|
||||
[y/n/a]: y
|
||||
❯
|
||||
```
|
||||
|
||||

|
||||
|
||||
<p align="right">(<a href="#top">back to top</a>)</p>
|
200
scripts/brk2tmuxp/brk2tmuxp.py
Normal file
200
scripts/brk2tmuxp/brk2tmuxp.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""
|
||||
reads a CML breakout labs.yaml file and returns a list of dict ready to be
|
||||
translated into multiple JSON or YAML tmuxp session files.
|
||||
|
||||
By default, each telnet session will have its own window but if the `-p` or
|
||||
`--panes` flag is set, then each telnet session will be in a pane in one
|
||||
(and only one) window.
|
||||
|
||||
VNC sessions are ignored.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="reads a CML breakout labs.yaml and generates a tmuxp session files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--brk-dir",
|
||||
type=str,
|
||||
default=".",
|
||||
help="path to dir containing the breakout labs YAML files (default:: current directory)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--yaml-file",
|
||||
type=str,
|
||||
default="labs.yaml",
|
||||
help="name of the 'labs' YAML file, (default to labs.yaml)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--panes",
|
||||
help="if set, all telnet sessions will be in one window (default: each telnet session has its own window)",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--listen_addr",
|
||||
default="::1",
|
||||
help="specify the listen address (default: ::1)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sleep",
|
||||
type=float,
|
||||
default="3",
|
||||
help="sleep time (in seconds) before initiating telnet session (default: 3)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--format-json",
|
||||
action="store_true",
|
||||
help="output JSON tmuxp session files (default: YAML)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# load labs yaml file
|
||||
brk_dir = args.brk_dir
|
||||
yaml_file = args.yaml_file
|
||||
|
||||
with open(f"{brk_dir}/{yaml_file}") as f:
|
||||
labs = yaml.safe_load(f)
|
||||
|
||||
panes = args.panes
|
||||
listen_addr = args.listen_addr
|
||||
sleep = args.sleep
|
||||
format_json = args.format_json
|
||||
tmux_sessions = {}
|
||||
|
||||
for uuid, lab in labs.items():
|
||||
if panes:
|
||||
tmux_sessions[uuid] = panes_configs(
|
||||
lab=lab, brk_dir=brk_dir, listen_addr=listen_addr, sleep=sleep
|
||||
)
|
||||
|
||||
else:
|
||||
tmux_sessions[uuid] = windows_configs(
|
||||
lab=lab, brk_dir=brk_dir, listen_addr=listen_addr, sleep=sleep
|
||||
)
|
||||
|
||||
for uuid, tmux_session in tmux_sessions.items():
|
||||
if format_json:
|
||||
with open(f"{uuid}.json", "w") as f:
|
||||
json.dump(tmux_session, f, indent=2)
|
||||
else:
|
||||
with open(f"{uuid}.yaml", "w") as f:
|
||||
yaml.dump(tmux_session, f)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def panes_configs(lab: dict, brk_dir: str, listen_addr: str, sleep: float) -> dict:
|
||||
"""
|
||||
returns a dict representing a tmuxp session file for one CML lab
|
||||
|
||||
This is the 'panes' variant, the tmuxp session will consist of:
|
||||
- one session named based on the lab title containing:
|
||||
- the first window will cd into the CML dir and launch `breakout run`
|
||||
for that lab
|
||||
- subsequent windows, will telnet to one lab node
|
||||
"""
|
||||
lab_title = lab["lab_title"]
|
||||
conf = {
|
||||
"session_name": lab_title,
|
||||
"windows": [
|
||||
{
|
||||
"window_name": "breakout",
|
||||
"panes": [
|
||||
{
|
||||
"shell_command": [
|
||||
f"cd {brk_dir}",
|
||||
f"breakout run '{lab_title}'",
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
{"window_name": "nodes", "layout": "tiled"},
|
||||
],
|
||||
}
|
||||
panes: list = []
|
||||
for _, node in lab["nodes"].items():
|
||||
node_label = node["label"]
|
||||
|
||||
panes.extend(
|
||||
{
|
||||
"shell_command":
|
||||
# dirty way to set tmux pane title
|
||||
# see https://github.com/tmux-python/tmuxp/issues/384
|
||||
[
|
||||
f"printf '\\033]2;%s\\033\\\\' '{node_label}/{n['name'][-1]}'",
|
||||
f"time sleep {sleep}",
|
||||
f"telnet {listen_addr} {n['listen_port']}",
|
||||
]
|
||||
}
|
||||
for n in node["devices"]
|
||||
if n["enabled"] and n["name"] != "vnc"
|
||||
)
|
||||
|
||||
conf["windows"][1]["panes"] = panes
|
||||
return conf
|
||||
|
||||
|
||||
def windows_configs(lab: dict, brk_dir: str, listen_addr: str, sleep: int) -> dict:
|
||||
"""
|
||||
returns a dict representing a tmuxp session file for one CML lab
|
||||
|
||||
This is the 'windows' variant, the tmuxp session will consist of:
|
||||
- one session named based on the lab title containing:
|
||||
- the first window will launch `breakout run` for that lab
|
||||
- next windows, will telnet to one lab node
|
||||
"""
|
||||
lab_title = lab["lab_title"]
|
||||
conf = {
|
||||
"session_name": lab_title,
|
||||
"windows": [
|
||||
{
|
||||
"window_name": "breakout",
|
||||
"panes": [
|
||||
{
|
||||
"shell_command": [
|
||||
f"cd {brk_dir}",
|
||||
f"breakout run '{lab_title}'",
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
for _, node in lab["nodes"].items():
|
||||
node_label = node["label"]
|
||||
|
||||
conf["windows"].extend(
|
||||
{
|
||||
"window_name": f"{node_label}/{n['name'][-1]}",
|
||||
"panes": [
|
||||
{
|
||||
"shell_command": [
|
||||
f"time sleep {sleep}",
|
||||
f"telnet {listen_addr} {n['listen_port']}",
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
for n in node["devices"]
|
||||
if n["enabled"] and n["name"] != "vnc"
|
||||
)
|
||||
|
||||
return conf
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
BIN
scripts/brk2tmuxp/panes-01.jpg
Normal file
BIN
scripts/brk2tmuxp/panes-01.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 KiB |
1
scripts/brk2tmuxp/requirements.txt
Normal file
1
scripts/brk2tmuxp/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
pyyaml
|
BIN
scripts/brk2tmuxp/windows-01.jpg
Normal file
BIN
scripts/brk2tmuxp/windows-01.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 652 KiB |
Reference in New Issue
Block a user