commit e0c24850440b8bcaa5bf6a49c0dd493fb30e94fb from: Oliver Lowe date: Fri Jan 14 00:21:29 2022 UTC Support Icinga2 hostgroups Tests of hostgroup operations are done in TestFilter commit - 7aa5b0553214edbb1a6e71187215972e1e4877c6 commit + e0c24850440b8bcaa5bf6a49c0dd493fb30e94fb blob - 93f2a07ce5cb4265c4dacdb0536536b16b754141 blob + d52a46c819fca3af7e1bcce94a5aaeb5c96fddf5 --- crud.go +++ crud.go @@ -156,3 +156,55 @@ func (c *Client) DeleteUser(name string) error { } return nil } + +// HostGroups returns a slice of HostGroup matching the filter expression filter. +// If no hostgroups match, error wraps ErrNoMatch. +// To fetch all hostgroup, set filter to the empty string (""). +func (c *Client) HostGroups(filter string) ([]HostGroup, error) { + objects, err := c.filterObjects("/objects/hostgroups", filter) + if err != nil { + return nil, fmt.Errorf("get hostgroups filter %q: %w", filter, err) + } + var hostgroups []HostGroup + for _, o := range objects { + v, ok := o.(HostGroup) + if !ok { + return nil, fmt.Errorf("get hostgroups filter %q: %T in response", filter, v) + } + hostgroups = append(hostgroups, v) + } + return hostgroups, nil +} + +// LookupHostGroup returns the HostGroup identified by name. If no HostGroup is found, error +// wraps ErrNotExist. +func (c *Client) LookupHostGroup(name string) (HostGroup, error) { + obj, err := c.lookupObject("/objects/hostgroups/" + name) + if err != nil { + return HostGroup{}, fmt.Errorf("lookup hostgroup %s: %w", name, err) + } + v, ok := obj.(HostGroup) + if !ok { + return HostGroup{}, fmt.Errorf("lookup hostgroup %s: result type %T is not HostGroup", name, v) + } + return v, nil +} + +// CreateHostGroup creates hostgroup. Some fields of hostgroup must be set for successful +// creation; see the type definition of HostGroup for details. +func (c *Client) CreateHostGroup(hostgroup HostGroup) error { + if err := c.createObject(hostgroup); err != nil { + return fmt.Errorf("create hostgroup %s: %w", hostgroup.Name, err) + } + return nil +} + +// DeleteHostGroup deletes the HostGroup identified by name. +// If no HostGroup is found, error wraps ErrNotExist. +func (c *Client) DeleteHostGroup(name string) error { + if err := c.deleteObject("/objects/hostgroups/" + name); err != nil { + return fmt.Errorf("delete hostgroup %s: %w", name, err) + } + return nil +} + blob - c2fa08026c533355acbe737f3b03804ce4f614cc blob + 1eb38a75c53d72f8fc8cd8b45902396067661a4f --- crud.sh +++ crud.sh @@ -1,6 +1,6 @@ #!/bin/sh -types="Host Service User" +types="Host Service User HostGroup" args=`getopt o: $*` if test $? -ne 0 blob - 2638ec294b6d52c607f76c143cc758f79369aa0e blob + 289f59e1aac4805abaa01e56213a8b612e2121a5 --- host.go +++ host.go @@ -2,7 +2,8 @@ package icinga import "encoding/json" -// Host represents a Host object. +// Host represents a Host object. To create a Host, the Name and CheckCommand +// fields must be set. type Host struct { Name string `json:"name"` Address string `json:"address"` @@ -13,6 +14,11 @@ type Host struct { DisplayName string `json:"display_name"` } +type HostGroup struct { + Name string `json:"name"` + DisplayName string `json:"display_name"` +} + type HostState int const ( @@ -41,6 +47,14 @@ func (h Host) path() string { return "/objects/hosts/" + h.Name } +func (hg HostGroup) name() string { + return hg.Name +} + +func (hg HostGroup) path() string { + return "/objects/hostgroups/" + hg.Name +} + func (h Host) MarshalJSON() ([]byte, error) { type Attrs struct { Address string `json:"address"` @@ -59,3 +73,17 @@ func (h Host) MarshalJSON() ([]byte, error) { } return json.Marshal(jhost) } + +func (hg HostGroup) MarshalJSON() ([]byte, error) { + type attrs struct { + DisplayName string `json:"display_name"` + } + type group struct { + Attrs attrs `json:"attrs"` + } + return json.Marshal(&group{ + Attrs: attrs{ + DisplayName: hg.DisplayName, + }, + }) +} blob - e8308305a7cde15ca9c8c2ca193ebd48305c0adf blob + fbeaba13612776537813dbc19a3a467fc0b5eb46 --- host_test.go +++ host_test.go @@ -35,14 +35,28 @@ func TestFilter(t *testing.T) { tp.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} c := http.DefaultClient c.Transport = tp - client, err := Dial("127.0.0.1:5665", "root", "8eec5ede1673b757", c) + client, err := Dial("127.0.0.1:5665", "root", "icinga", c) if err != nil { t.Skipf("no local test icinga? got: %v", err) } + hostgroup := HostGroup{Name: "examples", DisplayName: "Test Group"} + if err := client.CreateHostGroup(hostgroup); err != nil { + t.Error(err) + } + hostgroup, err = client.LookupHostGroup(hostgroup.Name) + if err != nil { + t.Error(err) + } + defer client.DeleteHostGroup(hostgroup.Name) + var want, got []string for i := 0; i < 5; i++ { - h := Host{Name: randomHostname(), CheckCommand: "hostalive"} + h := Host{ + Name: randomHostname(), + CheckCommand: "hostalive", + Groups: []string{hostgroup.Name}, + } want = append(want, h.Name) if err := client.CreateHost(h); err != nil { if !errors.Is(err, ErrExist) { @@ -52,6 +66,13 @@ func TestFilter(t *testing.T) { } t.Logf("created host %s", h.Name) } + defer func() { + for _, name := range want { + if err := client.DeleteHost(name); err != nil { + t.Log(err) + } + } + }() hosts, err := client.Hosts("match(\"*example.org\", host.name)") if err != nil { t.Fatal(err) blob - 84d80a2fc8ad39e46cf16a616c121be1f303fe78 blob + 4dbab0014688cbda7091bfcd4ddcb5868f987c39 --- object.go +++ object.go @@ -60,7 +60,7 @@ func (c *Client) filterObjects(objpath, expr string) ( func (c *Client) createObject(obj object) error { buf := &bytes.Buffer{} switch v := obj.(type) { - case Host, Service, User: + case Host, Service, User, HostGroup: if err := json.NewEncoder(buf).Encode(v); err != nil { return err } blob - faa8e3f3aaff33d56a12411ce4bb55b2269ac130 blob + 361a05a781c92dbda52c8eff75efdc3723fa8769 --- response.go +++ response.go @@ -71,6 +71,12 @@ func parseResponse(r io.Reader) (*response, error) { return nil, err } resp.Results = append(resp.Results, u) + case "HostGroup": + var h HostGroup + if err := json.Unmarshal(r.Attrs, &h); err != nil { + return nil, err + } + resp.Results = append(resp.Results, h) default: return nil, fmt.Errorf("unsupported unmarshal of type %s", r.Type) } blob - a6b02927619bc505fe49e38d85a142d6b7705459 blob + 372e7fbb5684e35d88de02751942c1b681b40492 --- user_test.go +++ user_test.go @@ -52,7 +52,7 @@ func TestUserRoundTrip(t *testing.T) { tp.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} c := http.DefaultClient c.Transport = tp - client, err := Dial("127.0.0.1:5665", "root", "8eec5ede1673b757", c) + client, err := Dial("127.0.0.1:5665", "root", "icinga", c) if err != nil { t.Skipf("no local test icinga? got: %v", err) }