Compare commits
No commits in common. "6e0198de35e960e1d127e3be4a8ffca33fb4cff4" and "f96a911722d7999a84a9a0f2a7992993afb83b8a" have entirely different histories.
6e0198de35
...
f96a911722
46
.air.toml
@ -1,46 +0,0 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ."
|
||||
delay = 1000
|
||||
exclude_dir = ["tmp", "vendor", "testdata", "assets/node_modules"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
1408
assets/package-lock.json
generated
@ -1,7 +0,0 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"tailwind-dracula": "^1.1.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind variants;
|
||||
|
||||
@layer components {
|
||||
.notimpl {
|
||||
@apply italic font-light list-image-possible;
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["../view/**/*.gohtml"],
|
||||
theme: {
|
||||
extend: {
|
||||
listStyleImage: {
|
||||
never: 'url("/static/icons/list-never.svg")',
|
||||
accepted: 'url("/static/icons/list-accepted.svg")',
|
||||
possible: 'url("/static/icons/list-possible.svg")',
|
||||
informational: 'url("/static/icons/list-informational.svg")',
|
||||
},
|
||||
content: {
|
||||
link: 'url("/static/icons/link.svg")',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('tailwind-dracula')(),
|
||||
],
|
||||
}
|
||||
|
5
go.mod
@ -4,7 +4,4 @@ go 1.22.1
|
||||
|
||||
require libvirt.org/go/libvirt v1.10001.0
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||
gopkg.in/ffmt.v1 v1.5.6 // indirect
|
||||
)
|
||||
require gopkg.in/ffmt.v1 v1.5.6 // indirect
|
||||
|
2
go.sum
@ -1,5 +1,3 @@
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
gopkg.in/ffmt.v1 v1.5.6 h1:4Bu3riZp5sAIXW2T/18JM9BkwJLodurXFR0f7PXp+cw=
|
||||
gopkg.in/ffmt.v1 v1.5.6/go.mod h1:LssvGOZFiBGoBcobkTqnyh+uN1VzIRoibW+c0JI/Ha4=
|
||||
libvirt.org/go/libvirt v1.10001.0 h1:lEVDNE7xfzmZXiDEGIS8NvJSuaz11OjRXw+ufbQEtPY=
|
||||
|
@ -12,8 +12,6 @@ import (
|
||||
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/guest"
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/secret"
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/storagepool"
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/storagevol"
|
||||
"git.staur.ca/stobbsm/clustvirt/util"
|
||||
"libvirt.org/go/libvirt"
|
||||
)
|
||||
@ -100,11 +98,6 @@ type StoragePoolInfo struct {
|
||||
XML string
|
||||
Active bool
|
||||
Persistent bool
|
||||
AutoStart bool
|
||||
State string
|
||||
Capacity uint64
|
||||
Allocation uint64
|
||||
Available uint64
|
||||
// HAEnabled indicates if the storage pool has High Availability
|
||||
HAEnabled bool
|
||||
// Volumes defined in the storage pool
|
||||
@ -114,11 +107,10 @@ type StoragePoolInfo struct {
|
||||
// VolumeInfo holds basic information about Volumes available in storage pools
|
||||
type VolumeInfo struct {
|
||||
Name string
|
||||
Key string
|
||||
Path string
|
||||
// StoragePool this volume is part of
|
||||
StoragePool StoragePoolInfo
|
||||
Type string
|
||||
Capacity uint64
|
||||
Allocation uint64
|
||||
Size uint
|
||||
XML string
|
||||
}
|
||||
|
||||
@ -273,48 +265,6 @@ func (h *Host) getStoragePools(wg *sync.WaitGroup) {
|
||||
if h.StoragePoolList[i].Persistent, err = s.IsPersistent(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if h.StoragePoolList[i].AutoStart, err = s.GetAutostart(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
spInfo, err := s.GetInfo()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
h.StoragePoolList[i].State = storagepool.StoragePoolStateMap[spInfo.State]
|
||||
h.StoragePoolList[i].Capacity = spInfo.Capacity
|
||||
h.StoragePoolList[i].Allocation = spInfo.Allocation
|
||||
h.StoragePoolList[i].Available = spInfo.Available
|
||||
|
||||
svols, err := s.ListAllStorageVolumes(0)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if len(svols) > 0 {
|
||||
h.StoragePoolList[i].Volumes = make([]VolumeInfo, len(svols))
|
||||
for j, sv := range svols {
|
||||
if h.StoragePoolList[i].Volumes[j].Name, err = sv.GetName(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if h.StoragePoolList[i].Volumes[j].Key, err = sv.GetKey(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if h.StoragePoolList[i].Volumes[j].Path, err = sv.GetPath(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
svInfo, err := sv.GetInfo() // Type, Capacity, Allocation
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
h.StoragePoolList[i].Volumes[j].Type = storagevol.StorageVolTypeMap[svInfo.Type]
|
||||
h.StoragePoolList[i].Volumes[j].Capacity = svInfo.Capacity
|
||||
h.StoragePoolList[i].Volumes[j].Allocation = svInfo.Allocation
|
||||
if h.StoragePoolList[i].Volumes[j].XML, err = sv.GetXMLDesc(0); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
sv.Free()
|
||||
}
|
||||
}
|
||||
|
||||
s.Free()
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package host
|
||||
|
||||
import "github.com/go-chi/chi/v5"
|
||||
|
||||
func (h *Host) HTMXRenderHandler(router *chi.Router) {
|
||||
}
|
@ -1,11 +1 @@
|
||||
package storagepool
|
||||
|
||||
import "libvirt.org/go/libvirt"
|
||||
|
||||
var StoragePoolStateMap = map[libvirt.StoragePoolState]string{
|
||||
libvirt.STORAGE_POOL_INACTIVE: "inactive",
|
||||
libvirt.STORAGE_POOL_BUILDING: "building",
|
||||
libvirt.STORAGE_POOL_RUNNING: "running",
|
||||
libvirt.STORAGE_POOL_DEGRADED: "degraded",
|
||||
libvirt.STORAGE_POOL_INACCESSIBLE: "inaccessible",
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package storagevol
|
||||
|
||||
import "libvirt.org/go/libvirt"
|
||||
|
||||
var StorageVolTypeMap = map[libvirt.StorageVolType]string{
|
||||
libvirt.STORAGE_VOL_FILE: "file",
|
||||
libvirt.STORAGE_VOL_BLOCK: "block",
|
||||
libvirt.STORAGE_VOL_DIR: "dir",
|
||||
libvirt.STORAGE_VOL_NETWORK: "network",
|
||||
libvirt.STORAGE_VOL_NETDIR: "netdir",
|
||||
libvirt.STORAGE_VOL_PLOOP: "ploop",
|
||||
}
|
54
main.go
@ -2,54 +2,24 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.staur.ca/stobbsm/clustvirt/view"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/host"
|
||||
ffmt "gopkg.in/ffmt.v1"
|
||||
)
|
||||
|
||||
const DEBUG bool = true
|
||||
|
||||
func main() {
|
||||
log.Println("Starting clustvirt, the libvirt cluster manager")
|
||||
|
||||
//venus, err := host.ConnectHost(host.URI_QEMU_SSH_SYSTEM, "venus.staur.ca")
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
//defer venus.Close()
|
||||
//lm, err := venus.GetGuestByName("logan-minecraft")
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
//defer lm.Close()
|
||||
|
||||
// Start webserver and serve homepage
|
||||
|
||||
fs := http.StripPrefix("/static/", http.FileServer(http.Dir("public")))
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Use(middleware.Logger)
|
||||
r.Get("/static/*", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
if DEBUG {
|
||||
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Add("Pragma", "no-cache")
|
||||
w.Header().Add("Expire", "0")
|
||||
venus, err := host.ConnectHost(host.URI_QEMU_SSH_SYSTEM, "venus.staur.ca")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fs.ServeHTTP(w, r)
|
||||
})
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
if DEBUG {
|
||||
w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Add("Pragma", "no-cache")
|
||||
w.Header().Add("Expire", "0")
|
||||
defer venus.Close()
|
||||
ffmt.P(venus)
|
||||
lm, err := venus.GetGuestByName("logan-minecraft")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
w.Header().Add("Content", "text/html")
|
||||
log.Println(view.ViewHome.Render(w, nil))
|
||||
})
|
||||
|
||||
log.Println(http.ListenAndServe(":3000", r))
|
||||
defer lm.Close()
|
||||
//ffmt.P(lm)
|
||||
}
|
||||
|
1090
public/css/style.css
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 411 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9.75 14.25 12m0 0 2.25 2.25M14.25 12l2.25-2.25M14.25 12 12 14.25m-2.58 4.92-6.374-6.375a1.125 1.125 0 0 1 0-1.59L9.42 4.83c.21-.211.497-.33.795-.33H19.5a2.25 2.25 0 0 1 2.25 2.25v10.5a2.25 2.25 0 0 1-2.25 2.25h-9.284c-.298 0-.585-.119-.795-.33Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 449 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0M3.124 7.5A8.969 8.969 0 0 1 5.292 3m13.416 0a8.969 8.969 0 0 1 2.168 4.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 488 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 415 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.412 15.655 9.75 21.75l3.745-4.012M9.257 13.5H3.75l2.659-2.849m2.048-2.194L14.25 2.25 12 10.5h8.25l-4.707 5.043M8.457 8.457 3 3m5.457 5.457 7.086 7.086m0 0L21 21" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 365 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 260 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 756 B |
@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 709 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 263 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 222 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
Before Width: | Height: | Size: 225 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 558 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 271 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 350 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 469 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
|
||||
<path d="M12.378 1.602a.75.75 0 0 0-.756 0L3 6.632l9 5.25 9-5.25-8.622-5.03ZM21.75 7.93l-9 5.25v9l8.628-5.032a.75.75 0 0 0 .372-.648V7.93ZM11.25 22.18v-9l-9-5.25v8.57a.75.75 0 0 0 .372.648l8.628 5.033Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 313 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-2.25-1.313M21 7.5v2.25m0-2.25-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3 2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75 2.25-1.313M12 21.75V19.5m0 2.25-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 467 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 284 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 276 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5-3.9 19.5m-2.1-19.5-3.9 19.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 265 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||
</svg>
|
Before Width: | Height: | Size: 434 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 3.75H6.912a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H15M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859M12 3v8.25m0 0-3-3m3 3 3-3" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 591 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m7.875 14.25 1.214 1.942a2.25 2.25 0 0 0 1.908 1.058h2.006c.776 0 1.497-.4 1.908-1.058l1.214-1.942M2.41 9h4.636a2.25 2.25 0 0 1 1.872 1.002l.164.246a2.25 2.25 0 0 0 1.872 1.002h2.092a2.25 2.25 0 0 0 1.872-1.002l.164-.246A2.25 2.25 0 0 1 16.954 9h4.636M2.41 9a2.25 2.25 0 0 0-.16.832V12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 12V9.832c0-.287-.055-.57-.16-.832M2.41 9a2.25 2.25 0 0 1 .382-.632l3.285-3.832a2.25 2.25 0 0 1 1.708-.786h8.43c.657 0 1.281.287 1.709.786l3.284 3.832c.163.19.291.404.382.632M4.5 20.25h15A2.25 2.25 0 0 0 21.75 18v-2.625c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125V18a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 852 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 353 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 408 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 447 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 370 B |
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 23.999992 23.999996"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="list-accepted.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
width="23.999992"
|
||||
height="23.999996"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="12"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="847"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m 8.3076888,12.923076 2.7692302,2.769231 4.615384,-6.4615418 m 7.384614,2.7692328 a 11.076921,11.076928 0 1 1 -22.15384198,0 11.076921,11.076928 0 0 1 22.15384198,0 z"
|
||||
id="path1"
|
||||
style="stroke-width:1.84615;stroke:#d6d6d6;stroke-opacity:1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM3.75 12h.007v.008H3.75V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.375 5.25h.007v.008H3.75v-.008Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 478 B |
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 23.999998 23.999996"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="list-informational.svg"
|
||||
width="23.999998"
|
||||
height="23.999996"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="9.7627119"
|
||||
inkscape:cy="9.7627119"
|
||||
inkscape:window-width="1312"
|
||||
inkscape:window-height="449"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m 11.076927,11.076927 0.05046,-0.02462 a 0.92307691,0.9230774 0 0 1 1.308308,1.048616 l -0.871384,3.490464 a 0.92307691,0.9230774 0 0 0 1.308308,1.049846 l 0.05046,-0.02585 m 10.153842,-4.615384 a 11.076923,11.076929 0 1 1 -22.15384598,0 11.076923,11.076929 0 0 1 22.15384598,0 z M 12.000004,7.3846176 h 0.0098 v 0.00985 h -0.0098 z"
|
||||
id="path1"
|
||||
style="stroke:#d6d6d6;stroke-width:1.84615;stroke-opacity:1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 23.994221 23.935944"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="list-never.svg"
|
||||
width="23.994221"
|
||||
height="23.935944"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="12"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="847"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M 19.833912,19.783417 A 11.082911,11.052709 0 0 0 4.1603098,4.1525257 M 19.833912,19.783417 A 11.082911,11.052709 0 0 1 4.1603098,4.1525257 M 19.833912,19.783417 4.1603098,4.1525257"
|
||||
id="path1"
|
||||
style="stroke:#d6d6d6;stroke-width:1.84462;stroke-opacity:1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 23.999998 23.999996"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="list-possible.svg"
|
||||
width="23.999998"
|
||||
height="23.999996"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="9.8333333"
|
||||
inkscape:cx="9.7627119"
|
||||
inkscape:cy="9.7627119"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="847"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M 15.692306,11.999999 H 8.3076904 m 14.7692306,0 a 11.076923,11.076929 0 1 1 -22.15384598,0 11.076923,11.076929 0 0 1 22.15384598,0 z"
|
||||
id="path1"
|
||||
style="stroke:#d6d6d6;stroke-width:1.84615;stroke-opacity:1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 278 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 245 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 299 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 9v6m-4.5 0V9M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 257 B |
@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 418 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 250 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 400 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 573 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 0 0-.12-1.03l-2.268-9.64a3.375 3.375 0 0 0-3.285-2.602H7.923a3.375 3.375 0 0 0-3.285 2.602l-2.268 9.64a4.5 4.5 0 0 0-.12 1.03v.228m19.5 0a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3m19.5 0a3 3 0 0 0-3-3H5.25a3 3 0 0 0-3 3m16.5 0h.008v.008h-.008v-.008Zm-3 0h.008v.008h-.008v-.008Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 498 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 462 B |
@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 9.563C9 9.252 9.252 9 9.563 9h4.874c.311 0 .563.252.563.563v4.874c0 .311-.252.563-.563.563H9.564A.562.562 0 0 1 9 14.437V9.564Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 431 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 614 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 400 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 270 B |
@ -1,7 +0,0 @@
|
||||
{{ define "footer" }}
|
||||
<div class="flex gap-4 md:gap-6 sm:gap-8 divide divide-solid">
|
||||
<div id="footer_left" class="flex-auto basis-1/4 md:basis-1/3 p-2">Left</div>
|
||||
<div id="footer_middle" class="flex-auto basis-1/2 md:basis-1/3 p-2">Middle</div>
|
||||
<div id="footer_right" class="flex-auto basis-1/4 md:basis-1/3 p-2">Right</div>
|
||||
</div>
|
||||
{{ end }}
|
@ -1,10 +0,0 @@
|
||||
{{ define "header" }}
|
||||
<h1 class="text-2xl font-bold">Clustvirt</h1>
|
||||
<h2 class="text-sm font-thin italic">Libvirtd simplified and clustered</h2>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>Server 1</li>
|
||||
<li>Server 2</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{ end }}
|
@ -1,22 +0,0 @@
|
||||
{{ define "_index" }}
|
||||
<!DOCTYPE html>
|
||||
<html class="text-nosferatu-100 bg-nosferatu-900">
|
||||
<head>
|
||||
<title>Clustvirt</title>
|
||||
<link href="/static/css/style.css" type="text/css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{{ template "header" }}
|
||||
</header>
|
||||
|
||||
<div id="content" name="content">
|
||||
{{ template "content" . }}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
{{ template "footer" }}
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
@ -1,4 +0,0 @@
|
||||
{{ define "content" }}
|
||||
<h1>{{.HostName}}</h1>
|
||||
<p>This will contain all the graphs, storage pool lists, vm lists, etc for the host selected.</p>
|
||||
{{ end }}
|
@ -1,43 +0,0 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Regular pages are defined as views here, like the homepage
|
||||
|
||||
// Major components of each page.
|
||||
const (
|
||||
index = `view/_index.gohtml`
|
||||
header = `view/_header.gohtml`
|
||||
footer = `view/_footer.gohtml`
|
||||
)
|
||||
|
||||
// These constitute the static parts of the site that don't need to change, loaded as a template for rendering
|
||||
const (
|
||||
home = `view/static/home.gohtml`
|
||||
)
|
||||
|
||||
func init() {
|
||||
if fi, err := os.Stat(index); err != nil {
|
||||
log.Fatal(fi.Name(), fi.IsDir(), err)
|
||||
}
|
||||
if fi, err := os.Stat(header); err != nil {
|
||||
log.Fatal(fi.Name(), fi.IsDir(), err)
|
||||
}
|
||||
if fi, err := os.Stat(footer); err != nil {
|
||||
log.Fatal(fi.Name(), fi.IsDir(), err)
|
||||
}
|
||||
if fi, err := os.Stat(home); err != nil {
|
||||
log.Fatal(fi.Name(), fi.IsDir(), err)
|
||||
}}
|
||||
|
||||
var ViewHome *View
|
||||
|
||||
func init() {
|
||||
log.Println("Initializing homepage")
|
||||
var err error
|
||||
ViewHome, err = NewFromFile(home)
|
||||
log.Println(err)
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
{{ define "content" }}
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-dracula-900 text-dracula-100 shadow-dracula-900
|
||||
divide-dracula-800 border-dracula-800">
|
||||
<h3 class="text-xl font-semibold">What is this?</h3>
|
||||
<p>Clustvirt (work in progress name) aims to be the agnostic
|
||||
cluster controller for libvirtd. The server component is
|
||||
used to display both the WebUI and run the REST API used to
|
||||
control one to many libvirtd hosts to manage virual machines,
|
||||
LXC containers (through libvirtd), gather information about
|
||||
each host, and monitor each host.</p>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-blade-900 text-blade-100 shadow-blade-900
|
||||
divide-blade-800 border-blade-800">
|
||||
<h3 class="text-xl font-semibold">Project Goals</h4>
|
||||
<ul class="leading-relaxed list-image-accepted list-inside">
|
||||
<li class="accepted">Open source, currently on the MIT license</li>
|
||||
<li class="notimpl">Base OS Agnostic. If it can run libvirtd, this should be
|
||||
able to control it on some level</li>
|
||||
<li class="notimpl">Control the Virtual Machine life cycle
|
||||
on one or more libvirtd hosts</li>
|
||||
<li class="notimpl">Add clusting capabilities to libvirtd host,
|
||||
including;</li>
|
||||
<li class="notimpl">Migration of VMs</li>
|
||||
<li class="notimpl">Syncronizing secrets</li>
|
||||
<li class="notimpl">Syncronizing VLANs, bridges, host only
|
||||
networking</li>
|
||||
<li class="notimpl">Sharing HA storage availability</li>
|
||||
<li class="notimpl">Locking shared resources like disks</li>
|
||||
<li class="notimpl">Starting VMs marked for HA on another host
|
||||
when one goes down</li>
|
||||
<li class="notimpl">Manage a library of Cloud-init resources
|
||||
and templates to build new VMs quickly</li>
|
||||
<li class="notimpl">Local Storage management, including local
|
||||
directory, lvm, zfs (if installed)</li>
|
||||
<li class="notimpl">Advanced Storage management, such as Ceph,
|
||||
glusterfs, drbd, iscsi, nfs</li>
|
||||
<li class="notimpl">Storage syncronization of local disks
|
||||
between hosts (zfs snapshots, lvm snapshots, rsync)</li>
|
||||
<li class="notimpl">Backup scheduling, creation, restoration</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-cullen-900 text-cullen-100 shadow-cullen-900
|
||||
divide-cullen-800 border-cullen-800">
|
||||
<h3 class="text-xl font-semibold">Stretch Goals</h3>
|
||||
<ul class="leading-relaxed list-image-possible list-inside">
|
||||
<li>Install the OS which libvirtd is running on</li>
|
||||
<li>Install/provision libvirtd on a host that does not have
|
||||
it installed</li>
|
||||
<li>Tools to move from one vendor to clustvirt/libvirtd</li>
|
||||
<li>VM templates for common aspects of VM creation and management,
|
||||
like appliances</li>
|
||||
<li>External tool access that can be used to manage things that
|
||||
are not managed here (cephadm dashboard, for instance)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-morbius-900 text-morbius-100 shadow-morbius-900
|
||||
divide-morbius-800 border-morbius-800">
|
||||
<h3 class="text-xl font-semibold">Reddit Requested Features</h3>
|
||||
<ul class="list-image-possible list-inside leading-relaxed">
|
||||
<li>Search/Filter on hosts/vms - @Lopsided_Speaker_553</li>
|
||||
<li>Balance on resource usage per host/Automattically migrate to
|
||||
least used host - @Lopsided_Speaker_553</li>
|
||||
<li>Support inter-vm only commmunication (VxLAN style)
|
||||
- @Lopsided_Speaker_553</li>
|
||||
<li>Deploy VMs using only API - @Lopsided_Speaker_553</li>
|
||||
<li>Well documented, first class API - @kasperlitheater</li>
|
||||
<li>Bootstrap service to configure a new server - @phatpappa_</li>
|
||||
<li>For the love of kitten, don't use XML as configuration files
|
||||
- @pascalbrax</li>
|
||||
<li>Expose the Cluster Manager functionalities as API - @raven2611</li>
|
||||
<li>CPU architecture awareness for migrations - @raven2611</li>
|
||||
<li>Inter VM Communications via VXLAN/EVPN - @raven2611</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-marcelin-900 text-marcelin-100 shadow-marcelin-900
|
||||
divide-marcelin-800 border-marcelin-800">
|
||||
<h3 class="text-xl font-semibold">Never Going to Happen</h3>
|
||||
<ul class="list-image-never list-inside leading-relaxed">
|
||||
<li>Kubernetes</li>
|
||||
<li>Application container management (docker, podman, etc)</li>
|
||||
<li>Become an OS</li>
|
||||
<li>Have a paywall</li>
|
||||
<li>Vendor lock-in</li>
|
||||
<li>Become a commercial entity (even indirectly)</li>
|
||||
<li>Anything that does not have an Open Source standard behind it</li>
|
||||
<li>Directly control a guest Operating System</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double divide-solid divide-y-2
|
||||
bg-dracula-900 text-dracula-100 shadow-dracula-900
|
||||
divide-dracula-800 border-dracula-800">
|
||||
<h3 class="text-xl font-semibold">Why?</h3>
|
||||
<ul class="list-image-informational list-inside leading-relaxed">
|
||||
<li>Broadcom buying VMWare, and VMWare losing a free teir for
|
||||
homelabbers pissed me off</li>
|
||||
<li>Vendor lock-in pisses me off</li>
|
||||
<li>Even good open source Hyperconverged systems (Proxmox, as
|
||||
an example) exhibit a form of vendor lock-in</li>
|
||||
<li>Libvirt is terrific, has the functionality for everything
|
||||
those other providers do, but there really is not a
|
||||
great option for those dipping their toes into Open Source</li>
|
||||
<li>Its fun to build things that solve a need</li>
|
||||
<li>I really want to do it</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="m-2 px-4 pt-2 flex-col shadow-md
|
||||
rounded-2xl border-8 border-double
|
||||
bg-voncount-900 text-voncount-100 shadow-voncount-900
|
||||
divide-voncount-800 border-voncount-800">
|
||||
<h3 class="text-xl font-semibold border-b-2 border-voncount-800">Other notes</h3>
|
||||
<p>I recently created a <a href="http://redd.it/1bct15z"
|
||||
class="after:content-link after:w-3 after:h-3 after:inline-block
|
||||
after:invert text-base text-morbius-100">post</a>
|
||||
on reddit announcing that I was building this, and while the majority
|
||||
of responses were supportive, even offering features that may enhance
|
||||
what I originally set out to do, many responded with
|
||||
"Why do we need another one??"</p>
|
||||
|
||||
<p>Besides the list above about why this exists, I wanted to clarify
|
||||
a few things those individuals did not seeem to get: This is not
|
||||
a rebuild of Proxmox, Cloudstack, VMWare, Harvester or any of
|
||||
the other "Hyper-converged/Single-solution/turnkey/Operating System"
|
||||
offerings out there. This will not take over your base operating system
|
||||
on your machine, just act as a cluster manager and interface to access
|
||||
the existing libvirtd instances on those machines, nor will it prescribe
|
||||
a set of requirements that make it hard to move your own infrastructure
|
||||
around.</p>
|
||||
|
||||
<p>At the heart of this project is that I hate the enshitifiation of
|
||||
Open Source that has been going on, where its just another way to make
|
||||
money and control the eco system. RedHat tried to do it by locking
|
||||
down their source code, Proxmox does it by making sure anything you
|
||||
do on Proxmox is tied to Proxmox (no offense to Proxmox), and even
|
||||
Hashicorp, who I loved so dearly, changed from a pure Open Source
|
||||
licensing model to one that protects the business over the community.</p>
|
||||
|
||||
<p>I will not let that happen here.</p>
|
||||
|
||||
<p>This project will seek to use the Unix philosophy, of building off
|
||||
of existing standards, combining tools, and having one tool do one
|
||||
job well. This does not mean there will be one application for each
|
||||
aspect of the job, but that this application stack will manage Libvirtd
|
||||
well, and have individual and configurable paths to manage each sub-aspect
|
||||
of the libvirt stack. This stack will not create a Ceph cluster for you,
|
||||
it leaves you to do that. It will not even talk to a ceph cluster.
|
||||
It will, however, let you add that cluster via configuration options to
|
||||
define it as a storage pool that libvirt can use.</p>
|
||||
|
||||
<p>If you want something that will allow you to use a single interface
|
||||
to create all sub aspects that can be used by libvirt
|
||||
(managing all firewall rules, creating a ceph cluster, etc.),
|
||||
use something like Proxmox which includes that builtin
|
||||
functionality. This isn't the stack for you.<p>
|
||||
</div>
|
||||
{{ end }}
|
55
view/view.go
@ -1,55 +0,0 @@
|
||||
// Package view handles WebUI generation for clustvirt. The methods and utilties in this module control what is viewed,
|
||||
// templates that are loaded, and building those templates. Caching is not considered beyond what is done
|
||||
// automattically by go (if anything).
|
||||
package view
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// View is responsible for assembling a group of templates, providing
|
||||
// methods to add data and compose pages in a common way.
|
||||
type View struct {
|
||||
content string
|
||||
template *template.Template
|
||||
}
|
||||
|
||||
var basetemplate *template.Template
|
||||
|
||||
// New returns a new instance of the View, expecting the content to be the actual
|
||||
// content as a template defining "content", in string format.
|
||||
func New(content string) *View {
|
||||
if basetemplate == nil {
|
||||
log.Println("Initializing base template")
|
||||
basetemplate = template.Must(template.New("").ParseFiles(index, header, footer))
|
||||
}
|
||||
log.Println("Cloning base template")
|
||||
v := &View{template: template.Must(basetemplate.Clone())}
|
||||
v.parse(content)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// NewFromFile loads a template from a file
|
||||
func NewFromFile(file string) (*View, error) {
|
||||
b, err := os.ReadFile(file)
|
||||
return New(string(b)), err
|
||||
}
|
||||
|
||||
func (v *View) parse(tmpl string) error {
|
||||
log.Println("Parsing template contents")
|
||||
if _, err := v.template.Parse(tmpl); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Template parsed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render returns the executed template with data
|
||||
func (v *View) Render(w io.Writer, data any) error {
|
||||
log.Println("Excuting template")
|
||||
return v.template.ExecuteTemplate(w, "_index", data)
|
||||
}
|