mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2025-02-24 13:30:02 +08:00
QEMU host
QEMU host
This commit is contained in:
commit
9792ae6615
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -8,7 +8,3 @@ updates:
|
|||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
- package-ecosystem: gomod
|
|
||||||
directory: /serial
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
|
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
@ -11,4 +11,4 @@ jobs:
|
|||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
env:
|
env:
|
||||||
SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064
|
SHELLCHECK_OPTS: -x -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064
|
||||||
|
1
.github/workflows/hub.yml
vendored
1
.github/workflows/hub.yml
vendored
@ -5,6 +5,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- readme.md
|
- readme.md
|
||||||
|
- README.md
|
||||||
- .github/workflows/hub.yml
|
- .github/workflows/hub.yml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@ -4,11 +4,15 @@ on:
|
|||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- '**/*.sh'
|
- '**/*.sh'
|
||||||
|
- '.github/workflows/test.yml'
|
||||||
|
- '.github/workflows/check.yml'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '**/*.sh'
|
- '**/*.sh'
|
||||||
|
- '.github/workflows/test.yml'
|
||||||
|
- '.github/workflows/check.yml'
|
||||||
|
|
||||||
name: "Test"
|
name: "Test"
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
|
16
Dockerfile
16
Dockerfile
@ -1,10 +1,10 @@
|
|||||||
FROM golang AS builder
|
FROM golang as builder
|
||||||
|
|
||||||
COPY serial/ /src/serial/
|
|
||||||
WORKDIR /src/serial
|
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
RUN git clone https://github.com/qemu-tools/qemu-host.git
|
||||||
|
WORKDIR /qemu-host/src
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /src/serial/main .
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host/src/main .
|
||||||
|
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
@ -34,12 +34,12 @@ RUN apt-get update && apt-get -y upgrade && \
|
|||||||
COPY run/*.sh /run/
|
COPY run/*.sh /run/
|
||||||
COPY agent/*.sh /agent/
|
COPY agent/*.sh /agent/
|
||||||
|
|
||||||
COPY --from=builder /src/serial/main /run/serial.bin
|
COPY --from=builder /qemu-host/src/main /run/host.bin
|
||||||
|
|
||||||
RUN ["chmod", "+x", "/run/run.sh"]
|
RUN ["chmod", "+x", "/run/run.sh"]
|
||||||
RUN ["chmod", "+x", "/run/check.sh"]
|
RUN ["chmod", "+x", "/run/check.sh"]
|
||||||
RUN ["chmod", "+x", "/run/server.sh"]
|
RUN ["chmod", "+x", "/run/server.sh"]
|
||||||
RUN ["chmod", "+x", "/run/serial.bin"]
|
RUN ["chmod", "+x", "/run/host.bin"]
|
||||||
|
|
||||||
VOLUME /storage
|
VOLUME /storage
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ ENV VERSION=$VERSION_ARG
|
|||||||
LABEL org.opencontainers.image.created=${DATE_ARG}
|
LABEL org.opencontainers.image.created=${DATE_ARG}
|
||||||
LABEL org.opencontainers.image.revision=${BUILD_ARG}
|
LABEL org.opencontainers.image.revision=${BUILD_ARG}
|
||||||
LABEL org.opencontainers.image.version=${VERSION_ARG}
|
LABEL org.opencontainers.image.version=${VERSION_ARG}
|
||||||
LABEL org.opencontainers.image.url=https://hub.docker.com/r/kroese/virtual-dsm/
|
|
||||||
LABEL org.opencontainers.image.source=https://github.com/kroese/virtual-dsm/
|
LABEL org.opencontainers.image.source=https://github.com/kroese/virtual-dsm/
|
||||||
|
LABEL org.opencontainers.image.url=https://hub.docker.com/r/kroese/virtual-dsm/
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --retries=1 CMD /run/check.sh
|
HEALTHCHECK --interval=30s --retries=1 CMD /run/check.sh
|
||||||
|
|
||||||
|
@ -182,4 +182,4 @@ Based on an [article](https://jxcn.org/2022/04/vdsm-first-try/) by JXCN.
|
|||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
Only run this container on Synology hardware, any other use is not permitted and might not be legal according to their terms.
|
Only run this container on Synology hardware, any other use is not permitted and might not be legal.
|
||||||
|
@ -81,5 +81,5 @@ set +m
|
|||||||
if (( KERNEL > 4 )); then
|
if (( KERNEL > 4 )); then
|
||||||
pidwait -F "${_QEMU_PID}" & wait $!
|
pidwait -F "${_QEMU_PID}" & wait $!
|
||||||
else
|
else
|
||||||
tail --pid "$(cat ${_QEMU_PID})" --follow /dev/null & wait $!
|
tail --pid "$(cat "${_QEMU_PID}")" --follow /dev/null & wait $!
|
||||||
fi
|
fi
|
||||||
|
@ -4,12 +4,11 @@ set -eu
|
|||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
||||||
: ${HOST_CPU:=''}
|
: ${HOST_CPU:=''}
|
||||||
: ${HOST_BUILD:='42962'}
|
: ${HOST_BUILD:=''}
|
||||||
: ${HOST_VERSION:='2.6.1-12139'}
|
: ${HOST_SERIAL:=''}
|
||||||
: ${HOST_TIMESTAMP:='1679863686'}
|
: ${GUEST_SERIAL:=''}
|
||||||
: ${HOST_SERIAL:='0000000000000'}
|
: ${HOST_VERSION:=''}
|
||||||
: ${GUEST_SERIAL:='0000000000000'}
|
: ${HOST_TIMESTAMP:=''}
|
||||||
: ${GUEST_UUID:='ba13a19a-c0c1-4fef-9346-915ed3b98341'}
|
|
||||||
|
|
||||||
if [ -z "$HOST_CPU" ]; then
|
if [ -z "$HOST_CPU" ]; then
|
||||||
HOST_CPU=$(lscpu | sed -nr '/Model name/ s/.*:\s*(.*) @ .*/\1/p' | sed ':a;s/ / /;ta' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
HOST_CPU=$(lscpu | sed -nr '/Model name/ s/.*:\s*(.*) @ .*/\1/p' | sed ':a;s/ / /;ta' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
||||||
@ -21,14 +20,24 @@ else
|
|||||||
HOST_CPU="QEMU, Virtual CPU, X86_64"
|
HOST_CPU="QEMU, Virtual CPU, X86_64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
./run/serial.bin -cpu="${CPU_CORES}" \
|
HOST_ARGS=()
|
||||||
-cpu_arch="${HOST_CPU}" \
|
HOST_ARGS+=("-cpu_arch=${HOST_CPU}")
|
||||||
-hostsn="${HOST_SERIAL}" \
|
|
||||||
-guestsn="${GUEST_SERIAL}" \
|
[ -n "$CPU_CORES" ] && HOST_ARGS+=("-cpu=${CPU_CORES}")
|
||||||
-vmmts="${HOST_TIMESTAMP}" \
|
[ -n "$HOST_BUILD" ] && HOST_ARGS+=("-build=${HOST_BUILD}")
|
||||||
-vmmversion="${HOST_VERSION}" \
|
[ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=${HOST_SERIAL}")
|
||||||
-buildnumber="${HOST_BUILD}" \
|
[ -n "$HOST_TIMESTAMP" ] && HOST_ARGS+=("-ts=${HOST_TIMESTAMP}")
|
||||||
-guestuuid="${GUEST_UUID}" > /dev/null 2>&1 &
|
[ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=${GUEST_SERIAL}")
|
||||||
|
[ -n "$HOST_VERSION" ] && HOST_ARGS+=("-version=${HOST_VERSION}")
|
||||||
|
|
||||||
|
if [ "$DEBUG" = "Y" ]; then
|
||||||
|
echo -n "./run/host.bin "
|
||||||
|
echo "${HOST_ARGS[*]}" && echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
./run/host.bin "${HOST_ARGS[@]}" > /dev/null 2>&1 &
|
||||||
|
|
||||||
|
# Configure serial ports
|
||||||
|
|
||||||
SERIAL_OPTS="\
|
SERIAL_OPTS="\
|
||||||
-serial mon:stdio \
|
-serial mon:stdio \
|
||||||
|
@ -1 +0,0 @@
|
|||||||
package main
|
|
@ -1,5 +0,0 @@
|
|||||||
module vdsm-serial
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
require github.com/gorilla/mux v1.8.0
|
|
@ -1,2 +0,0 @@
|
|||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
263
serial/main.go
263
serial/main.go
@ -1,263 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"flag"
|
|
||||||
"bytes"
|
|
||||||
"strconv"
|
|
||||||
"net/http"
|
|
||||||
"math/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
type REQ struct {
|
|
||||||
RandID int64
|
|
||||||
GuestUUID [16]byte
|
|
||||||
GuestID int64
|
|
||||||
IsReq int32
|
|
||||||
IsResp int32
|
|
||||||
NeedResponse int32
|
|
||||||
ReqLength int32
|
|
||||||
RespLength int32
|
|
||||||
CommandID int32
|
|
||||||
SubCommand int32
|
|
||||||
Reserve int32
|
|
||||||
}
|
|
||||||
|
|
||||||
var GuestCPUs = flag.Int("cpu", 1, "Num of Guest cpu")
|
|
||||||
var Cluster_UUID = "3bdea92b-68f4-4fe9-aa4b-d645c3c63864"
|
|
||||||
var HostDSMfixNumber = flag.Int("fixNumber", 0, "Fix Number of Host")
|
|
||||||
var VMMTimestamp = flag.Int("vmmts", 1679863686, "VMM Timestamp")
|
|
||||||
var VMMVersion = flag.String("vmmversion", "2.6.1-12139", "VMM version")
|
|
||||||
var HostSN = flag.String("hostsn", "0000000000000", "Host SN, 13 bytes")
|
|
||||||
var GuestSN = flag.String("guestsn", "0000000000000", "Guest SN, 13 bytes")
|
|
||||||
var HostDSMBuildNumber = flag.Int("buildnumber", 42962, "Build Number of Host")
|
|
||||||
var GuestCPU_ARCH = flag.String("cpu_arch", "QEMU, Virtual CPU, X86_64", "CPU arch")
|
|
||||||
var GuestUUID = flag.String("guestuuid", "ba13a19a-c0c1-4fef-9346-915ed3b98341", "Guest UUID")
|
|
||||||
|
|
||||||
var ApiPort = flag.String("api", ":2210", "API port")
|
|
||||||
var ListenAddr = flag.String("addr", "0.0.0.0:12345", "Listen address")
|
|
||||||
|
|
||||||
var LastConnection net.Conn
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/", home)
|
|
||||||
r.HandleFunc("/write", write)
|
|
||||||
go http.ListenAndServe(*ApiPort, r)
|
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", *ListenAddr)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Error listening", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Start listen on " + *ListenAddr)
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("Error on accept", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("New connection from %s\n", conn.RemoteAddr().String())
|
|
||||||
|
|
||||||
go incoming_conn(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func incoming_conn(conn net.Conn) {
|
|
||||||
|
|
||||||
LastConnection = conn
|
|
||||||
|
|
||||||
for {
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
len, err := conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error on read", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len != 4096 {
|
|
||||||
log.Printf("Read %d Bytes, not 4096\n", len)
|
|
||||||
// something wrong, close and wait for reconnect
|
|
||||||
conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go process_req(buf, conn)
|
|
||||||
//log.Printf("Read %d Bytes\n%#v\n", len, string(buf[:len]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandsName = map[int]string{
|
|
||||||
3: "Guest Power info",
|
|
||||||
4: "Host DSM version",
|
|
||||||
5: "Guest SN",
|
|
||||||
7: "Guest CPU info",
|
|
||||||
9: "Host DSM version",
|
|
||||||
8: "VMM version",
|
|
||||||
10: "Get Guest Info",
|
|
||||||
11: "Guest UUID",
|
|
||||||
12: "Cluster UUID",
|
|
||||||
13: "Host SN",
|
|
||||||
16: "Update Deadline",
|
|
||||||
17: "Guest Timestamp",
|
|
||||||
}
|
|
||||||
|
|
||||||
func process_req(buf []byte, conn net.Conn) {
|
|
||||||
var req REQ
|
|
||||||
var data string
|
|
||||||
err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, &req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error on decode %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.IsReq == 1 {
|
|
||||||
data = string(buf[64 : 64+req.ReqLength])
|
|
||||||
} else if req.IsResp == 1 {
|
|
||||||
data = string(buf[64 : 64+req.RespLength])
|
|
||||||
}
|
|
||||||
|
|
||||||
// log.Printf("%#v\n", req)
|
|
||||||
log.Printf("Command: %s from Guest:%d \n", commandsName[int(req.CommandID)], req.GuestID)
|
|
||||||
if data != "" {
|
|
||||||
log.Printf("Info: %s\n", data)
|
|
||||||
}
|
|
||||||
// Hard code of command
|
|
||||||
switch req.CommandID {
|
|
||||||
case 3:
|
|
||||||
// Guest start/reboot
|
|
||||||
case 4:
|
|
||||||
// Host DSM version
|
|
||||||
data = fmt.Sprintf(`{"buildnumber":%d,"smallfixnumber":%d}`, *HostDSMBuildNumber, *HostDSMfixNumber)
|
|
||||||
case 5:
|
|
||||||
// Guest SN
|
|
||||||
data = *GuestSN
|
|
||||||
case 7:
|
|
||||||
// CPU info
|
|
||||||
// {"cpuinfo":"QEMU, Virtual CPU, X86_64, 1" "vcpu_num":1}
|
|
||||||
data = fmt.Sprintf(`{"cpuinfo":"%s","vcpu_num":%d}`,
|
|
||||||
*GuestCPU_ARCH+", "+strconv.Itoa(*GuestCPUs), *GuestCPUs)
|
|
||||||
case 8:
|
|
||||||
data = fmt.Sprintf(`{"id":"Virtualization","name":"Virtual Machine Manager","timestamp":%d,"version":"%s"}`,
|
|
||||||
*VMMTimestamp, *VMMVersion)
|
|
||||||
case 9:
|
|
||||||
// Version Info
|
|
||||||
case 10:
|
|
||||||
// Guest Info
|
|
||||||
case 11:
|
|
||||||
// Guest UUID
|
|
||||||
data = *GuestUUID
|
|
||||||
case 12:
|
|
||||||
// cluster UUID
|
|
||||||
data = Cluster_UUID
|
|
||||||
case 13:
|
|
||||||
// Host SN
|
|
||||||
data = *HostSN
|
|
||||||
case 16:
|
|
||||||
// Update Dead line time, always 0x7fffffffffffffff
|
|
||||||
data = "9223372036854775807"
|
|
||||||
case 17:
|
|
||||||
// TimeStamp
|
|
||||||
default:
|
|
||||||
log.Printf("No handler for this command %d\n", req.CommandID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it's a req and need response
|
|
||||||
if req.IsReq == 1 && req.NeedResponse == 1 {
|
|
||||||
buf = make([]byte, 0, 4096)
|
|
||||||
writer := bytes.NewBuffer(buf)
|
|
||||||
req.IsResp = 1
|
|
||||||
req.IsReq = 0
|
|
||||||
req.ReqLength = 0
|
|
||||||
req.RespLength = int32(len([]byte(data)) + 1)
|
|
||||||
log.Printf("Response data: %s\n", data)
|
|
||||||
|
|
||||||
// write to buf
|
|
||||||
binary.Write(writer, binary.LittleEndian, &req)
|
|
||||||
writer.Write([]byte(data))
|
|
||||||
res := writer.Bytes()
|
|
||||||
// full fill 4096
|
|
||||||
buf = make([]byte, 4096, 4096)
|
|
||||||
copy(buf, res)
|
|
||||||
conn.Write(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func home(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(`{"status": "error", "data": null, "message": "No command specified"}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func write(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var commandID int
|
|
||||||
|
|
||||||
query := r.URL.Query()
|
|
||||||
commandID, err = strconv.Atoi(query.Get("command"))
|
|
||||||
|
|
||||||
if (err != nil || commandID < 1) {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(`{"status": "error", "data": null, "message": "Invalid command ID"}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (send_command((int32)(commandID), 1) == false) {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(`{"status": "error", "data": null, "message": "Failed to send command"}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(`{"status": "success", "data": null, "message": null}`))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func send_command(CommandID int32, SubCommand int32) bool {
|
|
||||||
|
|
||||||
var req REQ
|
|
||||||
|
|
||||||
req.CommandID = CommandID
|
|
||||||
req.SubCommand = SubCommand
|
|
||||||
|
|
||||||
req.IsReq = 1
|
|
||||||
req.IsResp = 0
|
|
||||||
req.ReqLength = 0
|
|
||||||
req.RespLength = 0
|
|
||||||
req.NeedResponse = 0
|
|
||||||
req.GuestID = 10000000
|
|
||||||
req.RandID = rand.Int63()
|
|
||||||
|
|
||||||
var buf = make([]byte, 0, 4096)
|
|
||||||
var writer = bytes.NewBuffer(buf)
|
|
||||||
|
|
||||||
// write to buf
|
|
||||||
binary.Write(writer, binary.LittleEndian, &req)
|
|
||||||
res := writer.Bytes()
|
|
||||||
|
|
||||||
// full fill 4096
|
|
||||||
buf = make([]byte, 4096, 4096)
|
|
||||||
copy(buf, res)
|
|
||||||
|
|
||||||
//log.Printf("Writing command %d\n", CommandID)
|
|
||||||
|
|
||||||
if (LastConnection == nil) { return false }
|
|
||||||
|
|
||||||
LastConnection.Write(buf)
|
|
||||||
return true
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user