From 1a1dd13a3364e626b9798d1a12bf7263a9fb95da Mon Sep 17 00:00:00 2001 From: AbdelrahmanElawady <60783742+AbdelrahmanElawady@users.noreply.github.com> Date: Wed, 13 Sep 2023 12:51:53 +0300 Subject: [PATCH 1/4] Add cpu benchmark binary package (#2053) * Add cpu benchmark binary package * Rename package to cpubench * Change package to be executable --- .github/workflows/bins.yaml | 6 ++++++ bins/packages/cpubench/cpubench.sh | 32 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 bins/packages/cpubench/cpubench.sh diff --git a/.github/workflows/bins.yaml b/.github/workflows/bins.yaml index 7923e2d74..1fc2bedc6 100644 --- a/.github/workflows/bins.yaml +++ b/.github/workflows/bins.yaml @@ -132,3 +132,9 @@ jobs: package: iperf secrets: token: ${{ secrets.HUB_JWT }} + cpubench: + uses: ./.github/workflows/bin-package.yaml + with: + package: cpubench + secrets: + token: ${{ secrets.HUB_JWT }} diff --git a/bins/packages/cpubench/cpubench.sh b/bins/packages/cpubench/cpubench.sh new file mode 100644 index 000000000..c77c8a6f4 --- /dev/null +++ b/bins/packages/cpubench/cpubench.sh @@ -0,0 +1,32 @@ +CPU_BENCHMARK_VERSION="v0.1" +CPU_BENCHMARK_CHECKSUM="25891eb15ec0b1bb8d745a8af3907895" +CPU_BENCHMARK_LINK="https://github.com/threefoldtech/cpu-benchmark-simple/releases/download/${CPU_BENCHMARK_VERSION}/grid-cpubench-simple-0.1-linux-amd64-static" + +download_cpubench() { + echo "downloading cpubench" + download_file ${CPU_BENCHMARK_LINK} ${CPU_BENCHMARK_CHECKSUM} cpubench +} + + +prepare_cpubench() { + echo "[+] prepare cpubench" + github_name "cpubench-${CPU_BENCHMARK_VERSION}" +} + +install_cpubench() { + echo "[+] install cpubench" + mkdir -p "${ROOTDIR}/usr/bin" + + cp ${DISTDIR}/cpubench ${ROOTDIR}/usr/bin/cpubench + chmod +x ${ROOTDIR}/usr/bin/cpubench +} + +build_cpubench() { + pushd "${DISTDIR}" + + download_cpubench + prepare_cpubench + install_cpubench + + popd +} From 90f870916f3c5ad6e8a34a1c3545190feab2128d Mon Sep 17 00:00:00 2001 From: Rawda Fawzy <47260239+rawdaGastan@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:07:43 +0300 Subject: [PATCH 2/4] add kernel version to zui (#2058) * add kernal version * fix typo * return empty string if arr is empty * use unsafe and slice for int8 slice conversion --- cmds/modules/zui/header.go | 13 ++++++++++++- cmds/modules/zui/main.go | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cmds/modules/zui/header.go b/cmds/modules/zui/header.go index 12709b6eb..c165109e5 100644 --- a/cmds/modules/zui/header.go +++ b/cmds/modules/zui/header.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "strings" + "syscall" + "unsafe" "github.com/gizak/termui/v3/widgets" "github.com/pkg/errors" @@ -43,6 +45,7 @@ func headerRenderer(ctx context.Context, c zbus.Client, h *widgets.Paragraph, r "\n" + " This is node %s (farmer %s)\n" + " running Zero-OS version [%s](fg:blue) (mode [%s](fg:cyan))\n" + + " kernel: %s\n" + " cache disk: %s" host := stubs.NewVersionMonitorStub(c) @@ -77,7 +80,15 @@ func headerRenderer(ctx context.Context, c zbus.Client, h *widgets.Paragraph, r cache = red("no ssd disks detected") } - h.Text = fmt.Sprintf(s, nodeID, farm, version.String(), env.RunningMode.String(), cache) + var utsname syscall.Utsname + var uname string + if err := syscall.Uname(&utsname); err != nil { + uname = red(err.Error()) + } else { + uname = green(string(unsafe.Slice((*byte)(unsafe.Pointer(&utsname.Release)), len(utsname.Release)))) + } + + h.Text = fmt.Sprintf(s, nodeID, farm, version.String(), env.RunningMode.String(), uname, cache) r.Signal() } }() diff --git a/cmds/modules/zui/main.go b/cmds/modules/zui/main.go index ba08b706d..6906bc074 100644 --- a/cmds/modules/zui/main.go +++ b/cmds/modules/zui/main.go @@ -67,11 +67,11 @@ func action(ctx *cli.Context) error { header := widgets.NewParagraph() header.Border = true - header.SetRect(0, 0, width, 7) + header.SetRect(0, 0, width, 8) netgrid := ui.NewGrid() netgrid.Title = "Network" - netgrid.SetRect(0, 7, width, 14) + netgrid.SetRect(0, 8, width, 14) resources := ui.NewGrid() resources.Title = "Provision" From aa6efe62094fd7e6329d9a5f84770b4aafef249a Mon Sep 17 00:00:00 2001 From: Rawda Fawzy <47260239+rawdaGastan@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:30:41 +0300 Subject: [PATCH 3/4] validate cpus according to actual number of node cpus (#2057) * validate cpus according to actual number of nodes cpus * fix typos * rename n to cpus --- pkg/gridtypes/zos/zmachine.go | 2 +- pkg/vm.go | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pkg/gridtypes/zos/zmachine.go b/pkg/gridtypes/zos/zmachine.go index 793a289cf..2f755c622 100644 --- a/pkg/gridtypes/zos/zmachine.go +++ b/pkg/gridtypes/zos/zmachine.go @@ -184,7 +184,7 @@ func (v ZMachine) Valid(getter gridtypes.WorkloadGetter) error { } } if v.ComputeCapacity.CPU == 0 { - return fmt.Errorf("cpu capcity can't be 0") + return fmt.Errorf("cpu capacity can't be 0") } if v.ComputeCapacity.Memory < 250*gridtypes.Megabyte { return fmt.Errorf("mem capacity can't be less that 250M") diff --git a/pkg/vm.go b/pkg/vm.go index 655085021..b9ef42aa6 100644 --- a/pkg/vm.go +++ b/pkg/vm.go @@ -6,6 +6,7 @@ import ( "net" "path/filepath" + "github.com/shirou/gopsutil/cpu" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -126,7 +127,7 @@ type VM struct { Network VMNetworkInfo // KernelImage path to uncompressed linux kernel ELF KernelImage string - // InitrdImage (optiona) path to initrd disk + // InitrdImage (optional) path to initrd disk InitrdImage string // KernelArgs to override the default kernel arguments. (default: "ro console=ttyS0 noapic reboot=k panic=1 pci=off nomodules") KernelArgs KernelArgs @@ -175,8 +176,16 @@ func (vm *VM) Validate() error { return fmt.Errorf("invalid memory must not be less than 250M") } - if vm.CPU == 0 || vm.CPU > 32 { - return fmt.Errorf("invalid cpu must be between 1 and 32") + cpus, err := cpu.Counts(true) + if err != nil { + return fmt.Errorf("failed to get count of cpus") + } + + if vm.CPU == 0 || vm.CPU > uint8(cpus) { + if cpus == 1 { + return fmt.Errorf("invalid cpu must be 1") + } + return fmt.Errorf("invalid cpu must be between 1 and %d", cpus) } for _, shared := range vm.Shared { @@ -234,7 +243,7 @@ func (n *NetMetric) Nu() float64 { } // MachineMetric is a container for metrics from multiple networks -// currently only groped as private (wiregaurd + yggdrasil), and public (public Ips) +// currently only grouped as private (wireguard + yggdrasil), and public (public Ips) type MachineMetric struct { Private NetMetric Public NetMetric From 4944c63a67eef869d5536b4d3b70df6d1686e90e Mon Sep 17 00:00:00 2001 From: Omar Abdulaziz Date: Tue, 26 Sep 2023 11:06:12 +0300 Subject: [PATCH 4/4] performance monitor package (#2046) * init performance monitor package * handle new rmb call * resolve comments: - introduce Task interface - remove testing code - use redis connection from pool - unexpose some methods and fields - update running zos node docs & add example for using perf * resolve comments: - use redis pool correctly - add GetAll method - better naming for tasks with prefix `perf.` - use unix date * update go.mod file * - add new handler for getAll method - reuse Get code * run tasks if it has no results stored, fix getAll method * run the tasks in separate routines and use the redis pool pointer * removing the wg and log the errors in the routine --- cmds/modules/noded/main.go | 25 ++++++ docs/development/quickstart.md | 3 +- go.mod | 5 +- go.sum | 11 +++ pkg/perf/README.md | 39 ++++++++++ pkg/perf/cache.go | 121 ++++++++++++++++++++++++++++++ pkg/perf/monitor.go | 88 ++++++++++++++++++++++ pkg/perf/tasks.go | 11 +++ qemu/Makefile | 13 ++++ qemu/README.md | 5 +- {docs/development => qemu}/net.sh | 0 11 files changed, 317 insertions(+), 4 deletions(-) create mode 100644 pkg/perf/README.md create mode 100644 pkg/perf/cache.go create mode 100644 pkg/perf/monitor.go create mode 100644 pkg/perf/tasks.go rename {docs/development => qemu}/net.sh (100%) diff --git a/cmds/modules/noded/main.go b/cmds/modules/noded/main.go index 01c3cd31b..cef315578 100644 --- a/cmds/modules/noded/main.go +++ b/cmds/modules/noded/main.go @@ -3,6 +3,7 @@ package noded import ( "context" "crypto/ed25519" + "encoding/json" "fmt" "os/exec" "strings" @@ -19,6 +20,7 @@ import ( "github.com/threefoldtech/zos/pkg/environment" "github.com/threefoldtech/zos/pkg/events" "github.com/threefoldtech/zos/pkg/monitord" + "github.com/threefoldtech/zos/pkg/perf" "github.com/threefoldtech/zos/pkg/registrar" "github.com/threefoldtech/zos/pkg/stubs" "github.com/threefoldtech/zos/pkg/utils" @@ -193,6 +195,29 @@ func action(cli *cli.Context) error { return hypervisor, nil }) + log.Info().Msg("start perf scheduler") + + perfMon, err := perf.NewPerformanceMonitor(msgBrokerCon) + if err != nil { + return errors.Wrap(err, "failed to create a new perfMon") + } + + if err = perfMon.Run(ctx); err != nil { + return errors.Wrap(err, "failed to run the scheduler") + } + bus.WithHandler("zos.perf.get", func(ctx context.Context, payload []byte) (interface{}, error) { + var taskName string + err := json.Unmarshal(payload, &taskName) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal payload: %v", payload) + } + + return perfMon.Get(taskName) + }) + bus.WithHandler("zos.perf.get_all", func(ctx context.Context, payload []byte) (interface{}, error) { + return perfMon.GetAll() + }) + // answer calls for dmi go func() { if err := bus.Run(ctx); err != nil { diff --git a/docs/development/quickstart.md b/docs/development/quickstart.md index 0563b614d..fb28b481d 100644 --- a/docs/development/quickstart.md +++ b/docs/development/quickstart.md @@ -4,6 +4,7 @@ - [Starting a local zos node](#starting-a-local-zos-node) - [Accessing node](#accessing-node) - [Development](#development) +- [Qemu docs](../../qemu/README.md) ## Starting a local zos node @@ -19,7 +20,7 @@ Then, inside zos repository make -C cmds cd qemu mv ./zos.efi -sudo ./vm.sh -n myzos-01 -c "farmer_id= printk.devmsg=on runmode=dev" +sudo ./vm.sh -n node-01 -c "farmer_id= printk.devmsg=on runmode=dev" ``` You should see the qemu console and boot logs, wait for awhile and you can [browse farms](https://dashboard.dev.grid.tf/explorer/farms) to see your node is added/detected automatically. diff --git a/go.mod b/go.mod index b0163e96f..88b819af8 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,9 @@ require ( github.com/diskfs/go-diskfs v1.2.0 github.com/fsnotify/fsnotify v1.6.0 github.com/g0rbe/go-chattr v0.0.0-20190906133247-aa435a6a0a37 + github.com/garyburd/redigo v1.6.2 github.com/gizak/termui/v3 v3.1.0 + github.com/go-co-op/gocron v1.33.1 github.com/go-redis/redis v6.15.9+incompatible github.com/gomodule/redigo v1.8.9 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 @@ -74,7 +76,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/ethereum/go-ethereum v1.11.6 // indirect - github.com/garyburd/redigo v1.6.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.4.8 // indirect @@ -121,6 +122,7 @@ require ( github.com/pkg/xattr v0.4.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/cors v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/safchain/ethtool v0.0.0-20201023143004-874930cb3ce0 // indirect @@ -136,6 +138,7 @@ require ( github.com/xxtea/xxtea-go v0.0.0-20170828040851-35c4b17eecf6 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opencensus.io v0.23.0 // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.14.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/text v0.12.0 // indirect diff --git a/go.sum b/go.sum index 8b1a270d5..07e99f388 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= @@ -156,6 +157,8 @@ github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= +github.com/go-co-op/gocron v1.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c= +github.com/go-co-op/gocron v1.33.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -292,6 +295,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -413,6 +417,7 @@ github.com/pierrec/lz4 v2.3.0+incompatible h1:CZzRn4Ut9GbUkHlQ7jqBXeZQV41ZSKWFc3 github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -437,9 +442,13 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= @@ -558,6 +567,8 @@ go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/pkg/perf/README.md b/pkg/perf/README.md new file mode 100644 index 000000000..c2447dd65 --- /dev/null +++ b/pkg/perf/README.md @@ -0,0 +1,39 @@ +# Perf + +Perf is a performance test module that is scheduled to run and cache those tests results in redis which can be retrieved later over RMB call. + +Perf tests are monitored by `noded` service from zos modules. + +### Usage Example + +1. Create a task that implement `Task` interface + +```go +type LoggingTask struct { + TaskID string + Schedule string // should be in cron syntax with seconds ("* 0 * * * *") +} + +func (t *LoggingTask) ID() string { + return t.TaskID +} + +func (t *LoggingTask) Cron() string { + return t.Schedule +} + +// a simple task that returns a string with the current time +func (t *LoggingTask) Run(ctx context.Context) (interface{}, error) { + result := fmt.Sprintf("time is: %v", time.Now()) + return result, nil +} +``` + +2. Add the created task to scheduler + +```go +perfMon.AddTask(&perf.LoggingTask{ + TaskID: "LoggingTask", + Schedule: "* 0 * * * *", // when minutes is 0 (aka: every hour) +}) +``` diff --git a/pkg/perf/cache.go b/pkg/perf/cache.go new file mode 100644 index 000000000..98ba81367 --- /dev/null +++ b/pkg/perf/cache.go @@ -0,0 +1,121 @@ +package perf + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/garyburd/redigo/redis" + "github.com/pkg/errors" +) + +const ( + moduleName = "perf" +) + +var ( + ErrResultNotFound = errors.New("result not found") +) + +// TaskResult the result test schema +type TaskResult struct { + Name string `json:"name"` + Timestamp uint64 `json:"timestamp"` + Result interface{} `json:"result"` +} + +// generateKey is helper method to add moduleName as prefix for the taskName +func generateKey(taskName string) string { + return fmt.Sprintf("%s.%s", moduleName, taskName) +} + +// setCache set result in redis +func (pm *PerformanceMonitor) setCache(ctx context.Context, result TaskResult) error { + data, err := json.Marshal(result) + if err != nil { + return errors.Wrap(err, "failed to marshal data to JSON") + } + + conn := pm.pool.Get() + defer conn.Close() + + _, err = conn.Do("SET", generateKey(result.Name), data) + return err +} + +// get directly gets result for some key +func get(conn redis.Conn, key string) (TaskResult, error) { + var res TaskResult + + data, err := conn.Do("GET", key) + if err != nil { + return res, errors.Wrap(err, "failed to get the result") + } + + if data == nil { + return res, ErrResultNotFound + } + + err = json.Unmarshal(data.([]byte), &res) + if err != nil { + return res, errors.Wrap(err, "failed to unmarshal data from json") + } + + return res, nil +} + +// Get gets data from redis +func (pm *PerformanceMonitor) Get(taskName string) (TaskResult, error) { + conn := pm.pool.Get() + defer conn.Close() + return get(conn, generateKey(taskName)) +} + +// GetAll gets the results for all the tests with moduleName as prefix +func (pm *PerformanceMonitor) GetAll() ([]TaskResult, error) { + var res []TaskResult + + conn := pm.pool.Get() + defer conn.Close() + + var keys []string + + cursor := 0 + for { + values, err := redis.Values(conn.Do("SCAN", cursor, "MATCH", generateKey("*"))) + if err != nil { + return nil, err + } + + _, err = redis.Scan(values, &cursor, &keys) + if err != nil { + return nil, err + } + + for _, key := range keys { + result, err := get(conn, key) + if err != nil { + continue + } + res = append(res, result) + } + + if cursor == 0 { + break + } + + } + return res, nil +} + +// exists check if a key exists +func (pm *PerformanceMonitor) exists(key string) (bool, error) { + conn := pm.pool.Get() + defer conn.Close() + + ok, err := redis.Bool(conn.Do("EXISTS", generateKey(key))) + if err != nil { + return false, errors.Wrapf(err, "error checking if key %s exists", generateKey(key)) + } + return ok, nil +} diff --git a/pkg/perf/monitor.go b/pkg/perf/monitor.go new file mode 100644 index 000000000..4d7c40318 --- /dev/null +++ b/pkg/perf/monitor.go @@ -0,0 +1,88 @@ +package perf + +import ( + "context" + "time" + + "github.com/go-co-op/gocron" + "github.com/gomodule/redigo/redis" + "github.com/rs/zerolog/log" + + "github.com/pkg/errors" + "github.com/threefoldtech/zos/pkg/utils" +) + +// PerformanceMonitor holds the module data +type PerformanceMonitor struct { + scheduler *gocron.Scheduler + pool *redis.Pool + tasks []Task +} + +// NewPerformanceMonitor returns PerformanceMonitor instance +func NewPerformanceMonitor(redisAddr string) (*PerformanceMonitor, error) { + redisPool, err := utils.NewRedisPool(redisAddr) + if err != nil { + return nil, errors.Wrap(err, "failed creating new redis pool") + } + + scheduler := gocron.NewScheduler(time.UTC) + + return &PerformanceMonitor{ + scheduler: scheduler, + pool: redisPool, + tasks: []Task{}, + }, nil +} + +// AddTask a simple helper method to add new tasks +func (pm *PerformanceMonitor) AddTask(task Task) { + pm.tasks = append(pm.tasks, task) +} + +// runTask runs the task and store its result +func (pm *PerformanceMonitor) runTask(ctx context.Context, task Task) error { + res, err := task.Run(ctx) + if err != nil { + return errors.Wrapf(err, "failed to run task: %s", task.ID()) + } + + err = pm.setCache(ctx, TaskResult{ + Name: task.ID(), + Timestamp: uint64(time.Now().Unix()), + Result: res, + }) + if err != nil { + return errors.Wrap(err, "failed to set cache") + } + + return nil +} + +// Run adds the tasks to the corn queue and start the scheduler +func (pm *PerformanceMonitor) Run(ctx context.Context) error { + for _, task := range pm.tasks { + if _, err := pm.scheduler.CronWithSeconds(task.Cron()).Do(func() error { + return pm.runTask(ctx, task) + }); err != nil { + return errors.Wrapf(err, "failed to schedule the task: %s", task.ID()) + } + + go func(task Task) { + ok, err := pm.exists(task.ID()) + if err != nil { + log.Error().Err(err).Msgf("failed to find key %s", task.ID()) + } + + if !ok { + if err := pm.runTask(ctx, task); err != nil { + log.Error().Err(err).Msgf("failed to run task: %s", task.ID()) + } + } + }(task) + + } + + pm.scheduler.StartAsync() + return nil +} diff --git a/pkg/perf/tasks.go b/pkg/perf/tasks.go new file mode 100644 index 000000000..e2b6efd96 --- /dev/null +++ b/pkg/perf/tasks.go @@ -0,0 +1,11 @@ +package perf + +import ( + "context" +) + +type Task interface { + ID() string + Cron() string + Run(ctx context.Context) (interface{}, error) +} diff --git a/qemu/Makefile b/qemu/Makefile index a7f951146..742cf0712 100644 --- a/qemu/Makefile +++ b/qemu/Makefile @@ -14,3 +14,16 @@ start: bash vm.sh -n node1 -c "runmode=dev farmer_id=$(FARMERID)" test: bash vm.sh -n node1 -c "runmode=test farmer_id=$(FARMERID)" + +auth: + @echo "Copying your public ssh to machine rootfs" + mkdir -p overlay/root/.ssh + cp ~/.ssh/id_rsa.pub overlay/root/.ssh/authorized_keys + +net: + @echo "Creating a virtual natted network" + bash ./net.sh + +run: + @echo "Running your node" + sudo ./vm.sh -g -n node-01 -c "farmer_id=$(id) version=v3 printk.devmsg=on runmode=dev nomodeset" diff --git a/qemu/README.md b/qemu/README.md index 11f378224..5050d58d1 100644 --- a/qemu/README.md +++ b/qemu/README.md @@ -1,4 +1,5 @@ # Run 0-OS in a VM using qemu +| For a quick development docs check [here](../docs/development/README.md) This folder contains a script that you can use to run 0-OS in a VM using qemu. @@ -111,7 +112,7 @@ sudo dnsmasq --strict-order \ 1. Now run your vm ```bash -sudo ./vm.sh -n myzos-01 -c "farmer_id=47 printk.devmsg=on runmode=dev" +sudo ./vm.sh -n node-01 -c "farmer_id=47 printk.devmsg=on runmode=dev" ``` where `runmode` is one of `dev` , `test` or `prod`, @@ -145,7 +146,7 @@ sudo ip link set forzos up 1. Now run your vm ```bash -./vm.sh -n myzos-01 -c "farmer_id=47 version=v3 printk.devmsg=on runmode=dev" +./vm.sh -n node-01 -c "farmer_id=47 version=v3 printk.devmsg=on runmode=dev" ``` where `runmode` is one of `dev` , `test` or `prod`, diff --git a/docs/development/net.sh b/qemu/net.sh similarity index 100% rename from docs/development/net.sh rename to qemu/net.sh