aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2024-03-10 09:35:50 -0700
committerFranck Cuny <franck@fcuny.net>2024-03-10 09:35:50 -0700
commit3218a873291aa202dc66e3124e3b13e3d7f41966 (patch)
treeb981d59661b6813cc719298fcb8ae91d99319f5f
parentinstall the zed editor (diff)
downloadinfra-3218a873291aa202dc66e3124e3b13e3d7f41966.tar.gz
delete a bunch of stuff
-rw-r--r--packages/dnsmasq-leases-html/README.md37
-rw-r--r--packages/dnsmasq-leases-html/default.nix36
-rwxr-xr-xpackages/dnsmasq-leases-html/dnsmasq-leases-html.py37
-rw-r--r--packages/dnsmasq-leases-html/templates/index.html60
-rw-r--r--packages/import-gh-to-gitea/README.org12
-rwxr-xr-xpackages/import-gh-to-gitea/archive-projects.py49
-rwxr-xr-xpackages/import-gh-to-gitea/delete-gh-repositories.py84
-rwxr-xr-xpackages/import-gh-to-gitea/import-gh-to-gitea.py62
-rw-r--r--packages/music-organizer/README.org21
-rw-r--r--packages/music-organizer/default.nix15
-rw-r--r--packages/music-organizer/go.mod5
-rw-r--r--packages/music-organizer/go.sum4
-rw-r--r--packages/music-organizer/main.go271
-rw-r--r--packages/numap/README.org47
-rw-r--r--packages/numap/go.mod3
-rw-r--r--packages/numap/internal/hwids/hwids.go148
-rw-r--r--packages/numap/internal/sysfs/parse.go21
-rw-r--r--packages/numap/internal/sysfs/pci.go145
-rw-r--r--packages/numap/numa.go116
-rw-r--r--packages/numap/numap.go31
-rw-r--r--packages/scheddomain/go.mod3
-rw-r--r--packages/scheddomain/main.go153
-rw-r--r--packages/schedlatency/go.mod3
-rw-r--r--packages/schedlatency/main.go254
24 files changed, 0 insertions, 1617 deletions
diff --git a/packages/dnsmasq-leases-html/README.md b/packages/dnsmasq-leases-html/README.md
deleted file mode 100644
index 2437deb..0000000
--- a/packages/dnsmasq-leases-html/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-Generates a static HTML page with a list of all the leases allocated by `dnsmasq`.
-
-A simple template written in the jinja syntax is used.
-
-The file containing the leases is expected to be at `/var/lib/dnsmasq/dnsmasq.leases`, but this can be overwritten by setting the environment variable `DNSMASQ_LEASES`.
-
-The output of the script is written to `/var/lib/dnsmasq/leases.html` by default, but the destination can be overwritten by setting the environment variable `DNSMASQ_LEASES_OUT`.
-
-The script can be executed automatically by `dnsmasq` if the configuration for `dhcp-script` is set to the path of the script. This will only be executed when a *new* lease is created or an *old* lease is deleted. To execute the script when a lease is *updated* you need to use the configuration `script-on-renewal`.
-
-A configuration looks like this:
-
-``` ini
-dhcp-script=${pkgs.tools.dnsmasq-to-html}/bin/dnsmasq-leases-html
-script-on-renewal
-```
-
-## nginx
-To serve the page with nginx, you can use the following configuration:
-
-``` nix
-services.nginx = {
- enable = true;
- virtualHosts."dnsmasq" = {
- listen = [
- {
- addr = "192.168.6.1";
- port = 8067;
- }
- ];
- locations."/" = {
- root = "/var/lib/dnsmasq";
- index = "leases.html";
- };
- };
-};
-```
diff --git a/packages/dnsmasq-leases-html/default.nix b/packages/dnsmasq-leases-html/default.nix
deleted file mode 100644
index 478c4cc..0000000
--- a/packages/dnsmasq-leases-html/default.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-{ lib, stdenvNoCC, pkgs }:
-
-stdenvNoCC.mkDerivation rec {
- pname = "dnsmasq-leases-html";
- src = ./dnsmasq-leases-html.py;
- templates = ./templates;
- version = "0.1.0";
-
- buildInputs = [
- (pkgs.python310.withPackages (ps: with ps; [
- jinja2
- ]))
- ];
-
- propagatedBuildInputs = [
- (pkgs.python310.withPackages (ps: with ps; [
- jinja2
- ]))
- ];
-
- dontUnpack = true;
- dontBuild = true;
-
- installPhase = ''
- mkdir -p $out/bin
- cp $src $out/bin/${pname}
- cp -r $templates $out/bin/templates
- '';
-
- meta = with pkgs.lib; {
- description = "CLI to generate a HTML page with dnsmasq leases.";
- license = licenses.mit;
- platforms = platforms.unix;
- maintainers = [ ];
- };
-}
diff --git a/packages/dnsmasq-leases-html/dnsmasq-leases-html.py b/packages/dnsmasq-leases-html/dnsmasq-leases-html.py
deleted file mode 100755
index c1f03db..0000000
--- a/packages/dnsmasq-leases-html/dnsmasq-leases-html.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python3
-
-import datetime
-import ipaddress
-import os
-
-from jinja2 import Environment, FileSystemLoader
-
-
-outfile = os.getenv("DNSMASQ_LEASES_OUT", "/var/lib/dnsmasq/leases.html")
-leases_file = os.getenv("DNSMASQ_LEASES", "/var/lib/dnsmasq/dnsmasq.leases")
-
-leases = []
-
-with open(leases_file, "r") as f:
- for line in f:
- content = line.rstrip("\n").split(" ")
- lease = dict()
- if int(content[0]) == 0:
- lease["expire"] = "never"
- else:
- lease["expire"] = datetime.datetime.fromtimestamp(int(content[0]))
- lease["MAC"] = content[1]
- lease["IP"] = ipaddress.ip_address(content[2])
- lease["hostname"] = content[3]
- leases.append(lease)
-
-leases = sorted(leases, key=lambda d: d["IP"])
-
-dir_path = os.path.dirname(os.path.realpath(__file__))
-templates_dir = os.path.join(dir_path, "templates")
-environment = Environment(loader=FileSystemLoader(templates_dir))
-template = environment.get_template("index.html")
-
-content = template.render(leases=leases)
-with open(outfile, "w") as fh:
- print(content, file=fh)
diff --git a/packages/dnsmasq-leases-html/templates/index.html b/packages/dnsmasq-leases-html/templates/index.html
deleted file mode 100644
index 913a0c9..0000000
--- a/packages/dnsmasq-leases-html/templates/index.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <title>Leases assigned by dnsmasq</title>
- <style type="text/css">
- body {
- margin: auto;
- width: 70%;
- font-family: monospace;
- font-size: 16px;
- }
- .center {
- margin-left: auto;
- margin-right: auto;
- }
- td, th {
- padding-left: 1em;
- padding-right: 1em;
- padding-top: .5em;
- padding-bottom: .5em;
- }
- td:first-child, th:first-child {
- padding-left: .25em;
- }
- td:last-child, th:last-child {
- padding-right: .25em;
- }
- th {
- padding-top: 1em;
- text-align: left;
- }
- tr:nth-child(even) {
- background: #eee;
- }
- form {
- display: inline;
- }
- </style>
-</head>
-
-<body>
- <table>
- <tr>
- <th>IP address</th>
- <th>MAC address</th>
- <th>Hostname</th>
- <th>Expire</th>
- </tr>
- {% for lease in leases %}
- <tr>
- <td>{{ lease.IP }}</td>
- <td>{{ lease.MAC }}</td>
- <td>{{ lease.hostname }}</td>
- <td>{{ lease.expire }}</td>
- </tr>
- {% endfor %}
- </table>
-</body>
-</html>
diff --git a/packages/import-gh-to-gitea/README.org b/packages/import-gh-to-gitea/README.org
deleted file mode 100644
index 2e26b88..0000000
--- a/packages/import-gh-to-gitea/README.org
+++ /dev/null
@@ -1,12 +0,0 @@
-#+TITLE: Import GitHub repositories to gitea
-
-Scripts to move my repositories from GitHub to my instance of [[https://git.fcuny.net][gitea]].
-
-* import repositories
-#+begin_src sh
-python3.10 import-gh-to-gitea.py -g (pass api/github/terraform|psub) -G (pass api/git.fcuny.net/gh-import|psub)
-#+end_src
-* archiving repositories
-#+begin_src sh
-python3.10 archive-projects.py -t (pass api/git.fcuny.net/gh-import|psub)
-#+end_src
diff --git a/packages/import-gh-to-gitea/archive-projects.py b/packages/import-gh-to-gitea/archive-projects.py
deleted file mode 100755
index 41bd898..0000000
--- a/packages/import-gh-to-gitea/archive-projects.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-
-import requests
-
-
-def main(api_token):
- s = requests.Session()
- s.headers.update({"Authorization": f"token {api_token}"})
- s.headers.update({"Accept": "application/json"})
- s.headers.update({"Content-Type": "application/json"})
-
- not_done = True
- page = 1
- while not_done:
- url = f"https://git.fcuny.net/api/v1/user/repos?page={page}&limit=10"
- res = s.get(
- url,
- timeout=5,
- )
- res.raise_for_status()
-
- repos = res.json()
- if len(repos) == 0:
- not_done = False
- else:
- page = page + 1
-
- for repo in repos:
- if repo.get("owner").get("login") == "attic":
- if repo.get("archived") is False:
- name = repo.get("name")
- data = {"archived": True}
- res = s.patch(
- f"https://git.fcuny.net/api/v1/repos/attic/{name}", json=data
- )
- res.raise_for_status()
- print(f"set {name} to archived: {res.status_code}")
-
-
-if __name__ == "__main__":
- argp = argparse.ArgumentParser()
- argp.add_argument("-t", "--token-file", nargs=1, type=argparse.FileType("r"))
-
- args = argp.parse_args()
- api_token = args.token_file[0].readline().strip()
-
- main(api_token)
diff --git a/packages/import-gh-to-gitea/delete-gh-repositories.py b/packages/import-gh-to-gitea/delete-gh-repositories.py
deleted file mode 100755
index b87c0f6..0000000
--- a/packages/import-gh-to-gitea/delete-gh-repositories.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env python3.10
-
-import argparse
-
-import requests
-
-
-def main(gitea_api_token, gh_api_token):
- gitea = requests.Session()
- gitea.headers.update({"Authorization": f"token {gitea_api_token}"})
- gitea.headers.update({"Accept": "application/json"})
- gitea.headers.update({"Content-Type": "application/json"})
-
- not_done = True
- page = 1
-
- gitea_repos = []
- while not_done:
- url = f"https://git.fcuny.net/api/v1/user/repos?page={page}&limit=10"
- res = gitea.get(
- url,
- timeout=5,
- )
- res.raise_for_status()
-
- repos = res.json()
- if len(repos) == 0:
- not_done = False
- else:
- page = page + 1
-
- for repo in repos:
- name = repo.get("name")
- gitea_repos.append(name)
-
- github = requests.Session()
- github.headers.update({"Authorization": f"token {gh_api_token}"})
- github.headers.update({"Accept": "application/vnd.github.v3+json"})
-
- not_done = True
- page = 1
- github_repos = []
- while not_done:
- url = f"https://api.github.com/user/repos?page={page}&type=all"
- res = github.get(
- url,
- timeout=5,
- )
- res.raise_for_status()
- repos = res.json()
- if len(repos) == 0:
- not_done = False
- else:
- page = page + 1
-
- for repo in repos:
- name = repo.get("name")
- if (
- repo.get("owner").get("login") == "fcuny"
- and repo.get("private") == True
- ):
- github_repos.append(name)
-
- for repo in github_repos:
- if repo in gitea_repos:
- url = f"https://api.github.com/repos/fcuny/{repo}"
- print(f"deleting {url}")
- res = github.delete(
- url,
- timeout=5,
- )
- res.raise_for_status()
-
-
-if __name__ == "__main__":
- argp = argparse.ArgumentParser()
- argp.add_argument("-t", "--gt-file", nargs=1, type=argparse.FileType("r"))
- argp.add_argument("-T", "--gh-file", nargs=1, type=argparse.FileType("r"))
-
- args = argp.parse_args()
- gitea_api_token = args.gt_file[0].readline().strip()
- github_api_token = args.gh_file[0].readline().strip()
-
- main(gitea_api_token, github_api_token)
diff --git a/packages/import-gh-to-gitea/import-gh-to-gitea.py b/packages/import-gh-to-gitea/import-gh-to-gitea.py
deleted file mode 100755
index b59c8eb..0000000
--- a/packages/import-gh-to-gitea/import-gh-to-gitea.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python3
-
-
-import argparse
-
-import requests
-
-
-def main(gh_api_token, gitea_api_token):
- s = requests.Session()
- s.headers.update({"Authorization": f"token {gh_api_token}"})
- s.headers.update({"Accept": "application/vnd.github.v3+json"})
-
- # hardcoded number of items per page, pagination is not handled.
- res = s.get("https://api.github.com/user/repos?per_page=200&type=all", timeout=5)
- res.raise_for_status()
-
- repos = res.json()
-
- gts = requests.Session()
- gts.headers.update({"Accept": "application/json"})
- gts.headers.update({"Content-Type": "application/json"})
- gts.headers.update({"Authorization": f"token {gitea_api_token}"})
- for repo in repos:
- # archived projects go to the attic.
- owner = ""
- if repo.get("archived"):
- owner = "attic"
- else:
- owner = "fcuny"
-
- data = {
- "auth_username": "fcuny",
- "auth_token": gh_api_token,
- "clone_addr": repo.get("html_url"),
- "mirror": False,
- "private": repo.get("private"),
- "repo_name": repo.get("name"),
- "repo_owner": owner,
- "service": "git",
- "description": repo.get("description"),
- }
- print(f"importing {data['repo_name']} from {data['clone_addr']}")
- res = gts.post(
- "https://git.fcuny.net/api/v1/repos/migrate",
- json=data,
- )
- try:
- res.raise_for_status()
- except Exception as e:
- print(f"failed for {data['repo_name']} with {e}")
-
-
-if __name__ == "__main__":
- argp = argparse.ArgumentParser()
- argp.add_argument("-g", "--gh-token-file", nargs=1, type=argparse.FileType("r"))
- argp.add_argument("-G", "--gitea-token-file", nargs=1, type=argparse.FileType("r"))
- args = argp.parse_args()
-
- gh_api_token = args.gh_token_file[0].readline().strip()
- gitea_api_token = args.gitea_token_file[0].readline().strip()
- main(gh_api_token, gitea_api_token)
diff --git a/packages/music-organizer/README.org b/packages/music-organizer/README.org
deleted file mode 100644
index a42a196..0000000
--- a/packages/music-organizer/README.org
+++ /dev/null
@@ -1,21 +0,0 @@
-#+TITLE: music organizer
-
-the tool takes a couple of arguments:
-- ~-dest~: where will the music be stored
-- a list of directories to scan
-
-all files that have tags that can be read will be processed and moved to the specified destination.
-
-files are organized like this: ={artist}/{album}/{track number} {track title}.{track format}=
-
-the tool ensures that files are not already present in the destination. if there's already a file with the same name, it checks that the md5 sum of the files are identical. if they are not, it logs a message.
-
-* build
-#+BEGIN_SRC sh
-go build
-#+END_SRC
-
-* install
-#+BEGIN_SRC sh
-go install
-#+END_SRC
diff --git a/packages/music-organizer/default.nix b/packages/music-organizer/default.nix
deleted file mode 100644
index 1242e34..0000000
--- a/packages/music-organizer/default.nix
+++ /dev/null
@@ -1,15 +0,0 @@
-{ pkgs, ... }:
-
-pkgs.buildGoModule rec {
- name = "music-organizer";
- src = ./.;
- vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
- nativeBuildInputs = with pkgs; [ go ];
-
- meta = with pkgs.lib; {
- description = "CLI to organize my music in folders.";
- license = licenses.mit;
- platforms = platforms.linux;
- maintainers = [ ];
- };
-}
diff --git a/packages/music-organizer/go.mod b/packages/music-organizer/go.mod
deleted file mode 100644
index ba9a1b8..0000000
--- a/packages/music-organizer/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module golang.fcuny.org/music-organizer
-
-go 1.17
-
-require github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b
diff --git a/packages/music-organizer/go.sum b/packages/music-organizer/go.sum
deleted file mode 100644
index 3383f0e..0000000
--- a/packages/music-organizer/go.sum
+++ /dev/null
@@ -1,4 +0,0 @@
-github.com/dhowden/itl v0.0.0-20170329215456-9fbe21093131/go.mod h1:eVWQJVQ67aMvYhpkDwaH2Goy2vo6v8JCMfGXfQ9sPtw=
-github.com/dhowden/plist v0.0.0-20141002110153-5db6e0d9931a/go.mod h1:sLjdR6uwx3L6/Py8F+QgAfeiuY87xuYGwCDqRFrvCzw=
-github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b h1:TG8R5ZZgd1Sj7iFWnkk5dNy94RG8fP8M4l24UYR8/HY=
-github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b/go.mod h1:Z3Lomva4pyMWYezjMAU5QWRh0p1VvO4199OHlFnyKkM=
diff --git a/packages/music-organizer/main.go b/packages/music-organizer/main.go
deleted file mode 100644
index 253afef..0000000
--- a/packages/music-organizer/main.go
+++ /dev/null
@@ -1,271 +0,0 @@
-package main
-
-import (
- "archive/zip"
- "crypto/md5"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/dhowden/tag"
-)
-
-const (
- // the max lenght for a track can only be 255 characters minus 3 for the
- // track number (followed by a space), and 4 for the format. The limit of
- // 255 is coming from HFS+.
- TrackTitleMaxLenght = 255 - 3 - 4
-)
-
-var musicDest = flag.String("dest", fmt.Sprintf("%s/media/music", os.Getenv("HOME")), "where to store the music")
-
-// replace slashes with dashes
-func stripSlash(s string) string {
- return strings.ReplaceAll(s, "/", "-")
-}
-
-// return the name of the artist, album and the title of the track
-// the title of the track has the following format:
-//
-// {track #} {track title}.{track format}
-func generatePath(m tag.Metadata) (string, string, string) {
- var artist, album, title string
- var track int
-
- // if there's no artist, let's fallback to "Unknown Artists"
- if len(m.Artist()) == 0 {
- artist = "Unknown Artists"
- } else {
- artist = stripSlash(m.Artist())
- }
-
- // if there's no album name, let's fallback to "Unknown Album"
- if len(m.Album()) == 0 {
- album = "Unknown Album"
- } else {
- album = stripSlash(m.Album())
- }
-
- track, _ = m.Track()
-
- // ok, there must be a better way
- format := strings.ToLower(string(m.FileType()))
-
- title = fmt.Sprintf("%02d %s.%s", track, stripSlash(m.Title()), format)
- if len(title) > TrackTitleMaxLenght {
- r := []rune(title)
- title = string(r[0:255])
- }
-
- return artist, album, title
-}
-
-// create all the required directories. if we fail to create one, we die
-func makeParents(path string) error {
- if err := os.MkdirAll(path, 0o777); err != nil {
- return fmt.Errorf("failed to create %s: %v", path, err)
- }
- return nil
-}
-
-func md5sum(path string) (string, error) {
- var sum string
- f, err := os.Open(path)
- if err != nil {
- return sum, err
- }
-
- defer f.Close()
-
- h := md5.New()
- if _, err := io.Copy(h, f); err != nil {
- return sum, err
- }
- sum = hex.EncodeToString(h.Sum(nil)[:16])
- return sum, nil
-}
-
-func makeCopy(src, dst string) error {
- f, err := os.Open(src)
- if err != nil {
- return err
- }
- defer f.Close()
-
- t, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0o666)
- if err != nil {
- return err
- }
- defer t.Close()
-
- _, err = io.Copy(t, f)
- if err != nil {
- return err
- }
- log.Printf("copied %s → %s\n", src, dst)
- return nil
-}
-
-// ensure the file is named correctly and is moved to the correct destination
-// before we can do that, we need to:
-// 1. check if the track already exists, if it does, does it have the same md5 ?
-// if they are similar, we skip them. if they are not, we log and don't do
-// anything
-// 2. we can move the file to the destination
-// 3. we can delete the original file
-func renameFile(originalPath string, artist, album, title string) error {
- directories := filepath.Join(*musicDest, artist, album)
- destination := filepath.Join(directories, title)
-
- // check if the file is present
- _, err := os.Stat(destination)
- if err == nil {
- var originalSum, destinationSum string
- if originalSum, err = md5sum(originalPath); err != nil {
- return err
- }
- if destinationSum, err = md5sum(destination); err != nil {
- return err
- }
-
- if destinationSum != originalSum {
- log.Printf("md5 sum are different: %s(%s) %s(%s)", originalPath, originalSum, destination, destinationSum)
- }
- return nil
- }
-
- if err := makeParents(directories); err != nil {
- return err
- }
-
- if err := makeCopy(originalPath, destination); err != nil {
- return err
- }
-
- // TODO delete original file
- // os.Remove(originalPath)
- return nil
-}
-
-// we try to open any files and read the metadata.
-// if the file has metadata we can read, we will try to move the file to the
-// correct destination
-func processFile(path string) error {
- f, err := os.Open(path)
- if err != nil {
- return err
- }
-
- defer f.Close()
- m, err := tag.ReadFrom(f)
- if err != nil {
- // this is fine, this might not be a music file
- log.Printf("SKIP failed to read tags from %s: %v", path, err)
- return nil
- }
-
- var artist, album, title string
- artist, album, title = generatePath(m)
- if err := renameFile(path, artist, album, title); err != nil {
- return fmt.Errorf("failed to move %s: %v", path, err)
- }
- return nil
-}
-
-func processPath(path string, f os.FileInfo, err error) error {
- if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
- if err := processFile(path); err != nil {
- return err
- }
- }
- return nil
-}
-
-// unzip takes two paths, a source and destination. The source is the
-// name of the archive and we will extract the content into the
-// destination directory. The destination directory has to already
-// exists, we are not going to create it here or delete it at the end.
-func unzip(src, dst string) error {
- r, err := zip.OpenReader(src)
- if err != nil {
- return err
- }
-
- defer r.Close()
-
- for _, f := range r.File {
- fpath := filepath.Join(dst, f.Name)
- outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
- if err != nil {
- return err
- }
-
- rc, err := f.Open()
- if err != nil {
- return err
- }
-
- _, err = io.Copy(outFile, rc)
- if err != nil {
- log.Printf("failed to copy %s: %s", outFile.Name(), err)
- }
-
- outFile.Close()
- rc.Close()
- }
- return nil
-}
-
-func main() {
- flag.Parse()
-
- if *musicDest == "" {
- log.Fatal("-dest is required")
- }
-
- paths := make([]string, flag.NArg())
-
- // For our temp directory, we use what ever the value of
- // XDG_RUNTIME_DIR is. If the value is unset, we will default to
- // the system default temp directory.
- tmpDir := os.Getenv("XDG_RUNTIME_DIR")
-
- for i, d := range flag.Args() {
- if filepath.Ext(d) == ".zip" {
- // If we have an extension and it's '.zip', we consider the
- // path to be an archive. In this case we want to create a new
- // temporary directory and extract the content of the archive
- // in that path. The temporary directory is removed once we're
- // done.
- out, err := ioutil.TempDir(tmpDir, "music-organizer")
- if err != nil {
- log.Printf("failed to create a temp directory to extract %s: %v", d, err)
- continue
- }
- defer os.RemoveAll(out)
-
- if err := unzip(d, out); err != nil {
- log.Printf("failed to extract %s: %v", d, err)
- continue
- }
- paths[i] = out
- } else {
- paths[i] = d
- }
- }
-
- for _, d := range paths {
- // XXX deal with filenames that are too long
- // scan the directory and try to find any file that we want to move
- err := filepath.Walk(d, processPath)
- if err != nil {
- log.Fatalf("error while processing files: %v", err)
- }
- }
-}
diff --git a/packages/numap/README.org b/packages/numap/README.org
deleted file mode 100644
index c7941b1..0000000
--- a/packages/numap/README.org
+++ /dev/null
@@ -1,47 +0,0 @@
-#+TITLE: numap
-
-Print the NUMA topology of a host.
-
-* Usage
-#+BEGIN_SRC sh
-./numap |jq .
-{
- "node0": {
- "name": "node0",
- "path": "/sys/devices/system/node/node0",
- "cpulist": "0-19,40-59",
- "pci_devices": [
- {
- "vendor": "Mellanox Technologies",
- "name": "MT27710 Family [ConnectX-4 Lx]"
- },
- {
- "vendor": "Mellanox Technologies",
- "name": "MT27710 Family [ConnectX-4 Lx]"
- }
- ]
- },
- "node1": {
- "name": "node1",
- "path": "/sys/devices/system/node/node1",
- "cpulist": "20-39,60-79",
- "pci_devices": [
- {
- "vendor": "Intel Corporation",
- "name": "NVMe Datacenter SSD [3DNAND, Beta Rock Controller]"
- }
- ]
- }
-}
-#+END_SRC
-
-The command will scan the host to find the NUMA nodes, and all the PCI devices, and map the PCI devices back to the NUMA node.
-
-It also provides a way to see the list of CPUs attached to the node.
-
-* Limitations
-** Device class
-For now only the following classes of hardware are cared for:
-- NVMe
-- network
-- GPU
diff --git a/packages/numap/go.mod b/packages/numap/go.mod
deleted file mode 100644
index 92b1885..0000000
--- a/packages/numap/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module golang.fcuny.net/numap
-
-go 1.17
diff --git a/packages/numap/internal/hwids/hwids.go b/packages/numap/internal/hwids/hwids.go
deleted file mode 100644
index 6aa9d8a..0000000
--- a/packages/numap/internal/hwids/hwids.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package hwids
-
-import (
- "bufio"
- "fmt"
- "os"
- "strings"
-)
-
-var pciPath = []string{
- "/usr/share/hwdata/pci.ids",
- "/usr/share/misc/pci.ids",
-}
-
-type PCIType int
-
-const (
- PCIVendor PCIType = iota
- PCIDevice
- PCISubsystem
-)
-
-type PciDevices map[uint16][]PciDevice
-
-// PciDevice represents a PCI device
-type PciDevice struct {
- Type PCIType
- Vendor, Device uint16
- SubVendor, SubDevice uint16
- VendorName, DeviceName string
- SubName string
-}
-
-// Load load the hardware database for PCI devices and return a map of
-// vendor -> list of devices.
-func Load() (PciDevices, error) {
- // if the environment variable HWDATAPATH is set, we add it to the
- // list of paths we check for the hardware database.
- extraPath := os.Getenv("HWDATA")
- if extraPath != "" {
- pciPath = append(pciPath, extraPath)
- }
-
- for _, f := range pciPath {
- fh, err := os.Open(f)
- if err != nil {
- continue
- }
- defer fh.Close()
- return parse(fh)
- }
- return PciDevices{}, fmt.Errorf("hwids: could not find a pci.ids file")
-}
-
-func parse(f *os.File) (PciDevices, error) {
- devices := make(PciDevices)
-
- s := bufio.NewScanner(f)
-
- // this is to keep track of the current device. The format of the
- // file is as follow:
- // vendor vendor_name
- // device device_name <-- single tab
- // subvendor subdevice subsystem_name <-- two tabs
- // the variable is to keep track of the current vendor / device
- cur := PciDevice{}
-
- for s.Scan() {
- l := s.Text()
- // skip empty lines or lines that are a comment
- if len(l) == 0 || l[0] == '#' {
- continue
- }
- // lines starting with a C are the classes definitions, and
- // they are at the end of the file, which means we're done
- // parsing the devices
- if l[0] == 'C' {
- break
- }
-
- parts := strings.SplitN(l, " ", 2)
- if len(parts) != 2 {
- return devices, fmt.Errorf("hwids: malformed PCI ID line (missing ID separator): %s", l)
- }
-
- ids, name := parts[0], parts[1]
- if len(ids) < 2 || len(name) == 0 {
- return devices, fmt.Errorf("hwids: malformed PCI ID line (empty ID or name): %s", l)
- }
-
- cur.Type = PCIVendor
-
- if ids[0] == '\t' {
- if ids[1] == '\t' {
- cur.Type = PCISubsystem
- } else {
- cur.Type = PCIDevice
- }
- }
-
- var err error
- switch cur.Type {
- case PCIVendor:
- _, err = fmt.Sscanf(ids, "%x", &cur.Vendor)
- cur.VendorName = name
- case PCIDevice:
- _, err = fmt.Sscanf(ids, "%x", &cur.Device)
- cur.DeviceName = name
- case PCISubsystem:
- _, err = fmt.Sscanf(ids, "%x %x", &cur.SubVendor, &cur.SubDevice)
- cur.SubName = name
- }
-
- if err != nil {
- return devices, fmt.Errorf("hwids: malformed PCI ID line: %s: %v", l, err)
- }
-
- // This is to reset the state when we are moving to a
- // different vendor or device
- switch cur.Type {
- case PCIVendor:
- cur.Device = 0
- cur.DeviceName = ""
- fallthrough
- case PCIDevice:
- cur.SubVendor = 0
- cur.SubDevice = 0
- cur.SubName = ""
- }
-
- _, ok := devices[cur.Vendor]
- if ok {
- _devices := devices[cur.Vendor]
- _devices = append(_devices, cur)
- devices[cur.Vendor] = _devices
-
- } else {
- _devices := []PciDevice{cur}
- devices[cur.Vendor] = _devices
- }
- }
-
- if err := s.Err(); err != nil {
- return devices, fmt.Errorf("hwids: failed to read PCI ID line: %v", err)
- }
-
- return devices, nil
-}
diff --git a/packages/numap/internal/sysfs/parse.go b/packages/numap/internal/sysfs/parse.go
deleted file mode 100644
index d518653..0000000
--- a/packages/numap/internal/sysfs/parse.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package sysfs
-
-import (
- "io/ioutil"
- "strconv"
- "strings"
-)
-
-// ContentUint64 parses the content of a file in sysfs, and convert
-// from hex to uint64.
-func ContentUint64(path string) (uint64, error) {
- content, err := ioutil.ReadFile(path)
- if err != nil {
- return 0, err
- }
- result, err := strconv.ParseUint(strings.TrimSpace(string(content)), 0, 64)
- if err != nil {
- return 0, err
- }
- return result, nil
-}
diff --git a/packages/numap/internal/sysfs/pci.go b/packages/numap/internal/sysfs/pci.go
deleted file mode 100644
index 9e714b1..0000000
--- a/packages/numap/internal/sysfs/pci.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package sysfs
-
-import (
- "fmt"
- "io/ioutil"
- "path"
- "path/filepath"
- "strconv"
- "strings"
-)
-
-const (
- sysFsPCIDevicesPath = "/sys/bus/pci/devices/"
-)
-
-type PCIDevice struct {
- NumaNode int
- ID string
- Device, Vendor uint64
- SubVendor, SubDevice uint64
- Class uint64
- MSIs []int
-}
-
-func ScanPCIDevices() []PCIDevice {
- devices, err := ioutil.ReadDir(sysFsPCIDevicesPath)
- if err != nil {
- panic(err)
- }
-
- pciDevices := []PCIDevice{}
-
- for _, device := range devices {
- dpath := filepath.Join(sysFsPCIDevicesPath, device.Name())
- pcid, err := NewPCIDevice(dpath, device.Name())
- if err != nil {
- panic(err)
- }
- pciDevices = append(pciDevices, pcid)
- }
- return pciDevices
-}
-
-func getPCIDeviceClass(path string) (uint64, error) {
- return ContentUint64(filepath.Join(path, "class"))
-}
-
-func getPCIDeviceVendor(path string) (uint64, error) {
- return ContentUint64(filepath.Join(path, "vendor"))
-}
-
-func getPCIDeviceId(path string) (uint64, error) {
- return ContentUint64(filepath.Join(path, "device"))
-}
-
-func getPCIDeviceSubsystemDevice(path string) (uint64, error) {
- return ContentUint64(filepath.Join(path, "subsystem_device"))
-}
-
-func getPCIDeviceSubsystemVendor(path string) (uint64, error) {
- return ContentUint64(filepath.Join(path, "subsystem_vendor"))
-}
-
-func getPCIDeviceNumaNode(path string) int {
- content, err := ioutil.ReadFile(filepath.Join(path, "numa_node"))
- if err != nil {
- panic(err)
- }
- nodeNum, err := strconv.Atoi(strings.TrimSpace(string(content)))
- if err != nil {
- panic(err)
- }
- return nodeNum
-}
-
-func getPCIDeviceMSIx(p string) []int {
- g := fmt.Sprintf("%s/*", filepath.Join(p, "msi_irqs"))
- files, err := filepath.Glob(g)
- if err != nil {
- panic(err)
- }
- if len(files) == 0 {
- return []int{}
- }
-
- msix := []int{}
-
- for _, f := range files {
- content, err := ioutil.ReadFile(f)
- if err != nil {
- panic(err)
- }
- if strings.TrimSpace(string(content)) == "msix" {
- base := path.Base(f)
- v, err := strconv.Atoi(base)
- if err != nil {
- panic(err)
- }
- msix = append(msix, v)
- }
- }
- return msix
-}
-
-func NewPCIDevice(path, name string) (PCIDevice, error) {
- nodeNum := getPCIDeviceNumaNode(path)
-
- device, err := getPCIDeviceId(path)
- if err != nil {
- return PCIDevice{}, err
- }
-
- vendor, err := getPCIDeviceVendor(path)
- if err != nil {
- return PCIDevice{}, err
- }
-
- subvendor, err := getPCIDeviceSubsystemVendor(path)
- if err != nil {
- return PCIDevice{}, err
- }
-
- subdevice, err := getPCIDeviceSubsystemDevice(path)
- if err != nil {
- return PCIDevice{}, err
- }
-
- deviceClass, err := getPCIDeviceClass(path)
- if err != nil {
- return PCIDevice{}, err
- }
-
- msix := getPCIDeviceMSIx(path)
-
- return PCIDevice{
- ID: name,
- Device: device,
- Class: deviceClass,
- NumaNode: nodeNum,
- Vendor: vendor,
- SubVendor: subvendor,
- SubDevice: subdevice,
- MSIs: msix,
- }, nil
-}
diff --git a/packages/numap/numa.go b/packages/numap/numa.go
deleted file mode 100644
index 402ea1d..0000000
--- a/packages/numap/numa.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package main
-
-import (
- "fmt"
- "io/ioutil"
- "path"
- "path/filepath"
- "strings"
-
- "golang.fcuny.net/numap/internal/hwids"
- "golang.fcuny.net/numap/internal/sysfs"
-)
-
-const (
- node_root = "/sys/devices/system/node/node*"
- CLASS_NVMe = 67586
- CLASS_ETHERNET = 131072
- CLASS_GPU = 197120
-)
-
-type node struct {
- Name string `json:"name"`
- Path string `json:"path"`
- CpuList string `json:"cpulist"`
- PCIDevices []PCIDevice `json:"pci_devices"`
-}
-
-type PCIDevice struct {
- Vendor string `json:"vendor"`
- Name string `json:"name"`
-}
-
-func findNodes(hwdb hwids.PciDevices) (map[string]node, error) {
- nodes := make(map[string]node)
-
- files, err := filepath.Glob(node_root)
- if err != nil {
- return nil, fmt.Errorf("Failed to find NUMA nodes under %s: %+v", node_root, err)
- }
- if len(files) == 0 {
- return nil, fmt.Errorf("Could not find NUMA node in %s", node_root)
- }
-
- for _, f := range files {
- n, err := newNode(f)
- if err != nil {
- return make(map[string]node), err
- }
- nodes[n.Name] = n
- }
-
- r, err := mapPCIDevicesToNumaNode(hwdb)
- if err != nil {
- panic(err)
- }
- for k, v := range r {
- nodeName := fmt.Sprintf("node%d", k)
- n := nodes[nodeName]
- n.PCIDevices = v
- nodes[nodeName] = n
- }
- return nodes, nil
-}
-
-func mapPCIDevicesToNumaNode(hwdb hwids.PciDevices) (map[int][]PCIDevice, error) {
- devices := sysfs.ScanPCIDevices()
- r := map[int][]PCIDevice{}
-
- for _, d := range devices {
- if d.Class == CLASS_NVMe || d.Class == CLASS_ETHERNET || d.Class == CLASS_GPU {
- _, ok := hwdb[uint16(d.Vendor)]
- if ok {
- desc := hwdb[uint16(d.Vendor)]
- var vendor, name string
- for _, m := range desc {
- if uint64(m.Device) == d.Device && uint64(m.Vendor) == d.Vendor {
- vendor = m.VendorName
- name = m.DeviceName
- break
- }
- }
- pciDevice := PCIDevice{
- Vendor: vendor,
- Name: name,
- }
- r[d.NumaNode] = append(r[d.NumaNode], pciDevice)
- }
- }
- }
- return r, nil
-}
-
-func newNode(p string) (node, error) {
- _, name := path.Split(p)
-
- cpulist, err := cpuList(p)
- if err != nil {
- return node{}, err
- }
-
- return node{
- Name: name,
- Path: p,
- CpuList: cpulist,
- PCIDevices: []PCIDevice{},
- }, nil
-}
-
-func cpuList(p string) (string, error) {
- lpath := filepath.Join(p, "cpulist")
- c, err := ioutil.ReadFile(lpath)
- if err != nil {
- return "", fmt.Errorf("Failed to open %s: %+v", lpath, err)
- }
- return strings.TrimRight(string(c), "\n"), nil
-}
diff --git a/packages/numap/numap.go b/packages/numap/numap.go
deleted file mode 100644
index c65f1f0..0000000
--- a/packages/numap/numap.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "os"
-
- "golang.fcuny.net/numap/internal/hwids"
-)
-
-func main() {
- hwdb, err := hwids.Load()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-
- nodes, err := findNodes(hwdb)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-
- out, err := json.Marshal(nodes)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-
- fmt.Println(string(out))
-}
diff --git a/packages/scheddomain/go.mod b/packages/scheddomain/go.mod
deleted file mode 100644
index afbc83a..0000000
--- a/packages/scheddomain/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module golang.fcuny.net/scheddomain
-
-go 1.17
diff --git a/packages/scheddomain/main.go b/packages/scheddomain/main.go
deleted file mode 100644
index 1d0f5d3..0000000
--- a/packages/scheddomain/main.go
+++ /dev/null
@@ -1,153 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "strconv"
- "strings"
-)
-
-// https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+/v4.17/include/linux/sched/topology.h#20
-var SDFlags = map[string]uint64{
- "SD_LOAD_BALANCE": 0x0001,
- "SD_BALANCE_NEWIDLE": 0x0002,
- "SD_BALANCE_EXEC": 0x0004,
- "SD_BALANCE_FORK": 0x0008,
- "SD_BALANCE_WAKE": 0x0010,
- "SD_WAKE_AFFINE": 0x0020,
- "SD_ASYM_CPUCAPACITY": 0x0040,
- "SD_SHARE_CPUCAPACITY": 0x0080,
- "SD_SHARE_POWERDOMAIN": 0x0100,
- "SD_SHARE_PKG_RESOURCES": 0x0200,
- "SD_SERIALIZE": 0x0400,
- "SD_ASYM_PACKING": 0x0800,
- "SD_PREFER_SIBLING": 0x1000,
- "SD_OVERLAP": 0x2000,
- "SD_NUMA": 0x4000,
-}
-
-type Scheduler map[string][]Domain
-
-type Domain struct {
- Name string `json:"name"`
- Type string `json:"type"`
- Flags []string `json:"flags"`
- Indexes map[string]string `json:"indexes"`
-}
-
-func main() {
- cpus, err := CPUs()
- if err != nil {
- fmt.Fprint(os.Stderr, err)
- os.Exit(1)
- }
-
- if len(cpus) == 0 {
- fmt.Fprint(os.Stderr, "there is no scheduler domains\n")
- os.Exit(1)
- }
-
- sched := Scheduler{}
- for _, cpu := range cpus {
- _, cpuID := path.Split(cpu)
- domains, err := domains(cpu)
- if err != nil {
- fmt.Fprint(os.Stderr, err)
- os.Exit(1)
- }
- sched[cpuID] = domains
- }
- out, err := json.Marshal(sched)
- if err != nil {
- fmt.Fprint(os.Stderr, err)
- os.Exit(1)
- }
- fmt.Println(string(out))
-}
-
-func domains(cpuPath string) ([]Domain, error) {
- domainPath := fmt.Sprintf("%s/domain*", cpuPath)
- availDomains, err := filepath.Glob(domainPath)
- if err != nil {
- return nil, fmt.Errorf("failed to get domains under %s: %v", cpuPath, err)
- }
-
- domains := []Domain{}
-
- if len(availDomains) == 0 {
- return domains, nil
- }
-
- for _, d := range availDomains {
- _, dName := path.Split(d)
- dType := getContent(d, "name")
- flags, err := domainFlags(d)
- if err != nil {
- return nil, err
- }
- indexes := domainIndexes(d)
-
- domain := Domain{
- Name: dName,
- Type: dType,
- Flags: flags,
- Indexes: indexes,
- }
- domains = append(domains, domain)
- }
- return domains, nil
-}
-
-func domainFlags(path string) ([]string, error) {
- flagPath := fmt.Sprintf("%s/flags", path)
-
- content, err := ioutil.ReadFile(flagPath)
- if err != nil {
- return nil, fmt.Errorf("failed to read %s: %v", flagPath, err)
- }
-
- flags, err := strconv.ParseUint(strings.TrimSpace(string(content)), 0, 64)
- if err != nil {
- return nil, fmt.Errorf("failed to convert flags %s: %v", flagPath, err)
- }
-
- supportedFlags := []string{}
- for k, v := range SDFlags {
- if flags&v > 0 {
- supportedFlags = append(supportedFlags, k)
- }
- }
- return supportedFlags, nil
-}
-
-func domainIndexes(path string) map[string]string {
- indexes := map[string]string{
- "busy": getContent(path, "busy_idx"),
- "idle": getContent(path, "idle_idx"),
- "new_idle": getContent(path, "newidle_idx"),
- "wake": getContent(path, "wake_idx"),
- "fork_exec": getContent(path, "forkexec_idx"),
- }
- return indexes
-}
-
-func getContent(path, fileName string) string {
- domainName := fmt.Sprintf("%s/%s", path, fileName)
- name, err := ioutil.ReadFile(domainName)
- if err != nil {
- return ""
- }
- return strings.TrimSpace(string(name))
-}
-
-func CPUs() ([]string, error) {
- cpus, err := filepath.Glob("/proc/sys/kernel/sched_domain/cpu*")
- if err != nil {
- return nil, fmt.Errorf("failed to get a list of cpus: %v", err)
- }
- return cpus, nil
-}
diff --git a/packages/schedlatency/go.mod b/packages/schedlatency/go.mod
deleted file mode 100644
index 9a073ac..0000000
--- a/packages/schedlatency/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module golang.fcuny.net/schedlatency
-
-go 1.17
diff --git a/packages/schedlatency/main.go b/packages/schedlatency/main.go
deleted file mode 100644
index 7dd709e..0000000
--- a/packages/schedlatency/main.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "strconv"
- "strings"
- "time"
-)
-
-type SchedStat struct {
- Pid int `json:"pid"`
- RunTicks int `json:"run_ticks"`
- WaitTicks int `json:"wait_ticks"`
- SlicesRan int `json:"ran_slices"`
- AverageRun float64 `json:"avg_run"`
- AverageWait float64 `json:"avg_wait"`
-}
-
-func usage() {
- fmt.Fprintf(os.Stderr, "usage: %s <pid>\n", os.Args[0])
-}
-
-func main() {
- if len(os.Args) == 1 {
- usage()
- os.Exit(1)
- }
-
- input := os.Args[1]
- pid, err := strconv.Atoi(input)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to convert %s to a PID: %v", input, err)
- os.Exit(1)
- }
-
- p := Proc{
- PID: pid,
- }
- oran := 0
- owait_ticks := 0
- orun_ticks := 0
- for {
- stat, err := p.SchedStat()
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to get schedstat for %d: %v\n", p.PID, err)
- os.Exit(1)
- }
- diff := stat.SlicesRan - oran
- var avgrun, avgwait float64
-
- if diff > 0 {
- avgrun = float64((stat.RunTicks - orun_ticks) / diff)
- avgwait = float64((stat.WaitTicks - owait_ticks) / diff)
- } else {
- avgrun = 0
- avgwait = 0
- }
-
- stat.AverageRun = avgrun
- stat.AverageWait = avgwait
-
- out, err := json.Marshal(stat)
- if err != nil {
- fmt.Fprintln(err)
- os.Exit(1)
- }
- fmt.Println(string(out))
- oran = stat.SlicesRan
- orun_ticks = stat.RunTicks
- owait_ticks = stat.WaitTicks
- time.Sleep(5 * time.Second)
- }
-}
-
-// This the the path that contains the scheduler statistics.
-// Note that they are not populated unless the value for
-// /proc/sys/kernel/sched_schedstats is 1
-const procSchedStat = "/proc/schedstat"
-
-var idleness = []string{"idle", "busy", "newlyIdle"}
-
-type ProcSchedStat struct {
- RunTicks int `json:"run_ticks"`
- WaitTicks int `json:"wait_ticks"`
- SlicesRan int `json:"ran_slices"`
- AverageRun float64 `json:"avg_run"`
- AverageWait float64 `json:"avg_wait"`
-}
-
-// SchedCPUStat contains the load balancer statistics for a CPU.
-type SchedCPUStat struct {
- YieldCount uint64 `json:"yield_count"`
- SchedulerCount uint64 `json:"sched_count"`
- SchedulerGoIdle uint64 `json:"sched_go_idle"`
- TryToWakeUp uint64 `json:"try_to_wake"`
- TryToWakeUpLocal uint64 `json:"try_to_wake_local"`
- Running uint64 `json:"running"`
- Waiting uint64 `json:"waiting"`
- Slices uint64 `json:"slices"`
- Domains map[string]SchedDomain `json:"domains"`
-}
-
-// SchedLoadBalance contains the load balancer statistics for a domain
-// in a given domain.
-type SchedLoadBalance struct {
- LBCount uint64 `json:"lb_count"`
- LBBalanced uint64 `json:"lb_balanced"`
- LBFailed uint64 `json:"lb_failed"`
- LBImbalanced uint64 `json:"lb_imbalanced"`
- LBGained uint64 `json:"lb_gained"`
- LBHotGain uint64 `json:"lb_hot_gain"`
- LBNoBusyQueue uint64 `json:"lb_no_busy_queue"`
- LBNoBusyGroup uint64 `json:"lb_no_busy_group"`
-}
-
-// SchedDomain contains the statistics for a domain.
-type SchedDomain struct {
- LoadBalancers map[string]SchedLoadBalance `json:"lbs"`
- ActiveLoadBalanceCount uint64 `json:"active_lb_count"`
- ActiveLoadBalanceFailed uint64 `json:"active_lb_failed"`
- ActiveLoadBalancePushed uint64 `json:"active_lb_pushed"`
- TryToWakeUpRemote uint64 `json:"try_to_wake_up_remote"`
- TryToWakeUpMoveAffine uint64 `json:"try_to_wake_up_move_affine"`
- TryToWakeUpMoveBalance uint64 `json:"try_to_wake_up_move_balance"`
-}
-
-// Proc provides information about a running process.
-type Proc struct {
- // The process ID.
- PID int
-}
-
-// SchedStat returns scheduler statistics for the process.
-// The information available are:
-// 1. time spent on the cpu
-// 2. time spent waiting on a runqueue
-// 3. # of timeslices run on this cpu
-func (p Proc) SchedStat() (ProcSchedStat, error) {
- path := fmt.Sprintf("/proc/%d/schedstat", p.PID)
- b, err := ioutil.ReadFile(path)
- if err != nil {
- return ProcSchedStat{}, err
- }
- content := string(b)
- stats := strings.Fields(content)
-
- run_ticks, err := strconv.Atoi(stats[0])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- wait_ticks, err := strconv.Atoi(stats[1])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- nran, err := strconv.Atoi(stats[2])
- if err != nil {
- return ProcSchedStat{}, err
- }
-
- stat := ProcSchedStat{
- RunTicks: run_ticks,
- WaitTicks: wait_ticks,
- SlicesRan: nran,
- }
- return stat, nil
-}
-
-// ReadSchedstat returns statistics from the scheduler.
-// Information about the statistics can be found at
-// https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html.
-func ReadSchedStat() (map[string]SchedCPUStat, error) {
- b, err := ioutil.ReadFile(procSchedStat)
- if err != nil {
- return nil, fmt.Errorf("procfs: failed to open %s: %v", procSchedStat, err)
- }
- content := string(b)
-
- cpus := map[string]SchedCPUStat{}
-
- lines := strings.Split(content, "\n")
-
- var currentCpu string
-
- // The first line is the version of the stats
- // TODO(fcuny): we should check which version is used, because the
- // format changes.
- for _, line := range lines[2:] {
- // The format is as follow:
- // cpu<N> 1 2 3 4 5 6 7 8 9
- // domain<N> <cpumask> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
- if strings.HasPrefix(line, "cpu") {
- // meaning of the fields: https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html#cpu-statistics
- fields := strings.Fields(line)
- cpuStat := SchedCPUStat{
- YieldCount: convertField(fields[1]),
- SchedulerCount: convertField(fields[3]),
- SchedulerGoIdle: convertField(fields[4]),
- TryToWakeUp: convertField(fields[5]),
- TryToWakeUpLocal: convertField(fields[6]),
- Running: convertField(fields[7]),
- Waiting: convertField(fields[8]),
- Slices: convertField(fields[9]),
- Domains: map[string]SchedDomain{},
- }
- currentCpu = fields[0]
- cpus[currentCpu] = cpuStat
- } else if strings.HasPrefix(line, "domain") {
- // meaning of the fields: https://www.kernel.org/doc/html/latest/scheduler/sched-stats.html#domain-statistics
- fields := strings.Fields(line)
- i := 2
- lbs := map[string]SchedLoadBalance{}
- for _, idle := range idleness {
- lb := SchedLoadBalance{
- LBCount: convertField(fields[i]),
- LBBalanced: convertField(fields[i+1]),
- LBFailed: convertField(fields[i+2]),
- LBImbalanced: convertField(fields[i+3]),
- LBGained: convertField(fields[i+4]),
- LBHotGain: convertField(fields[i+5]),
- LBNoBusyQueue: convertField(fields[i+6]),
- LBNoBusyGroup: convertField(fields[i+7]),
- }
- i = i + 8
- lbs[idle] = lb
- }
- domain := SchedDomain{
- LoadBalancers: lbs,
- ActiveLoadBalanceCount: convertField(fields[26]),
- ActiveLoadBalanceFailed: convertField(fields[27]),
- ActiveLoadBalancePushed: convertField(fields[28]),
- TryToWakeUpRemote: convertField(fields[35]),
- TryToWakeUpMoveAffine: convertField(fields[36]),
- TryToWakeUpMoveBalance: convertField(fields[37]),
- }
- c := cpus[currentCpu]
- c.Domains[fields[0]] = domain
- cpus[currentCpu] = c
- }
- }
- return cpus, nil
-}
-
-func convertField(field string) uint64 {
- val, err := strconv.ParseUint(field, 10, 64)
- if err != nil {
- return 0
- }
- return val
-}