Commit Diff


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)
 	}