diff --git a/mgmt/rest/metric.go b/mgmt/rest/metric.go index 87ed6d204..46a684cef 100644 --- a/mgmt/rest/metric.go +++ b/mgmt/rest/metric.go @@ -31,6 +31,7 @@ import ( "github.com/intelsdi-x/snap/core" "github.com/intelsdi-x/snap/mgmt/rest/rbody" + apiV2 "github.com/intelsdi-x/snap/mgmt/rest/v2" ) func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { @@ -40,6 +41,7 @@ func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter // perform a query q := r.URL.Query() v := q.Get("ver") + apiVersion := r.URL.Path[1:3] ns_query := q.Get("ns") if ns_query != "" { ver = 0 // 0: get all versions @@ -62,7 +64,7 @@ func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter respond(404, rbody.FromError(err), w) return } - respondWithMetrics(r.Host, mets, w) + respondWithMetrics(r.Host, mets, w, apiVersion) return } @@ -71,7 +73,7 @@ func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter respond(500, rbody.FromError(err), w) return } - respondWithMetrics(r.Host, mets, w) + respondWithMetrics(r.Host, mets, w, apiVersion) } func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, params httprouter.Params) { @@ -94,6 +96,7 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para ) q := r.URL.Query() v := q.Get("ver") + apiVersion := r.URL.Path[1:3] if ns[len(ns)-1] == "*" { if v == "" { @@ -111,7 +114,7 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para respond(404, rbody.FromError(err), w) return } - respondWithMetrics(r.Host, mets, w) + respondWithMetrics(r.Host, mets, w, apiVersion) return } @@ -122,7 +125,7 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para respond(404, rbody.FromError(err), w) return } - respondWithMetrics(r.Host, mets, w) + respondWithMetrics(r.Host, mets, w, apiVersion) return } @@ -171,7 +174,11 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para respond(200, b, w) } -func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter) { +func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter, version string) { + if version == "v2" { + apiV2.RespondWithMetrics(host, mets, w) + return + } b := rbody.NewMetricsReturned() for _, met := range mets { diff --git a/mgmt/rest/rbody/v2/metric.go b/mgmt/rest/rbody/v2/metric.go new file mode 100644 index 000000000..7bcf86bb7 --- /dev/null +++ b/mgmt/rest/rbody/v2/metric.go @@ -0,0 +1,49 @@ +package v2 + +import ( + "fmt" + + "github.com/intelsdi-x/snap/mgmt/rest/rbody" +) + +type MetricList struct { + Metrics []rbody.Metric `json:"metrics,omitempty"` +} + +type MetricReturned struct { + Metric *rbody.Metric `json:"metric,omitempty"` +} + +func (m *MetricReturned) ResponseBodyMessage() string { + return "Metric returned" +} + +func (m *MetricReturned) ResponseBodyType() string { + return "metric_returned" +} + +type MetricsReturned MetricList + +func (m MetricsReturned) Len() int { + return len(m.Metrics) +} + +func (m MetricsReturned) Less(i, j int) bool { + return (fmt.Sprintf("%s:%d", m.Metrics[i].Namespace, m.Metrics[i].Version)) < (fmt.Sprintf("%s:%d", m.Metrics[j].Namespace, m.Metrics[j].Version)) +} + +func (m MetricsReturned) Swap(i, j int) { + m.Metrics[i], m.Metrics[j] = m.Metrics[j], m.Metrics[i] +} + +func NewMetricsReturned() MetricsReturned { + return MetricsReturned{Metrics: []rbody.Metric{}} +} + +func (m MetricsReturned) ResponseBodyMessage() string { + return "Metrics returned" +} + +func (m MetricsReturned) ResponseBodyType() string { + return rbody.MetricsReturnedType +} diff --git a/mgmt/rest/rbody/v2/task.go b/mgmt/rest/rbody/v2/task.go new file mode 100644 index 000000000..759167045 --- /dev/null +++ b/mgmt/rest/rbody/v2/task.go @@ -0,0 +1,27 @@ +package v2 + +import "github.com/intelsdi-x/snap/mgmt/rest/rbody" + +type ScheduledTaskListReturned struct { + ScheduledTasks []rbody.ScheduledTask `json:"scheduled_task,omitempty"` +} + +func (s *ScheduledTaskListReturned) Len() int { + return len(s.ScheduledTasks) +} + +func (s *ScheduledTaskListReturned) Less(i, j int) bool { + return s.ScheduledTasks[j].CreationTime().After(s.ScheduledTasks[i].CreationTime()) +} + +func (s *ScheduledTaskListReturned) Swap(i, j int) { + s.ScheduledTasks[i], s.ScheduledTasks[j] = s.ScheduledTasks[j], s.ScheduledTasks[i] +} + +func (s *ScheduledTaskListReturned) ResponseBodyMessage() string { + return "Scheduled tasks retrieved" +} + +func (s *ScheduledTaskListReturned) ResponseBodyType() string { + return "scheduled_task_list_returned" +} diff --git a/mgmt/rest/rest_v1_test.go b/mgmt/rest/rest_v1_test.go index 9dd6024d4..f32c7a7ed 100644 --- a/mgmt/rest/rest_v1_test.go +++ b/mgmt/rest/rest_v1_test.go @@ -74,7 +74,6 @@ func startV1API(cfg *mockConfig, testType string) *restAPIInstance { mockTaskManager := &fixtures.MockTaskManager{} r.BindTaskManager(mockTaskManager) } - go func(ch <-chan error) { // Block on the error channel. Will return exit status 1 for an error or // just return if the channel closes. diff --git a/mgmt/rest/server.go b/mgmt/rest/server.go index abff403d7..8e9957e57 100644 --- a/mgmt/rest/server.go +++ b/mgmt/rest/server.go @@ -470,8 +470,13 @@ func (s *Server) addRoutes() { s.r.GET("/v1/metrics", s.getMetrics) s.r.GET("/v1/metrics/*namespace", s.getMetricsFromTree) + s.r.GET("/v2/metrics", s.getMetrics) + s.r.GET("/v2/metrics/*namespace", s.getMetricsFromTree) + // task routes s.r.GET("/v1/tasks", s.getTasks) + s.r.GET("/v2/tasks", s.getTasks) + s.r.GET("/v1/tasks/:id", s.getTask) s.r.GET("/v1/tasks/:id/watch", s.watchTask) s.r.POST("/v1/tasks", s.addTask) diff --git a/mgmt/rest/task.go b/mgmt/rest/task.go index 221c96af5..e1caba85c 100644 --- a/mgmt/rest/task.go +++ b/mgmt/rest/task.go @@ -28,6 +28,7 @@ import ( "time" log "github.com/Sirupsen/logrus" + apiV2 "github.com/intelsdi-x/snap/mgmt/rest/v2" "github.com/julienschmidt/httprouter" "github.com/intelsdi-x/snap/core" @@ -56,6 +57,11 @@ func (s *Server) addTask(w http.ResponseWriter, r *http.Request, _ httprouter.Pa func (s *Server) getTasks(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { sts := s.mt.GetTasks() + apiVersion := r.URL.Path[1:3] + if apiVersion == "v2" { + apiV2.GetTasks(w, r, sts) + return + } tasks := &rbody.ScheduledTaskListReturned{} tasks.ScheduledTasks = make([]rbody.ScheduledTask, len(sts)) diff --git a/mgmt/rest/v2/metric.go b/mgmt/rest/v2/metric.go new file mode 100644 index 000000000..15fe7ff66 --- /dev/null +++ b/mgmt/rest/v2/metric.go @@ -0,0 +1,92 @@ +package v2 + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "sort" + + "github.com/codegangsta/negroni" + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/mgmt/rest/rbody" + "github.com/intelsdi-x/snap/mgmt/rest/rbody/v2" +) + +func RespondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter) { + b := v2.NewMetricsReturned() + + for _, met := range mets { + rt := met.Policy().RulesAsTable() + policies := make([]rbody.PolicyTable, 0, len(rt)) + for _, r := range rt { + policies = append(policies, rbody.PolicyTable{ + Name: r.Name, + Type: r.Type, + Default: r.Default, + Required: r.Required, + Minimum: r.Minimum, + Maximum: r.Maximum, + }) + } + dyn, indexes := met.Namespace().IsDynamic() + var dynamicElements []rbody.DynamicElement + if dyn { + dynamicElements = getDynamicElements(met.Namespace(), indexes) + } + + b.Metrics = append(b.Metrics, rbody.Metric{ + Namespace: met.Namespace().String(), + Version: met.Version(), + LastAdvertisedTimestamp: met.LastAdvertisedTime().Unix(), + Description: met.Description(), + Dynamic: dyn, + DynamicElements: dynamicElements, + Unit: met.Unit(), + Policy: policies, + Href: catalogedMetricURI(host, met), + }) + } + sort.Sort(b) + respond(200, b, w) +} + +func catalogedMetricURI(host string, mt core.CatalogedMetric) string { + return fmt.Sprintf("%s://%s/v1/metrics?ns=%s&ver=%d", "http", host, url.QueryEscape(mt.Namespace().String()), mt.Version()) +} + +func getDynamicElements(ns core.Namespace, indexes []int) []rbody.DynamicElement { + elements := make([]rbody.DynamicElement, 0, len(indexes)) + for _, v := range indexes { + e := ns.Element(v) + elements = append(elements, rbody.DynamicElement{ + Index: v, + Name: e.Name, + Description: e.Description, + }) + } + return elements +} + +func respond(code int, b rbody.Body, w http.ResponseWriter) { + resp := &rbody.APIResponse{ + Meta: &rbody.APIResponseMeta{ + Code: code, + Message: b.ResponseBodyMessage(), + Type: b.ResponseBodyType(), + Version: 2, + }, + Body: b, + } + if !w.(negroni.ResponseWriter).Written() { + w.WriteHeader(code) + } + + j, err := json.MarshalIndent(resp, "", " ") + if err != nil { + panic(err) + } + j = bytes.Replace(j, []byte("\\u0026"), []byte("&"), -1) + fmt.Fprint(w, string(j)) +} diff --git a/mgmt/rest/v2/task.go b/mgmt/rest/v2/task.go new file mode 100644 index 000000000..328ea9828 --- /dev/null +++ b/mgmt/rest/v2/task.go @@ -0,0 +1,29 @@ +package v2 + +import ( + "fmt" + "net/http" + "sort" + + "github.com/intelsdi-x/snap/core" + "github.com/intelsdi-x/snap/mgmt/rest/rbody" + "github.com/intelsdi-x/snap/mgmt/rest/rbody/v2" +) + +func GetTasks(w http.ResponseWriter, r *http.Request, sts map[string]core.Task) { + tasks := &v2.ScheduledTaskListReturned{} + tasks.ScheduledTasks = make([]rbody.ScheduledTask, len(sts)) + + i := 0 + for _, t := range sts { + tasks.ScheduledTasks[i] = *rbody.SchedulerTaskFromTask(t) + tasks.ScheduledTasks[i].Href = taskURI(r.Host, t) + i++ + } + sort.Sort(tasks) + respond(200, tasks, w) +} + +func taskURI(host string, t core.Task) string { + return fmt.Sprintf("%s://%s/v1/tasks/%s", "http", host, t.ID()) +}