From 0e3c8081c68e5edeed65c73575d35c5f80cd852e Mon Sep 17 00:00:00 2001 From: Miki Date: Thu, 10 Oct 2024 15:50:07 +0200 Subject: [PATCH] hosts file --- container.go | 71 ++++++++++++++++++ host.go | 120 ++++++++++++++++++++++++++++++ hosts.cfg | 10 +++ main.go | 201 ++++++++++++--------------------------------------- options.go | 38 ++++++++++ 5 files changed, 287 insertions(+), 153 deletions(-) create mode 100644 container.go create mode 100644 host.go create mode 100644 hosts.cfg create mode 100644 options.go diff --git a/container.go b/container.go new file mode 100644 index 0000000..d04c147 --- /dev/null +++ b/container.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" +) + +type Containers struct { + Containers []Container `json:"containers"` +} + +type Container struct { + Name string `json:"names"` + Port int `json:"port"` + IP string `json:"ip"` + Image string `json:"image"` + ID string `json:"id"` + Status string `json:"status"` + State string `json:"state"` +} + +type Models struct { + Models []Model `json:"models"` +} + +func (c *Containers) Add(ctr Container) { + c.Containers = append(c.Containers, ctr) +} + +func (c *Containers) Adds(ctrs Containers) { + c.Containers = append(c.Containers, ctrs.Containers...) +} + +func get_ollama_containers(host Host, all bool) Containers { + + ctr_list := Containers{} + + cli, err := client.NewClientWithOpts(client.WithHost(fmt.Sprintf("tcp://%s:%s", host.IP, host.Port))) + if err != nil { + fmt.Println(err) + return ctr_list + } + defer cli.Close() + + containers, err := cli.ContainerList(context.Background(), container.ListOptions{All: all}) + if err != nil { + fmt.Println(err) + return ctr_list + } + + for _, ctr := range containers { + if strings.Contains(ctr.Names[0], "ollama") { //&& ctr.Image == "ollama/ollama" { + c := Container{ID: ctr.ID, Name: ctr.Names[0], Image: ctr.Image, State: ctr.State, Status: ctr.Status} + if ctr.State == "running" { + // fmt.Println(ctr.Ports[0]) + c.Port = int(ctr.Ports[0].PublicPort) + } + c.IP = host.IP + ctr_list.Add(c) + // ctr_list = append(ctr_list, c) + } + } + + // fmt.Println(ctr_list) + + return ctr_list +} diff --git a/host.go b/host.go new file mode 100644 index 0000000..b16b7ca --- /dev/null +++ b/host.go @@ -0,0 +1,120 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "regexp" + "strings" +) + +type Hosts struct { + Hosts []Host `json:"hosts"` +} + +type Host struct { + IP string `json:"ip"` + Port string `json:"port"` + Containers []Container `json:"containers"` +} + +type Model struct { + Container string `json:"container"` + Name string `json:"name"` + Model string `json:"model"` + Modified_at string `json:"modified_at"` + Size int `json:"size"` + Details Details `json:"details"` + Ip string `json:"ip"` + Port int `json:"port"` + State string `json:"state"` +} + +type Details struct { + Parent_model string `json:"parent_model"` + Format string `json:"format"` + Family string `json:"family"` + Families []string `json:"families"` + Parameter_size string `json:"parameter_size"` + Quantization_level string `json:"quantization_level"` +} + +func get_ollama_tags(hosts Hosts) (Models, error) { + + var ctr Containers + + for _, host := range hosts.Hosts { + fmt.Printf("get ollama containers: %s -> %s\n", host.IP, host.Port) + ctr.Adds(get_ollama_containers(host, false)) + } + + retval := []Model{} + + for _, ollama := range ctr.Containers { + if ollama.State != "running" { + retval = append(retval, Model{Ip: ollama.IP, Port: ollama.Port, State: "stopped"}) + } else { + url := fmt.Sprintf("http://%s:%d/api/tags", ollama.IP, ollama.Port) + resp, err := http.Get(url) + if err != nil { + return Models{}, fmt.Errorf("failed to get %s: %v", url, err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return Models{}, fmt.Errorf("failed to read body: %v", err) + } + + mods := Models{} + err = json.Unmarshal(body, &mods) + if err != nil { + return Models{}, fmt.Errorf("failed to unmarshal JSON: %v", err) + } + + for n := range mods.Models { + mods.Models[n].Ip = ollama.IP + mods.Models[n].Port = ollama.Port + mods.Models[n].State = "running" + mods.Models[n].Container = ollama.Name + fmt.Printf("mod: %s - %s:%d\n", mods.Models[n].Name, mods.Models[n].Ip, mods.Models[n].Port) + } + + retval = append(retval, mods.Models...) + } + } + + return Models{Models: retval}, nil +} + +func (h *Hosts) Add(s string) { + ip_port := strings.Split(s, ":") + h.Hosts = append(h.Hosts, Host{IP: ip_port[0], Port: ip_port[1]}) +} + +func (h *Hosts) Init(server string) error { + + if debug { + fmt.Printf("server: %s\n", server) + } + + servers := strings.Split(server, ",") + + if len(servers) == 0 { + return fmt.Errorf("no servers specified in config file: '%s'", server) + } + + if len(servers) == 1 && servers[0] == "" { + return fmt.Errorf("no servers specified in config file: '%s'", server) + } + + for _, server := range servers { + if !regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$`).MatchString(server) { + return fmt.Errorf("invalid server specified: '%s'", server) + } + h.Add(server) + } + + return nil +} diff --git a/hosts.cfg b/hosts.cfg new file mode 100644 index 0000000..535cc63 --- /dev/null +++ b/hosts.cfg @@ -0,0 +1,10 @@ +## IP:PORT +## IP:PORT +## || +## IP:PORT,IP:PORT +## +#192.168.55.13:2375 +#192.168.55.15:2375 +#192.168.55.10:2375 +192.168.55.10:2375,192.168.55.15:2375 +192.168.55.13:2375 diff --git a/main.go b/main.go index 010fa66..ea362c0 100644 --- a/main.go +++ b/main.go @@ -1,182 +1,77 @@ package main import ( - "context" - "encoding/json" + "flag" "fmt" - "io" - "net/http" + "log" + "os" "strings" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" ) -type Hosts struct { - Hosts []Host `json:"hosts"` -} - -type Host struct { - Name string `json:"name"` - IP string `json:"ip"` - Port string `json:"port"` - Containers []Container `json:"containers"` -} - -type Containers struct { - Containers []Container `json:"containers"` -} - -type Container struct { - Name string `json:"names"` - Port int `json:"port"` - IP string `json:"ip"` - Image string `json:"image"` - ID string `json:"id"` - Status string `json:"status"` - State string `json:"state"` -} - -type Details struct { - Parent_model string `json:"parent_model"` - Format string `json:"format"` - Family string `json:"family"` - Families []string `json:"families"` - Parameter_size string `json:"parameter_size"` - Quantization_level string `json:"quantization_level"` -} - -type Model struct { - Container string `json:"container"` - Name string `json:"name"` - Model string `json:"model"` - Modified_at string `json:"modified_at"` - Size int `json:"size"` - Details Details `json:"details"` - Ip string `json:"ip"` - Port int `json:"port"` - State string `json:"state"` -} - -type Models struct { - Models []Model `json:"models"` -} - -func get_ollama_tags(hosts Hosts) (Models, error) { - - var ctr Containers - - for _, host := range hosts.Hosts { - fmt.Printf("HOST: %s -> %s\n", host.Name, host.IP) - ctr.Adds(get_ollama_containers(host, false)) - // ctr = append(ctr, get_ollama_containers(host)...) - // fmt.Println(ctr) +func read_config() string { + f, ferr := os.ReadFile(config) + if ferr != nil { + log.Fatalf("failed to read config file: %v", ferr) } - retval := []Model{} + tmp_srv := strings.TrimSpace(string(f)) + tmp_srv = strings.ReplaceAll(tmp_srv, "\r", "") - for _, ollama := range ctr.Containers { - // fmt.Println(ollama) - if ollama.State != "running" { - retval = append(retval, Model{Ip: ollama.IP, Port: ollama.Port, State: "stopped"}) - } else { - url := fmt.Sprintf("http://%s:%d/api/tags", ollama.IP, ollama.Port) - resp, err := http.Get(url) - if err != nil { - return Models{}, fmt.Errorf("failed to get %s: %v", url, err) - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return Models{}, fmt.Errorf("failed to read body: %v", err) - } - - mods := Models{} - err = json.Unmarshal(body, &mods) - if err != nil { - return Models{}, fmt.Errorf("failed to unmarshal JSON: %v", err) - } - fmt.Printf("%+v\n", mods) - - for n := range mods.Models { - mods.Models[n].Ip = ollama.IP - mods.Models[n].Port = ollama.Port - mods.Models[n].State = "running" - mods.Models[n].Container = ollama.Name - fmt.Printf("mod: %s - %s:%d\n", mods.Models[n].Name, mods.Models[n].Ip, mods.Models[n].Port) - } - - retval = append(retval, mods.Models...) + srvs := []string{} + /// delete comment rows + for _, server := range strings.Split(tmp_srv, "\n") { + if !strings.Contains(server, "#") { + server = strings.ReplaceAll(server, " ", "") + srvs = append(srvs, server) } } - return Models{Models: retval}, nil -} - -func get_ollama_containers(host Host, all bool) Containers { - - ctr_list := Containers{} - - // fmt.Printf("%s -> %s\n", host.Name, host.IP) - cli, err := client.NewClientWithOpts(client.WithHost(fmt.Sprintf("tcp://%s:%s", host.IP, host.Port))) - if err != nil { - fmt.Println(err) - return ctr_list - } - defer cli.Close() - - containers, err := cli.ContainerList(context.Background(), container.ListOptions{All: all}) - if err != nil { - fmt.Println(err) - return ctr_list + var server_list string + if len(srvs) > 1 { + server_list = strings.Join(srvs, ",") + } else { + server_list = srvs[0] } - for _, ctr := range containers { - if strings.Contains(ctr.Names[0], "ollama") { //&& ctr.Image == "ollama/ollama" { - c := Container{ID: ctr.ID, Name: ctr.Names[0], Image: ctr.Image, State: ctr.State, Status: ctr.Status} - if ctr.State == "running" { - // fmt.Println(ctr.Ports[0]) - c.Port = int(ctr.Ports[0].PublicPort) - } - c.IP = host.IP - ctr_list.Add(c) - // ctr_list = append(ctr_list, c) - } + if debug { + fmt.Printf("config: '%s'\n", server_list) } - // fmt.Println(ctr_list) - - return ctr_list -} - -func (h *Hosts) Add(name string, ip string, port string) { - h.Hosts = append(h.Hosts, Host{Name: name, IP: ip, Port: port}) -} - -func (c *Containers) Add(ctr Container) { - c.Containers = append(c.Containers, ctr) -} - -func (c *Containers) Adds(ctrs Containers) { - c.Containers = append(c.Containers, ctrs.Containers...) -} - -func (h *Hosts) Init() { - h.Add("thor", "192.168.55.13", "2375") - h.Add("ironman", "192.168.55.10", "2375") - h.Add("hela", "192.168.55.15", "2375") + return server_list } func main() { - port := "4000" + var server_list string + + flag.Usage = usage + flag.Parse() + + if version { + fmt.Printf("%s: %s\n", os.Args[0], _Version) + os.Exit(0) + } + + if servers == "" && config == "" { + fmt.Println("no servers specified") + usage() + } hosts := Hosts{} - hosts.Init() + if config != "" { + server_list = read_config() + } else { + server_list = servers + } + + error := hosts.Init(server_list) + + if error != nil { + panic(error) + } r := gin.Default() r.Use(cors.Default()) diff --git a/options.go b/options.go new file mode 100644 index 0000000..532dc71 --- /dev/null +++ b/options.go @@ -0,0 +1,38 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path" +) + +const ( + _Version = "0.5.0" +) + +var ( + port string + debug bool + servers string + config string + version bool +) + +func usage() { + fmt.Printf("Usage of %s:\n", path.Base(os.Args[0])) + fmt.Printf("\t-p \n") + fmt.Printf("\t-D \n") + fmt.Printf("\t-s \n") + fmt.Printf("\t-c \n") + fmt.Println() + os.Exit(0) +} + +func init() { + flag.StringVar(&port, "p", "4000", "port") + flag.BoolVar(&debug, "D", false, "debug") + flag.BoolVar(&debug, "v", false, "debug") + flag.StringVar(&servers, "s", "", "servers") + flag.StringVar(&config, "c", "", "config file") +}