package main import ( "fmt" "log" "os" "context" "syscall" "strings" "crypto/tls" "encoding/json" "net/http" "bazil.org/fuse" "bazil.org/fuse/fs" _ "bazil.org/fuse/fs/fstestutil" ) var client *http.Client var password string var classes map[float64]Puppetclass var hosts = map[string]Host{} var topLevels = []string{ "hosts" } // const apiurl := "http://localhost:8000/hosts.json" const apiurl = "https://chapman.lysator.liu.se/api/" func main() { password = os.Args[1:][0] classes = map[float64]Puppetclass{} tr := &http.Transport { TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client = &http.Client { Transport: tr } request, err := http.NewRequest("GET", apiurl + "hosts", nil) if err != nil { log.Fatal(err); } request.SetBasicAuth("hugo", password); log.Print("Getting host list") resp, err := client.Do(request) if err != nil { log.Fatal(err); } defer resp.Body.Close() // echo curl --user "hugo:$(pass lysator/hugo)" -k https://chapman.lysator.liu.se/api/hosts log.Print("Parsing host list"); decoder := json.NewDecoder(resp.Body); var m Message err = decoder.Decode(&m) if err != nil { log.Fatal(err); } // var data []ResultRecord // data := m.Results for _, record := range m.Results { /* if record.Owner_name == "Hugo Hörnquist" { fmt.Printf("%v\n", record.Name) } */ hosts[record.Name] = Host{ record } } c, err := fuse.Mount( "mnt", fuse.FSName("Foreman"), fuse.Subtype("foreman"), ) if err != nil { log.Fatal(err) } // defer c.Unmount() defer c.Close() log.Print("Serving filesystem"); err = fs.Serve(c, FS{}) if err != nil { log.Fatal(err) } } type ResultRecord struct { Ip string // "130.236.254.139", Ip6 string // "2001:6b0:17:f0a0::8b", Environment_id float64 // 1, Environment_name string // "production", Last_report string // "2020-10-28 19:10:57 UTC", Mac string // "1c:c1:de:03:e7:b6", /* "realm_id": null, "realm_name": null, "sp_mac": null, "sp_ip": null, "sp_name": null, */ Domain_id float64 // 1, Domain_name string // "lysator.liu.se", Architecture_id float64 // 1, Architecture_name string // "x86_64", Operatingsystem_id float64 // 6, Operatingsystem_name string // "CentOS Linux 7.8.2003", /* "subnet_id": null, "subnet_name": null, "subnet6_id": null, "subnet6_name": null, "sp_subnet_id": null, "ptable_id": null, "ptable_name": null, "medium_id": null, "medium_name": null, "pxe_loader": null, "build": false, */ Comment string // "", /* "disk": null, "installed_at": null, */ Model_id float64 // 3, Hostgroup_id float64 // 6, Owner_id float64 // 5, Owner_name string // "Henrik Henriksson", Owner_type string // "User", Enabled bool // true, Managed bool // false, /* "use_image": null, */ Image_file string // "", /* "uuid": null, "compute_resource_id": null, "compute_resource_name": null, "compute_profile_id": null, "compute_profile_name": null, */ // capabilities []interface{} // [ "build" ], Provision_method string // "build", Certname string // "analysator-system.lysator.liu.se", /* "image_id": null, "image_name": null, */ Created_at string // "2020-08-16 19:51:59 UTC", Updated_at string // "2020-10-28 19:11:26 UTC", /* "last_compile": null, */ Global_status float64 // 0, Global_status_label string // "OK", Uptime_seconds float64 // 20857465, Organization_id float64 // 1, Organization_name string // "Lysator", Location_id float64 // 2, Location_name string // "Foo", Puppet_status float64 // 0, Model_name string // "ProLiant DL180 G6", Configuration_status float64 // 0, Configuration_status_label string // "No changes", Name string // "analysator-system.lysator.liu.se", Id float64 // 13, Puppet_proxy_id float64 // 1, Puppet_proxy_name string // "chapman.lysator.liu.se", Puppet_ca_proxy_id float64 // 1, Puppet_ca_proxy_name string // "chapman.lysator.liu.se", /* "puppet_proxy": { "name": "chapman.lysator.liu.se", "id": 1, "url": "https://chapman.lysator.liu.se:8443" }, "puppet_ca_proxy": { "name": "chapman.lysator.liu.se", "id": 1, "url": "https://chapman.lysator.liu.se:8443" }, */ Hostgroup_name string // "System", Hostgroup_title string // "Analysator/System" } type Message struct { Total, Subtotal, Page/*, Per_page*/ float64 Results []ResultRecord // search // sort { by, order null } } type FS struct{ // data []ResultRecord } func (fs FS) Root() (fs.Node, error) { return Dir{ /*fs.data*/ }, nil } /* func (FS) GenerateInode(parentInode uint64, name string) uint64 { sum := uint64(0) sum += parentInode for _, c := range []byte(name) { sum += uint64(c) } log.Printf("Generating inode for %v (inode = %v, parent = %v)", name, sum, parentInode) return sum } */ /* dir hashable? */ /* TODO does two Dir object with same value hash become the same * object? */ type Dir struct{ // data []ResultRecord // name []string name string } func (Dir) Attr(ctx context.Context, a *fuse.Attr) error { // log.Print("Stat dir") a.Inode = 0 a.Mode = os.ModeDir | 0o555 return nil } type Host struct { Data ResultRecord } func (d Dir) Lookup(ctx context.Context, name string) (fs.Node, error) { log.Printf("Lookup '%v' in '%v'", name, d.name) path := strings.Split(d.name, "/")[1:] log.Printf("path = %v", path) switch { case len(path) == 0: for _, n := range topLevels { if n == name { return Dir{ d.name + "/" + name }, nil } return nil, syscall.ENOENT } case path[0] == "hosts": if len(path) == 1 { _, ok := hosts[name] if ! ok { return nil, syscall.ENOENT } return Dir{ d.name + "/" + name }, nil } else { if len(path) != 2 { return nil, syscall.ENOENT } switch name { case "data": return DataFile{ path[1] }, nil case "classes": return Dir{ d.name + "/" + name }, nil } } } // return Dir{ d.name + name + "/" }, nil /* for _, item := range data { if item.Name == name { log.Printf("Found %v", name) return File{item}, nil } } log.Printf("File %v missing", name) */ return nil, syscall.ENOENT } type DataFile struct { // record ResultRecord hostname string } func (d Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { var dirents []fuse.Dirent // log.Printf("reading directory %v", d.name) path := strings.Split(d.name, "/")[1:] log.Printf("path = %v", path) switch { case len(path) == 0: for _, name := range topLevels { dirents = append(dirents, fuse.Dirent{Inode: 0, Name: name, Type: fuse.DT_Dir}) } case path[0] == "hosts": if len(path) == 1 { for _, item := range hosts { dirents = append(dirents, fuse.Dirent{Inode: 0, Name: item.Data.Name, Type: fuse.DT_Dir}) } } else { //h := hosts[d.name[7:]] if len(path) != 2 { return nil, syscall.ENOENT } dirents = append(dirents, fuse.Dirent{Inode: 0, Name: "data", Type: fuse.DT_File}) dirents = append(dirents, fuse.Dirent{Inode: 0, Name: "classes", Type: fuse.DT_Dir}) } default: // TODO } return dirents, nil } func (f DataFile) Attr(ctx context.Context, a *fuse.Attr) error { a.Inode = 0 a.Mode = 0o444 // a.Size = uint64(len(fmt.Sprintf("%+v\n", f.record))) // a.Size = uint64(len(greeting)) a.Size = uint64(len(fmt.Sprintf("%+v\n", hosts[f.hostname]))) return nil } type Puppetclass struct { Profiles []struct { Id float64 Name string Created_at string Updated_at string } /* [{"id":433,"name":"profiles::elvisp","created_at":"2020-10-27T07:57:47.102Z","updated_at":"2020-10-27T07:57:47.102Z"}, {"id":243,"name":"profiles::service","created_at":"2020-08-16T15:07:23.431Z","updated_at":"2020-08-16T15:07:23.431Z"}, {"id":434,"name":"profiles::vitellary","created_at":"2020 -10-27T07:57:47.130Z","updated_at":"2020-10-27T07:57:47.130Z"}]} */ /* { "total": 3, "subtotal": 3, "page": 1, "per_page": 50, "search": null, "sort": { "by": null, "order": null }, "results": {"profiles":[{"id":433,"name":"profiles::elvisp","created_at":"2020-10-27T07:57:47.102Z","updated_at":"2020-10-27T07:57:47.102Z"},{"id":243,"name" :"profiles::service","created_at":"2020-08-16T15:07:23.431Z","updated_at":"2020-08-16T15:07:23.431Z"},{"id":434,"name":"profiles::vitellary","created_at":"2020 -10-27T07:57:47.130Z","updated_at":"2020-10-27T07:57:47.130Z"}]} } */ } type PuppetclassMessage struct { Total, Subtotal, Page/*, Per_page*/ float64 Results Puppetclass } func (f DataFile) ReadAll(ctx context.Context) ([]byte, error) { /* var record Puppetclass if val, ok := classes[f.record.Id]; ok { record = val } else { url := fmt.Sprintf("https://chapman.lysator.liu.se/api/hosts/%d/puppetclasses", int(f.record.Id)); request, err := http.NewRequest("GET",url, nil) if err != nil { log.Fatal(err); } request.SetBasicAuth("hugo", password); resp, err := client.Do(request) if err != nil { log.Fatal(err); } defer resp.Body.Close() decoder := json.NewDecoder(resp.Body); var m PuppetclassMessage err = decoder.Decode(&m) if err != nil { log.Fatal(err); } classes[f.record.Id] = m.Results record = m.Results } str := "" for _, r := range record.Profiles { str += r.Name + "\n" } */ return []byte(fmt.Sprintf("%+v\n", hosts[f.hostname])), nil //return []byte(fmt.Sprintf("%+v\n", f.record)), nil // return []byte(str), nil // return []byte(greeting), nil }