commit 99a66f4cfd632030b8182f95700cd71783f22eba Author: Dessa Simpson Date: Wed Oct 25 18:25:51 2023 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2101448 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.xz +*.dsk +*.tgz + +# generated from template +k8s/deployment.yaml +k8s/metallb.yaml diff --git a/README.md b/README.md new file mode 100644 index 0000000..51858fe --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# PDP-11 in Docker/Kubernetes + +## Docker +To build the container, run `docker/build.sh [tag]` with the tag you want to apply to the image. + +To run the container, use the following command adapted to your needs: + + docker run -d --rm --privileged --name pdp -v `pwd`/rq0.dsk:/mnt/rq0.dsk + +## Kubernetes +To run in Kubernetes, first you need to build the docker image and push it to a registry your cluster has access to. + +Once you've done that, edit k8s/deployment.yaml diff --git a/bootstrap-k8s.sh b/bootstrap-k8s.sh new file mode 100755 index 0000000..949647e --- /dev/null +++ b/bootstrap-k8s.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +usage () { + echo "usage: $0 tag disk_filename" + exit 1 +} + +[[ $# -eq 2 ]] || usage +tag="$1" +disk_filename="$2" + +cd "$(dirname $0)" +[[ -f "$disk_filename" && -r "$disk_filename" ]] || { echo "$disk_filename does not exist, is not a regular file, or is not readable"; exit 1; } +[[ "$disk_filename" == *.xz ]] || { echo "$disk_filename does not appear to be an xz-compressed disk image"; exit 1; } + +sed "s!!${tag}!" ./k8s/deployment.yaml.template > ./k8s/deployment.yaml + +echo "Creating PVC..." +kubectl apply -f ./k8s/pvc.yaml +echo "Creating temporary pod..." +kubectl apply -f ./k8s/pod-tmp.yaml +echo "Waiting for temporary pod..." +while ! kubectl get pod pdp-tmp|grep -q Running; do sleep 1; done +echo "Checking for disk image..." +if ! kubectl exec -it pdp-tmp -- test -f /mnt/rq0.dsk &>/dev/null; then + echo "Uploading compressed disk image..." + kubectl exec pdp-tmp -- rm -f /mnt/rq0.dsk.xz + kubectl cp "$disk_filename" pdp-tmp:/mnt/rq0.dsk.xz + echo "Extracting disk image..." + kubectl exec pdp-tmp -- apk add xz + kubectl exec pdp-tmp -- xz -d /mnt/rq0.dsk.xz +fi +echo "Removing temporary pod..." +kubectl delete -f ./k8s/pod-tmp.yaml +echo "Creating PDP deployment..." +kubectl apply -f ./k8s/deployment.yaml +echo "Creating services..." +for i in ./k8s/svc-*.yaml; do kubectl apply -f "$i"; done +echo "Done! Optionally, run ./k8s/configure-metallb.sh to put the services on an external IP." diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..7de09d6 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:11-slim AS builder +RUN apt-get -y update && apt-get -y upgrade && apt-get -y install gcc libpcap-dev libvdeplug-dev libpcre3-dev libedit-dev libsdl2-dev libpng-dev libsdl2-ttf-dev build-essential && apt-get clean +ADD simh-3.9-0.tgz / +WORKDIR /simh-3.9-0 +RUN make pdp11 + +FROM debian:11-slim +ENV DISK_FILENAME=rq0.dsk +RUN apt-get -y update && apt-get -y upgrade && apt-get -y install ed libpcap-dev iproute2 iptables gcc vdeplug libpcre3 net-tools telnet +COPY --from=builder /simh-3.9-0/BIN/pdp11 /usr/local/bin/pdp11 +COPY boot.ini.template / +COPY startup.sh / +CMD /startup.sh diff --git a/docker/boot.ini.template b/docker/boot.ini.template new file mode 100644 index 0000000..8f8842e --- /dev/null +++ b/docker/boot.ini.template @@ -0,0 +1,42 @@ +; model a PDP-11/70 with maximum memory, fpu, no CIS +set cpu 11/70 4096K fpp + +; disable all extra devices by default, will enable later +detach all +reset all + +; use 7b ascii terminal +set tto 7b + +; set the boot disk as an MSCP UDA50 +set rq enabled +set rq0 rauser=1000 +attach rq0 /mnt/ +show rq0 + +; set one DZ11 8 line async mux +; accessible via telnet to port 4000 +set dz enabled +set dz lines=8 +set dz 7b +attach -am dz 4000 +show dz + +; set one DELUA/DEUNA unibus enet controller +; ==> must setup ethX per your system config +set xu enabled +set xu type=delua +attach xu tap:pdp +show xu + +; show our config +show devices +show cpu iospace + +; Configure all csr/vector +set cpu autoconfig +; Change DZ vector +set dz vector=310 +; boot it +boot rq0 + diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000..493d9b7 --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +usage () { + echo "usage: $0 tag" + exit 1 +} + +[ -n "$1" ] && tag="$1" || usage + +file="simh-3.9-0.tgz" +url="https://codeload.github.com/simh/simh/tar.gz/refs/tags/v3.9-0" + +cd "$(dirname $0)" + +[ -f "$file" ] || wget "$url" -O "$file" + +docker build -t "$tag" . diff --git a/docker/startup.sh b/docker/startup.sh new file mode 100755 index 0000000..f8e23e9 --- /dev/null +++ b/docker/startup.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -ex + +[ -n "$DISK_FILENAME" ] || DISK_FILENAME="rq0.dsk" +[ -f "/mnt/$DISK_FILENAME" ] || { echo "Unable to find tape /mnt/$DISK_FILENAME"; exit 1; } +[ -w "/mnt/$DISK_FILENAME" ] || { echo "Tape /mnt/$DISK_FILENAME is not writable"; exit 1; } + +# Set exit command only if NOEXIT not set +[ -z "$NOEXIT" ] && EXIT_CMD="exit" || EXIT_CMD="" + +sed -e "s//${DISK_FILENAME}/" \ + -e "s//${EXIT_CMD}/" /boot.ini.template > /boot.ini + +cidr=28 +hostip=1.3.3.1 +pdpip=1.3.3.7 + +ip tuntap add pdp mode tap +ip link set pdp up +ip addr add "${hostip}/${cidr}" dev pdp +iptables -t nat -A PREROUTING ! -i pdp -p tcp --dport 4000 -j ACCEPT +iptables -t nat -A PREROUTING ! -i pdp -j DNAT --to-destination 1.3.3.7 +iptables -t nat -A POSTROUTING ! -o pdp -j MASQUERADE + +pdp11 /boot.ini diff --git a/k8s/configure-metallb.sh b/k8s/configure-metallb.sh new file mode 100755 index 0000000..7a90469 --- /dev/null +++ b/k8s/configure-metallb.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +usage () { + echo "usage: $0 ip" + exit 1 +} + +[[ $# -eq 1 ]] || usage +ip="$1" + +cd "$(dirname $0)" + +sed "s!!${ip}!" ./metallb.yaml.template > ./metallb.yaml + +echo "Configuring metallb..." +kubectl apply -f ./metallb.yaml +echo "Configuring services..." +for i in ./svc-*.yaml; do + svc="$(grep -Po "name: \Kpdp-.*$" "$i")" + kubectl patch svc "$svc" -p '{"metadata":{"annotations":{"metallb.universe.tf/allow-shared-ip":"'"$ip"'"}},"spec":{"type": "LoadBalancer","loadBalancerIP":"'"$ip"'"}}' +done +echo "Done!" diff --git a/k8s/deployment.yaml.template b/k8s/deployment.yaml.template new file mode 100644 index 0000000..4b97a57 --- /dev/null +++ b/k8s/deployment.yaml.template @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: pdp + labels: + app.kubernetes.io/name: pdp +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: pdp + template: + metadata: + labels: + app.kubernetes.io/name: pdp + spec: + volumes: + - name: disk + persistentVolumeClaim: + claimName: pdp-disk + containers: + - name: pdp + image: + volumeMounts: + - name: disk + mountPath: /mnt + securityContext: + allowPrivilegeEscalation: true + privileged: true diff --git a/k8s/metallb.yaml.template b/k8s/metallb.yaml.template new file mode 100644 index 0000000..cbf4ae9 --- /dev/null +++ b/k8s/metallb.yaml.template @@ -0,0 +1,15 @@ +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: pdp + namespace: metallb-system +spec: + addresses: + - /32 + autoAssign: false +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: default-l2 + namespace: metallb-system diff --git a/k8s/pod-tmp.yaml b/k8s/pod-tmp.yaml new file mode 100644 index 0000000..23a2d4a --- /dev/null +++ b/k8s/pod-tmp.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pdp-tmp +spec: + volumes: + - name: disk + persistentVolumeClaim: + claimName: pdp-disk + containers: + - name: pdp + image: alpine + command: ["sleep", "infinity"] + volumeMounts: + - name: disk + mountPath: /mnt + restartPolicy: Never diff --git a/k8s/pvc.yaml b/k8s/pvc.yaml new file mode 100644 index 0000000..7704f2b --- /dev/null +++ b/k8s/pvc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pdp-disk +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi diff --git a/k8s/svc-ftp.yaml b/k8s/svc-ftp.yaml new file mode 100644 index 0000000..35f039d --- /dev/null +++ b/k8s/svc-ftp.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: pdp-ftp + labels: + app.kubernetes.io/name: pdp +spec: + selector: + app.kubernetes.io/name: pdp + ports: + - protocol: TCP + port: 21 diff --git a/k8s/svc-http.yaml b/k8s/svc-http.yaml new file mode 100644 index 0000000..4f1abd9 --- /dev/null +++ b/k8s/svc-http.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: pdp-http + labels: + app.kubernetes.io/name: pdp +spec: + selector: + app.kubernetes.io/name: pdp + ports: + - protocol: TCP + port: 80 diff --git a/k8s/svc-telnet.yaml b/k8s/svc-telnet.yaml new file mode 100644 index 0000000..b5cc576 --- /dev/null +++ b/k8s/svc-telnet.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: pdp-telnet + labels: + app.kubernetes.io/name: pdp +spec: + selector: + app.kubernetes.io/name: pdp + ports: + - protocol: TCP + port: 23