Merge: initial scaffolding + field guide + role skeleton (Tasks 1-3)
No-device tasks complete and two-stage reviewed: - repo scaffolding (direnv, ansible.cfg, lint, requirements) - makerfloss vault identity, inventory, connection group_vars - role skeleton makerfloss.mikrotik_switch (stubbed domains), host_vars, play_switch.yml - on-site makerspace field guide; plan carry-over notes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
bdfde1644c
22 changed files with 402 additions and 0 deletions
8
.ansible-lint
Normal file
8
.ansible-lint
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
profile: production
|
||||
skip_list:
|
||||
- line-length
|
||||
- no-changed-when
|
||||
exclude_paths:
|
||||
- .venv/
|
||||
- backups/
|
||||
9
.envrc
Normal file
9
.envrc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Create .venv automatically if it doesn't exist
|
||||
if [ ! -d .venv ]; then
|
||||
python3 -m venv .venv
|
||||
.venv/bin/python -m pip install -U pip setuptools wheel
|
||||
fi
|
||||
|
||||
# Activate the environment manually (avoids Python 3.13 deprecation warning)
|
||||
export VIRTUAL_ENV=$PWD/.venv
|
||||
PATH_add .venv/bin
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.venv/
|
||||
*.retry
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.DS_Store
|
||||
11
.yamllint
Normal file
11
.yamllint
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
extends: default
|
||||
rules:
|
||||
line-length: disable
|
||||
comments:
|
||||
min-spaces-from-content: 1
|
||||
truthy:
|
||||
allowed-values: ["true", "false", "yes", "no"]
|
||||
ignore: |
|
||||
.venv/
|
||||
backups/
|
||||
19
ansible.cfg
Normal file
19
ansible.cfg
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[defaults]
|
||||
inventory = inventories/prod/hosts.yml
|
||||
roles_path = roles:~/.ansible/roles
|
||||
collections_path = ~/.ansible/collections
|
||||
host_key_checking = False
|
||||
retry_files_enabled = False
|
||||
interpreter_python = auto_silent
|
||||
nocows = 1
|
||||
timeout = 30
|
||||
stdout_callback = yaml
|
||||
bin_ansible_callbacks = True
|
||||
vault_identity_list = makerfloss@~/.ansible/vault-keys/makerfloss.txt
|
||||
|
||||
[persistent_connection]
|
||||
command_timeout = 60
|
||||
connect_timeout = 60
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
165
docs/makerspace-switch-fieldguide.md
Normal file
165
docs/makerspace-switch-fieldguide.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# Makerspace Field Guide — Preparing the CRS310 Switch
|
||||
|
||||
**Print this and bring it.** This is the exact, on-site procedure to get the MikroTik
|
||||
**CRS310-8G+2S+IN** ready so Ansible can take over. Total time: ~30–45 min (most of it
|
||||
the firmware upgrade). Work on a **bench/isolated network** — do **not** plug the switch
|
||||
into the live makerspace network until VLANs are configured later (avoids loops and
|
||||
DHCP/IP conflicts).
|
||||
|
||||
When you're done, you'll have: the switch on a known firmware, wiped to a clean slate,
|
||||
reachable over SSH at a temporary IP, and a few facts written down for me to drop into
|
||||
`host_vars`.
|
||||
|
||||
---
|
||||
|
||||
## Bring with you
|
||||
|
||||
- [ ] The CRS310 + its PSU.
|
||||
- [ ] A laptop with **WinBox** (download from mikrotik.com/download) — or just a browser for WebFig.
|
||||
- [ ] One Ethernet cable (laptop ↔ a 2.5G port).
|
||||
- [ ] Internet for the switch during the upgrade (a cable from an existing LAN/uplink, **temporarily**, with DHCP — unplug it again before the final steps).
|
||||
- [ ] The **SFP+ module or DAC** for the 10G uplink (to fit physically; we don't cable the real uplink yet).
|
||||
- [ ] This guide + something to write the recorded facts on (or a phone note).
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Power on and get in
|
||||
|
||||
1. Power the switch. Wait ~1 min for it to boot RouterOS.
|
||||
2. Connect your laptop to **ether1** (a 2.5G port).
|
||||
3. Open **WinBox → Neighbors tab**. The switch appears (by IP `192.168.88.1` and/or by MAC).
|
||||
- **Tip:** click the **MAC address** (not the IP) to connect — this works even when the
|
||||
switch has no IP, which matters in Step 5.
|
||||
4. Log in: user `admin`, password **blank** (just press Enter). RouterOS 7 may ask you to
|
||||
set a password — you can set a temporary one or skip; Ansible will set the real one later.
|
||||
|
||||
> No WinBox? Browse to `http://192.168.88.1` (WebFig) instead. The CLI commands below are
|
||||
> typed in **WinBox/WebFig → New Terminal**.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Confirm it's running RouterOS (not SwOS)
|
||||
|
||||
The CRS310 can dual-boot SwOS, but we need **RouterOS** for VLAN filtering + Ansible.
|
||||
|
||||
- In terminal: `/system/routerboard/print`
|
||||
- It should report RouterOS. If the device booted **SwOS** (different, simpler web UI),
|
||||
switch the boot OS: in SwOS go to the **System** page and set boot to RouterOS, or use
|
||||
the reset/boot-OS toggle, then reboot. (You want the full RouterOS interface.)
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Upgrade and pin the firmware
|
||||
|
||||
This needs internet for the switch. Plug a DHCP uplink into **ether8** temporarily.
|
||||
|
||||
1. Give the switch internet briefly: it should pull a DHCP lease on the uplink port, or in
|
||||
terminal: `/ip/dhcp-client/add interface=ether8 disabled=no`
|
||||
2. Update RouterOS:
|
||||
```
|
||||
/system/package/update/set channel=stable
|
||||
/system/package/update/check-for-updates
|
||||
/system/package/update/download
|
||||
/system/reboot
|
||||
```
|
||||
(Or WinBox: **System → Packages → Check For Updates → Download & Install**.)
|
||||
3. After reboot, upgrade the bootloader (RouterBOOT) to match:
|
||||
```
|
||||
/system/routerboard/upgrade
|
||||
/system/reboot
|
||||
```
|
||||
4. **Write down the final version:** `/system/resource/print` → the `version` line.
|
||||
➜ **Record as `RouterOS version: ______`** (this becomes `switch_firmware_target`).
|
||||
5. **Unplug the temporary internet uplink** and remove the DHCP client:
|
||||
`/ip/dhcp-client/remove [find]`
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Record the device facts
|
||||
|
||||
Run `/system/routerboard/print` and `/system/resource/print` and write down:
|
||||
|
||||
- [ ] **Model:** ____________________ (should be CRS310-8G+2S+IN)
|
||||
- [ ] **Serial:** ____________________ (also on the sticker underneath)
|
||||
- [ ] **Base MAC:** ____________________
|
||||
- [ ] **RouterOS version:** ____________________ (from Step 3.4)
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Wipe to a clean slate (no default config)
|
||||
|
||||
This makes Ansible the single owner of the whole configuration.
|
||||
|
||||
1. In terminal:
|
||||
```
|
||||
/system/reset-configuration no-defaults=yes skip-backup=yes
|
||||
```
|
||||
(Or WinBox: **System → Reset Configuration** → tick **No Default Configuration** and
|
||||
**Do Not Backup** → **Reset**.)
|
||||
2. The switch reboots. It now has **no IP and no services** — WinBox-by-IP won't find it.
|
||||
3. Reconnect using **WinBox → Neighbors → click the MAC address** (this is why we use MAC).
|
||||
Log in as `admin` with a **blank** password.
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Give it a temporary IP + enable SSH (so Ansible can reach it)
|
||||
|
||||
In the terminal (laptop still on **ether1**):
|
||||
|
||||
```
|
||||
/ip/address/add address=192.168.88.1/24 interface=ether1
|
||||
/ip/service/enable ssh
|
||||
/ip/service/print
|
||||
```
|
||||
|
||||
Then on your laptop, set a static IP `192.168.88.2` / `255.255.255.0` and confirm SSH:
|
||||
|
||||
```
|
||||
ssh admin@192.168.88.1
|
||||
```
|
||||
|
||||
If that logs in, **you're done** — leave the switch powered and on the bench.
|
||||
|
||||
> ⚠️ Keep a **WinBox MAC session** open as your lifeline whenever you change network
|
||||
> settings. If you ever lock yourself out, MAC-telnet/WinBox-by-MAC still works; a full
|
||||
> **Netinstall** (mikrotik.com/download) is the last-resort recovery.
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — Decide the real addressing (write it down for me)
|
||||
|
||||
I need these to fill in `host_vars/crs310-maker.yml`. Decide with whatever the makerspace
|
||||
network plan is (or we can finalize together):
|
||||
|
||||
- [ ] **Management IP + mask** (real, not the temp one): ____________________
|
||||
- [ ] **Management VLAN ID:** ____________________
|
||||
- [ ] **Default gateway:** ____________________
|
||||
- [ ] **Upstream uplink port** (which SFP+ / port goes to the OPNsense/router): ____________________
|
||||
- [ ] **DNS / NTP server IP** (usually the gateway): ____________________
|
||||
|
||||
(If the makerspace VLAN plan isn't settled yet, that's fine — we ship a placeholder and
|
||||
fill these in later. The switch just needs to be reachable per Step 6.)
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — Physical finish
|
||||
|
||||
- [ ] Fit the **SFP+ module/DAC** into `sfp-sfpplus1` (don't cable the live uplink yet).
|
||||
- [ ] Mount/label the switch.
|
||||
|
||||
---
|
||||
|
||||
## When you're back
|
||||
|
||||
Bring me:
|
||||
1. The recorded facts (Step 4) and addressing decisions (Step 7).
|
||||
2. Confirmation that `ssh admin@192.168.88.1` (or your temp IP) works.
|
||||
|
||||
Then I'll: create the empty `MakerFLOSS_Mikrotik` repo on `forgejo.makerfloss.eu`, drop
|
||||
your facts into `host_vars`, and run **`play_bootstrap.yml`** — which creates your named
|
||||
admin user, imports your SSH key, and hands the switch over to Ansible. After that,
|
||||
`play_switch.yml` configures identity, services, VLANs, and backups.
|
||||
|
||||
> **Do not connect the switch to the live makerspace network** until VLANs are configured
|
||||
> (Task 7 in the implementation plan) — an unconfigured switch on the live net can cause
|
||||
> loops or hand out the wrong VLAN.
|
||||
|
|
@ -922,3 +922,25 @@ Expected: repo populated on `forgejo.makerfloss.eu`.
|
|||
- **RouterOS version drift:** exact CLI syntax (NTP `servers=` property, `ssh-keys/import` path) is RouterOS-7 specific; verify against the pinned version from Phase 0.3 and adjust.
|
||||
- **`net_put`/`net_get` over `network_cli`:** depends on SCP being available on the RouterOS SSH service; if it fails, fall back to importing the key by pasting its contents via `/user/ssh-keys/...` or enabling SCP.
|
||||
- **`changed_when: false`** is used widely because the `command` module can't detect RouterOS state changes; idempotency comes from the `:if [find]` guards. Revisit if you want accurate change reporting (parse command output).
|
||||
|
||||
## Carry-over notes from the skeleton code review (Tasks 1–3, done 2026-06-07)
|
||||
|
||||
The no-device tasks (1–3) are implemented, reviewed, and committed on branch
|
||||
`feat/initial-scaffolding`. The code-quality review of the role skeleton raised these
|
||||
points to handle WHEN the device task files (Tasks 5–9) are written:
|
||||
|
||||
- **`switch_ssh_port` (default 22):** the identity task will *set* the SSH port. If the
|
||||
device was manually moved to a non-standard port before Ansible manages it, the first
|
||||
run resets it to 22 and the connection drops. Confirm the live port matches before the
|
||||
identity task runs, or override `switch_ssh_port` in host_vars.
|
||||
- **`switch_bridge_name` / `switch_admin_group`:** these default to the CRS310 factory
|
||||
values (`bridge` / `full`) and are NOT overridden in host_vars. Correct for this one
|
||||
device; if the bridge/group name ever differs, the VLAN and users tasks silently target
|
||||
the wrong object. Add explicit host_vars overrides if a second device is ever onboarded.
|
||||
- **Trunk `pvid: 1` (sfp-sfpplus1):** untagged frames on the uplink land in VLAN 1. In a
|
||||
hardened VLAN design VLAN 1 is usually unused — when writing `vlans.yml`, decide
|
||||
deliberately whether the trunk should accept untagged traffic at all, and comment intent.
|
||||
- **host_vars `# EDIT:` placeholders:** `switch_mgmt_address/gateway/dns/ntp` in
|
||||
`host_vars/crs310-maker.yml` hold plausible `10.0.99.x` placeholders. Replace with the
|
||||
real values from the field guide (Step 7) and remove the `# EDIT` comments so it's
|
||||
unambiguous they were updated.
|
||||
|
|
|
|||
4
group_vars/all.yml
Normal file
4
group_vars/all.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
# Shared non-secret defaults across all hosts go here.
|
||||
# Secrets live in the vault (see host_vars / a vaulted file), not in this file.
|
||||
org_name: "MakerFLOSS"
|
||||
14
group_vars/mikrotik.yml
Normal file
14
group_vars/mikrotik.yml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
ansible_connection: ansible.netcommon.network_cli
|
||||
ansible_network_os: community.routeros.routeros
|
||||
# Bootstrap default. play_bootstrap.yml creates a named admin user and imports the
|
||||
# operator SSH key; thereafter override ansible_user to that named user (host_vars).
|
||||
ansible_user: admin
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
|
||||
# Domain enable-flags (day-2 play). Override per-host if needed.
|
||||
switch_identity_enabled: true
|
||||
switch_users_enabled: true
|
||||
switch_vlans_enabled: true
|
||||
switch_backup_enabled: true
|
||||
switch_firmware_enabled: false # opt-in; upgrades are disruptive
|
||||
23
host_vars/crs310-maker.yml
Normal file
23
host_vars/crs310-maker.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
# Identity facts recorded during Phase 0.6 (edit to match the device)
|
||||
switch_identity_name: "crs310-maker"
|
||||
switch_mgmt_vlan_id: 99
|
||||
switch_mgmt_address: "10.0.99.2/24" # EDIT: real mgmt IP
|
||||
switch_mgmt_gateway: "10.0.99.1" # EDIT: real gateway
|
||||
switch_dns_servers: "10.0.99.1"
|
||||
switch_ntp_servers: "10.0.99.1"
|
||||
|
||||
switch_admin_user: "sjat"
|
||||
|
||||
# Real VLAN/port topology (EDIT to the makerspace plan when known)
|
||||
switch_vlans:
|
||||
- {id: 99, name: "mgmt"}
|
||||
- {id: 10, name: "members"}
|
||||
switch_bridge_ports:
|
||||
- {interface: "ether1", pvid: 10, mode: access}
|
||||
- {interface: "ether2", pvid: 10, mode: access}
|
||||
- {interface: "sfp-sfpplus1", pvid: 1, mode: trunk, tagged_vlans: [99, 10]}
|
||||
|
||||
# Firmware (opt-in)
|
||||
# switch_firmware_enabled: true
|
||||
# switch_firmware_target: "7.x.y" # EDIT to the version pinned in Phase 0.3
|
||||
7
inventories/prod/hosts.yml
Normal file
7
inventories/prod/hosts.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
all:
|
||||
children:
|
||||
mikrotik:
|
||||
hosts:
|
||||
crs310-maker:
|
||||
ansible_host: 192.168.88.1 # temp mgmt IP until Task 4 sets the real one
|
||||
6
play_switch.yml
Normal file
6
play_switch.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
- name: Configure MikroTik switches (day-2, key auth)
|
||||
hosts: mikrotik
|
||||
gather_facts: false
|
||||
roles:
|
||||
- makerfloss.mikrotik_switch
|
||||
10
requirements.txt
Normal file
10
requirements.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Core Ansible
|
||||
ansible==10.3.0
|
||||
|
||||
# Linting & validation
|
||||
ansible-lint==24.7.0
|
||||
yamllint==1.35.1
|
||||
|
||||
# Network connection plugins / SCP for SSH key transfer to RouterOS
|
||||
paramiko>=3.4.0
|
||||
scp>=0.15.0
|
||||
6
requirements.yml
Normal file
6
requirements.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
collections:
|
||||
- name: community.routeros
|
||||
version: ">=3.0.0,<4.0.0"
|
||||
- name: ansible.netcommon
|
||||
version: ">=6.0.0,<8.0.0"
|
||||
38
roles/makerfloss.mikrotik_switch/defaults/main.yml
Normal file
38
roles/makerfloss.mikrotik_switch/defaults/main.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
# ----- Identity / management -----
|
||||
switch_identity_name: "{{ inventory_hostname }}"
|
||||
switch_mgmt_vlan_id: 99
|
||||
switch_mgmt_address: "192.168.88.1/24" # PLACEHOLDER — override in host_vars
|
||||
switch_mgmt_gateway: "192.168.88.254" # PLACEHOLDER — override in host_vars
|
||||
switch_dns_servers: "192.168.88.254"
|
||||
switch_ntp_servers: "192.168.88.254"
|
||||
|
||||
# Services to disable for hardening (winbox kept on by default for recovery)
|
||||
switch_disabled_services:
|
||||
- telnet
|
||||
- ftp
|
||||
- www
|
||||
- www-ssl
|
||||
- api
|
||||
- api-ssl
|
||||
switch_ssh_port: 22
|
||||
|
||||
# ----- Users -----
|
||||
switch_admin_user: "sjat"
|
||||
switch_admin_group: "full"
|
||||
switch_admin_ssh_pubkey_file: "~/.ssh/id_ed25519.pub"
|
||||
switch_disable_default_admin: true
|
||||
|
||||
# ----- VLAN / bridge / ports (PLACEHOLDER example) -----
|
||||
# Real topology is defined in host_vars/<switch>.yml.
|
||||
switch_bridge_name: "bridge"
|
||||
switch_vlans:
|
||||
- {id: 99, name: "mgmt"}
|
||||
- {id: 10, name: "members"}
|
||||
switch_bridge_ports:
|
||||
# ether1..ether8 = 2.5GbE access ports; sfp-sfpplus1/2 = 10G uplinks
|
||||
- {interface: "ether1", pvid: 10, mode: access}
|
||||
- {interface: "sfp-sfpplus1", pvid: 1, mode: trunk, tagged_vlans: [99, 10]}
|
||||
|
||||
# ----- Firmware -----
|
||||
switch_firmware_target: "" # set in host_vars when opting into upgrades
|
||||
10
roles/makerfloss.mikrotik_switch/meta/main.yml
Normal file
10
roles/makerfloss.mikrotik_switch/meta/main.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
galaxy_info:
|
||||
role_name: mikrotik_switch
|
||||
namespace: makerfloss
|
||||
author: sjat
|
||||
description: Configure a MikroTik RouterOS switch (CRS310) over SSH.
|
||||
license: MIT
|
||||
min_ansible_version: "2.17"
|
||||
platforms: []
|
||||
dependencies: []
|
||||
4
roles/makerfloss.mikrotik_switch/tasks/backup.yml
Normal file
4
roles/makerfloss.mikrotik_switch/tasks/backup.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Placeholder
|
||||
ansible.builtin.debug:
|
||||
msg: "not yet implemented"
|
||||
4
roles/makerfloss.mikrotik_switch/tasks/firmware.yml
Normal file
4
roles/makerfloss.mikrotik_switch/tasks/firmware.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Placeholder
|
||||
ansible.builtin.debug:
|
||||
msg: "not yet implemented"
|
||||
4
roles/makerfloss.mikrotik_switch/tasks/identity.yml
Normal file
4
roles/makerfloss.mikrotik_switch/tasks/identity.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Placeholder
|
||||
ansible.builtin.debug:
|
||||
msg: "not yet implemented"
|
||||
25
roles/makerfloss.mikrotik_switch/tasks/main.yml
Normal file
25
roles/makerfloss.mikrotik_switch/tasks/main.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
- name: Identity, management and services
|
||||
ansible.builtin.import_tasks: identity.yml
|
||||
when: switch_identity_enabled | bool
|
||||
tags: [identity]
|
||||
|
||||
- name: Users and SSH keys
|
||||
ansible.builtin.import_tasks: users.yml
|
||||
when: switch_users_enabled | bool
|
||||
tags: [users]
|
||||
|
||||
- name: VLANs, bridge and ports
|
||||
ansible.builtin.import_tasks: vlans.yml
|
||||
when: switch_vlans_enabled | bool
|
||||
tags: [vlans]
|
||||
|
||||
- name: Backup configuration
|
||||
ansible.builtin.import_tasks: backup.yml
|
||||
when: switch_backup_enabled | bool
|
||||
tags: [backup]
|
||||
|
||||
- name: Firmware upgrade
|
||||
ansible.builtin.import_tasks: firmware.yml
|
||||
when: switch_firmware_enabled | bool
|
||||
tags: [firmware]
|
||||
4
roles/makerfloss.mikrotik_switch/tasks/users.yml
Normal file
4
roles/makerfloss.mikrotik_switch/tasks/users.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Placeholder
|
||||
ansible.builtin.debug:
|
||||
msg: "not yet implemented"
|
||||
4
roles/makerfloss.mikrotik_switch/tasks/vlans.yml
Normal file
4
roles/makerfloss.mikrotik_switch/tasks/vlans.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
- name: Placeholder
|
||||
ansible.builtin.debug:
|
||||
msg: "not yet implemented"
|
||||
Loading…
Add table
Reference in a new issue