Merge 'master' into feature/tr-search-by-tag
Conflicts: - ui/src/hosts/components/HostsTable.js Concurrent design changes happened alongside some performance improvements.pull/635/head
commit
9de7bcf3ce
|
@ -107,14 +107,21 @@ func MarshalLayout(l chronograf.Layout) ([]byte, error) {
|
|||
for i, c := range l.Cells {
|
||||
queries := make([]*Query, len(c.Queries))
|
||||
for j, q := range c.Queries {
|
||||
r := new(Range)
|
||||
if q.Range != nil {
|
||||
r.Upper, r.Lower = q.Range.Upper, q.Range.Lower
|
||||
}
|
||||
queries[j] = &Query{
|
||||
Command: q.Command,
|
||||
DB: q.DB,
|
||||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
Range: r,
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = &Cell{
|
||||
X: c.X,
|
||||
Y: c.Y,
|
||||
|
@ -155,8 +162,16 @@ func UnmarshalLayout(data []byte, l *chronograf.Layout) error {
|
|||
RP: q.RP,
|
||||
GroupBys: q.GroupBys,
|
||||
Wheres: q.Wheres,
|
||||
Label: q.Label,
|
||||
}
|
||||
if q.Range.Upper != q.Range.Lower {
|
||||
queries[j].Range = &chronograf.Range{
|
||||
Upper: q.Range.Upper,
|
||||
Lower: q.Range.Lower,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cells[i] = chronograf.Cell{
|
||||
X: c.X,
|
||||
Y: c.Y,
|
||||
|
|
|
@ -15,6 +15,7 @@ It has these top-level messages:
|
|||
Layout
|
||||
Cell
|
||||
Query
|
||||
Range
|
||||
AlertRule
|
||||
User
|
||||
*/
|
||||
|
@ -108,6 +109,8 @@ type Cell struct {
|
|||
Queries []*Query `protobuf:"bytes,5,rep,name=queries" json:"queries,omitempty"`
|
||||
I string `protobuf:"bytes,6,opt,name=i,proto3" json:"i,omitempty"`
|
||||
Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Yranges []int64 `protobuf:"varint,8,rep,name=yranges" json:"yranges,omitempty"`
|
||||
Ylabels []string `protobuf:"bytes,9,rep,name=ylabels" json:"ylabels,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Cell) Reset() { *m = Cell{} }
|
||||
|
@ -128,6 +131,8 @@ type Query struct {
|
|||
RP string `protobuf:"bytes,3,opt,name=RP,proto3" json:"RP,omitempty"`
|
||||
GroupBys []string `protobuf:"bytes,4,rep,name=GroupBys" json:"GroupBys,omitempty"`
|
||||
Wheres []string `protobuf:"bytes,5,rep,name=Wheres" json:"Wheres,omitempty"`
|
||||
Label string `protobuf:"bytes,6,opt,name=Label,proto3" json:"Label,omitempty"`
|
||||
Range *Range `protobuf:"bytes,7,opt,name=Range" json:"Range,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Query) Reset() { *m = Query{} }
|
||||
|
@ -135,6 +140,23 @@ func (m *Query) String() string { return proto.CompactTextString(m) }
|
|||
func (*Query) ProtoMessage() {}
|
||||
func (*Query) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{5} }
|
||||
|
||||
func (m *Query) GetRange() *Range {
|
||||
if m != nil {
|
||||
return m.Range
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Range struct {
|
||||
Upper int64 `protobuf:"varint,1,opt,name=Upper,proto3" json:"Upper,omitempty"`
|
||||
Lower int64 `protobuf:"varint,2,opt,name=Lower,proto3" json:"Lower,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Range) Reset() { *m = Range{} }
|
||||
func (m *Range) String() string { return proto.CompactTextString(m) }
|
||||
func (*Range) ProtoMessage() {}
|
||||
func (*Range) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
|
||||
|
||||
type AlertRule struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
JSON string `protobuf:"bytes,2,opt,name=JSON,proto3" json:"JSON,omitempty"`
|
||||
|
@ -145,7 +167,7 @@ type AlertRule struct {
|
|||
func (m *AlertRule) Reset() { *m = AlertRule{} }
|
||||
func (m *AlertRule) String() string { return proto.CompactTextString(m) }
|
||||
func (*AlertRule) ProtoMessage() {}
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{6} }
|
||||
func (*AlertRule) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
|
||||
|
||||
type User struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
|
@ -155,7 +177,7 @@ type User struct {
|
|||
func (m *User) Reset() { *m = User{} }
|
||||
func (m *User) String() string { return proto.CompactTextString(m) }
|
||||
func (*User) ProtoMessage() {}
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{7} }
|
||||
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorInternal, []int{8} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Exploration)(nil), "internal.Exploration")
|
||||
|
@ -164,6 +186,7 @@ func init() {
|
|||
proto.RegisterType((*Layout)(nil), "internal.Layout")
|
||||
proto.RegisterType((*Cell)(nil), "internal.Cell")
|
||||
proto.RegisterType((*Query)(nil), "internal.Query")
|
||||
proto.RegisterType((*Range)(nil), "internal.Range")
|
||||
proto.RegisterType((*AlertRule)(nil), "internal.AlertRule")
|
||||
proto.RegisterType((*User)(nil), "internal.User")
|
||||
}
|
||||
|
@ -171,40 +194,45 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 555 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x94, 0x4f, 0x8e, 0xd3, 0x4a,
|
||||
0x10, 0xc6, 0xd5, 0xb1, 0x9d, 0xc4, 0x35, 0x4f, 0x79, 0xa8, 0x35, 0x42, 0x16, 0x62, 0x11, 0x59,
|
||||
0x2c, 0x82, 0x84, 0x66, 0x01, 0x27, 0x48, 0xe2, 0x11, 0x0a, 0x0c, 0x43, 0xe8, 0x4c, 0xc4, 0x8a,
|
||||
0x45, 0x93, 0x54, 0x88, 0x25, 0xc7, 0x36, 0xed, 0x36, 0x89, 0xcf, 0x00, 0x67, 0xe0, 0x12, 0x5c,
|
||||
0x80, 0xa3, 0xa1, 0x6a, 0xb7, 0x1d, 0x4b, 0xfc, 0xd1, 0xec, 0xea, 0xab, 0xaa, 0xae, 0xfc, 0xfa,
|
||||
0xab, 0x8e, 0x61, 0x14, 0xa7, 0x1a, 0x55, 0x2a, 0x93, 0xab, 0x5c, 0x65, 0x3a, 0xe3, 0xc3, 0x46,
|
||||
0x87, 0x3f, 0x18, 0x5c, 0x5c, 0x9f, 0xf2, 0x24, 0x53, 0x52, 0xc7, 0x59, 0xca, 0x47, 0xd0, 0x5b,
|
||||
0x44, 0x01, 0x1b, 0xb3, 0x89, 0x23, 0x7a, 0x8b, 0x88, 0x73, 0x70, 0x6f, 0xe5, 0x01, 0x83, 0xde,
|
||||
0x98, 0x4d, 0x7c, 0x61, 0x62, 0xfe, 0x10, 0xfa, 0xeb, 0x02, 0xd5, 0x22, 0x0a, 0x1c, 0xd3, 0x67,
|
||||
0x15, 0xf5, 0x46, 0x52, 0xcb, 0xc0, 0xad, 0x7b, 0x29, 0xe6, 0x8f, 0xc1, 0x9f, 0x2b, 0x94, 0x1a,
|
||||
0xb7, 0x53, 0x1d, 0x78, 0xa6, 0xfd, 0x9c, 0xa0, 0xea, 0x3a, 0xdf, 0xda, 0x6a, 0xbf, 0xae, 0xb6,
|
||||
0x09, 0x1e, 0xc0, 0x20, 0xc2, 0x9d, 0x2c, 0x13, 0x1d, 0x0c, 0xc6, 0x6c, 0x32, 0x14, 0x8d, 0x0c,
|
||||
0x7f, 0x32, 0xe8, 0xaf, 0xb2, 0x52, 0x6d, 0xf0, 0x5e, 0xc0, 0x1c, 0xdc, 0xbb, 0x2a, 0x47, 0x83,
|
||||
0xeb, 0x0b, 0x13, 0xf3, 0x47, 0x30, 0x24, 0xec, 0x94, 0x7a, 0x6b, 0xe0, 0x56, 0x53, 0x6d, 0x29,
|
||||
0x8b, 0xe2, 0x98, 0xa9, 0xad, 0x61, 0xf6, 0x45, 0xab, 0xf9, 0x03, 0x70, 0xd6, 0xe2, 0xc6, 0xc0,
|
||||
0xfa, 0x82, 0xc2, 0xbf, 0x63, 0xd2, 0x9c, 0x3b, 0x4c, 0xf0, 0x93, 0x92, 0xbb, 0x60, 0x58, 0xcf,
|
||||
0x69, 0x74, 0xf8, 0x8d, 0xae, 0x80, 0xea, 0x0b, 0xaa, 0x7b, 0x5d, 0xa1, 0x8b, 0xeb, 0xfc, 0x03,
|
||||
0xd7, 0xfd, 0x33, 0xae, 0x77, 0xc6, 0xbd, 0x04, 0x6f, 0xa5, 0x36, 0x8b, 0xc8, 0xfa, 0x5d, 0x8b,
|
||||
0xf0, 0x3b, 0x83, 0xfe, 0x8d, 0xac, 0xb2, 0x52, 0x77, 0x70, 0x7c, 0x83, 0x33, 0x86, 0x8b, 0x69,
|
||||
0x9e, 0x27, 0xf1, 0xc6, 0xbc, 0x10, 0x4b, 0xd5, 0x4d, 0x51, 0xc7, 0x1b, 0x94, 0x45, 0xa9, 0xf0,
|
||||
0x80, 0xa9, 0xb6, 0x7c, 0xdd, 0x14, 0x7f, 0x02, 0xde, 0x1c, 0x93, 0xa4, 0x08, 0xdc, 0xb1, 0x33,
|
||||
0xb9, 0x78, 0x3e, 0xba, 0x6a, 0x1f, 0x24, 0xa5, 0x45, 0x5d, 0xa4, 0x8b, 0x4c, 0x4b, 0x9d, 0xed,
|
||||
0x92, 0xec, 0x68, 0x88, 0x87, 0xa2, 0xd5, 0xe1, 0x57, 0x06, 0x2e, 0x75, 0xf1, 0xff, 0x80, 0x9d,
|
||||
0x0c, 0x9d, 0x27, 0xd8, 0x89, 0x54, 0x65, 0x90, 0x3c, 0xc1, 0x2a, 0x52, 0x47, 0xf3, 0xf3, 0x9e,
|
||||
0x60, 0x47, 0x52, 0x7b, 0x63, 0x88, 0x27, 0xd8, 0x9e, 0x3f, 0x85, 0xc1, 0xe7, 0x12, 0x55, 0x8c,
|
||||
0x45, 0xe0, 0x19, 0x88, 0xff, 0xcf, 0x10, 0xef, 0x4a, 0x54, 0x95, 0x68, 0xea, 0x74, 0x30, 0xb6,
|
||||
0x1b, 0x66, 0x31, 0xad, 0xc3, 0xd8, 0x3e, 0xa8, 0xd7, 0x41, 0x71, 0x58, 0x82, 0x67, 0xce, 0xd0,
|
||||
0xf2, 0xe7, 0xd9, 0xe1, 0x20, 0xd3, 0xad, 0x75, 0xac, 0x91, 0x64, 0x63, 0x34, 0xb3, 0x6e, 0xf5,
|
||||
0xa2, 0x19, 0x69, 0xb1, 0xb4, 0xde, 0xf4, 0xc4, 0x92, 0x2e, 0xfb, 0x52, 0x65, 0x65, 0x3e, 0xab,
|
||||
0x6a, 0x57, 0x7c, 0xd1, 0x6a, 0xfa, 0x87, 0xbd, 0xdf, 0xa3, 0xb2, 0xa8, 0xbe, 0xb0, 0x2a, 0xfc,
|
||||
0x00, 0xfe, 0x34, 0x41, 0xa5, 0x45, 0x99, 0xe0, 0x6f, 0x7b, 0xe2, 0xe0, 0xbe, 0x5a, 0xbd, 0xbd,
|
||||
0x6d, 0x9e, 0x0d, 0xc5, 0xe7, 0x65, 0x3b, 0x9d, 0x65, 0xd3, 0xf8, 0xd7, 0x32, 0x97, 0x8b, 0xc8,
|
||||
0xb8, 0xe3, 0x08, 0xab, 0xc2, 0x67, 0xe0, 0xd2, 0xa3, 0xea, 0x4c, 0x76, 0xcd, 0xe4, 0x4b, 0xf0,
|
||||
0xae, 0x0f, 0x32, 0x4e, 0xec, 0xe8, 0x5a, 0x7c, 0xec, 0x9b, 0x6f, 0xc9, 0x8b, 0x5f, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0x92, 0xf3, 0x5a, 0x42, 0x5d, 0x04, 0x00, 0x00,
|
||||
// 626 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x94, 0x5f, 0x6e, 0xd3, 0x4c,
|
||||
0x10, 0xc0, 0xb5, 0xb1, 0x9d, 0xc4, 0xdb, 0x4f, 0xfd, 0xd0, 0xaa, 0x42, 0x2b, 0xc4, 0x83, 0x65,
|
||||
0x81, 0x14, 0x24, 0xd4, 0x07, 0x7a, 0x82, 0xb4, 0xae, 0x50, 0xa0, 0x94, 0xb2, 0x6d, 0xc4, 0x13,
|
||||
0x0f, 0xdb, 0x66, 0xda, 0x5a, 0xda, 0xd8, 0x66, 0x6d, 0x93, 0xfa, 0x0e, 0x9c, 0x81, 0x43, 0xc0,
|
||||
0x01, 0xe0, 0x68, 0x68, 0x66, 0xd7, 0x49, 0x10, 0x7f, 0xd4, 0xb7, 0xfd, 0xed, 0x4c, 0xc7, 0xbf,
|
||||
0x99, 0x9d, 0x86, 0xef, 0xe6, 0x45, 0x03, 0xb6, 0xd0, 0x66, 0xbf, 0xb2, 0x65, 0x53, 0x8a, 0x71,
|
||||
0xcf, 0xe9, 0x37, 0xc6, 0x77, 0x8e, 0xef, 0x2a, 0x53, 0x5a, 0xdd, 0xe4, 0x65, 0x21, 0x76, 0xf9,
|
||||
0x60, 0x96, 0x49, 0x96, 0xb0, 0x49, 0xa0, 0x06, 0xb3, 0x4c, 0x08, 0x1e, 0x9e, 0xea, 0x25, 0xc8,
|
||||
0x41, 0xc2, 0x26, 0xb1, 0xa2, 0xb3, 0x78, 0xc8, 0x87, 0xf3, 0x1a, 0xec, 0x2c, 0x93, 0x01, 0xe5,
|
||||
0x79, 0xc2, 0xdc, 0x4c, 0x37, 0x5a, 0x86, 0x2e, 0x17, 0xcf, 0xe2, 0x31, 0x8f, 0x8f, 0x2c, 0xe8,
|
||||
0x06, 0x16, 0xd3, 0x46, 0x46, 0x94, 0xbe, 0xb9, 0xc0, 0xe8, 0xbc, 0x5a, 0xf8, 0xe8, 0xd0, 0x45,
|
||||
0xd7, 0x17, 0x42, 0xf2, 0x51, 0x06, 0xd7, 0xba, 0x35, 0x8d, 0x1c, 0x25, 0x6c, 0x32, 0x56, 0x3d,
|
||||
0xa6, 0x3f, 0x18, 0x1f, 0x9e, 0x97, 0xad, 0xbd, 0x82, 0x7b, 0x09, 0x0b, 0x1e, 0x5e, 0x74, 0x15,
|
||||
0x90, 0x6e, 0xac, 0xe8, 0x2c, 0x1e, 0xf1, 0x31, 0x6a, 0x17, 0x98, 0xeb, 0x84, 0xd7, 0x8c, 0xb1,
|
||||
0x33, 0x5d, 0xd7, 0xab, 0xd2, 0x2e, 0xc8, 0x39, 0x56, 0x6b, 0x16, 0x0f, 0x78, 0x30, 0x57, 0x27,
|
||||
0x24, 0x1b, 0x2b, 0x3c, 0xfe, 0x5d, 0x13, 0xeb, 0x5c, 0x80, 0x81, 0x1b, 0xab, 0xaf, 0xe5, 0xd8,
|
||||
0xd5, 0xe9, 0x39, 0xfd, 0x8c, 0x2d, 0x80, 0xfd, 0x04, 0xf6, 0x5e, 0x2d, 0x6c, 0xeb, 0x06, 0xff,
|
||||
0xd0, 0x0d, 0xff, 0xac, 0x1b, 0x6d, 0x74, 0xf7, 0x78, 0x74, 0x6e, 0xaf, 0x66, 0x99, 0x9f, 0xb7,
|
||||
0x83, 0xf4, 0x0b, 0xe3, 0xc3, 0x13, 0xdd, 0x95, 0x6d, 0xb3, 0xa5, 0x13, 0x93, 0x4e, 0xc2, 0x77,
|
||||
0xa6, 0x55, 0x65, 0xf2, 0x2b, 0xda, 0x10, 0x6f, 0xb5, 0x7d, 0x85, 0x19, 0x6f, 0x40, 0xd7, 0xad,
|
||||
0x85, 0x25, 0x14, 0x8d, 0xf7, 0xdb, 0xbe, 0x12, 0x4f, 0x78, 0x74, 0x04, 0xc6, 0xd4, 0x32, 0x4c,
|
||||
0x82, 0xc9, 0xce, 0x8b, 0xdd, 0xfd, 0xf5, 0x42, 0xe2, 0xb5, 0x72, 0x41, 0x6c, 0x64, 0xda, 0x36,
|
||||
0xe5, 0xb5, 0x29, 0x57, 0x64, 0x3c, 0x56, 0x6b, 0x4e, 0xbf, 0x33, 0x1e, 0x62, 0x96, 0xf8, 0x8f,
|
||||
0xb3, 0x3b, 0xb2, 0x8b, 0x14, 0xbb, 0x43, 0xea, 0x48, 0x29, 0x52, 0xac, 0x43, 0x5a, 0xd1, 0xe7,
|
||||
0x23, 0xc5, 0x56, 0x48, 0xb7, 0x34, 0x90, 0x48, 0xb1, 0x5b, 0xf1, 0x8c, 0x8f, 0x3e, 0xb6, 0x60,
|
||||
0x73, 0xa8, 0x65, 0x44, 0x12, 0xff, 0x6f, 0x24, 0xde, 0xb5, 0x60, 0x3b, 0xd5, 0xc7, 0xf1, 0x0f,
|
||||
0x73, 0xff, 0xc2, 0x2c, 0xc7, 0xe7, 0xa0, 0xb1, 0x8f, 0xdc, 0x73, 0xd0, 0xc8, 0x25, 0x1f, 0x75,
|
||||
0x56, 0x17, 0x37, 0x50, 0xcb, 0x71, 0x12, 0x4c, 0x02, 0xd5, 0x23, 0x45, 0x8c, 0xbe, 0x04, 0x53,
|
||||
0xcb, 0x38, 0x09, 0x26, 0xb1, 0xea, 0x31, 0xfd, 0xca, 0x78, 0x44, 0x1f, 0xc2, 0x9c, 0xa3, 0x72,
|
||||
0xb9, 0xd4, 0xc5, 0xc2, 0x8f, 0xb9, 0x47, 0x9c, 0x7d, 0x76, 0xe8, 0x47, 0x3c, 0xc8, 0x0e, 0x91,
|
||||
0xd5, 0x99, 0x1f, 0xe8, 0x40, 0x9d, 0xe1, 0x84, 0x5e, 0xda, 0xb2, 0xad, 0x0e, 0x3b, 0x37, 0xca,
|
||||
0x58, 0xad, 0x19, 0xff, 0x2d, 0xdf, 0xdf, 0x82, 0xf5, 0xfd, 0xc5, 0xca, 0x13, 0x3e, 0xf8, 0x09,
|
||||
0x1a, 0xf8, 0x8e, 0x1c, 0x88, 0xa7, 0x3c, 0x52, 0x68, 0x4c, 0x6d, 0xfd, 0x32, 0x0c, 0xba, 0x56,
|
||||
0x2e, 0x9a, 0x1e, 0xf8, 0x34, 0xac, 0x32, 0xaf, 0x2a, 0xb0, 0x7e, 0x4f, 0x1d, 0x50, 0xed, 0x72,
|
||||
0x05, 0x96, 0x94, 0x03, 0xe5, 0x20, 0xfd, 0xc0, 0xe3, 0xa9, 0x01, 0xdb, 0xa8, 0xd6, 0xc0, 0x6f,
|
||||
0xeb, 0x24, 0x78, 0xf8, 0xea, 0xfc, 0xed, 0x69, 0xbf, 0xdd, 0x78, 0xde, 0xec, 0x64, 0xb0, 0xb5,
|
||||
0x93, 0xd8, 0xd0, 0x6b, 0x5d, 0xe9, 0x59, 0x46, 0x8f, 0x18, 0x28, 0x4f, 0xe9, 0x73, 0x1e, 0xe2,
|
||||
0xee, 0x6f, 0x55, 0x0e, 0xa9, 0xf2, 0x1e, 0x8f, 0x8e, 0x97, 0x3a, 0x37, 0xbe, 0xb4, 0x83, 0xcb,
|
||||
0x21, 0xfd, 0xe4, 0x1d, 0xfc, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xdb, 0x24, 0x8b, 0x04, 0x05,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ message Cell {
|
|||
repeated Query queries = 5; // Time-series data queries for Cell.
|
||||
string i = 6; // Unique identifier for the cell
|
||||
string name = 7; // User-facing name for this cell
|
||||
repeated int64 yranges = 8; // Limits of the y-axes
|
||||
repeated string ylabels = 9; // Labels of the y-axes
|
||||
}
|
||||
|
||||
message Query {
|
||||
|
@ -55,6 +57,13 @@ message Query {
|
|||
string RP = 3; // RP is a retention policy and optional;
|
||||
repeated string GroupBys= 4; // GroupBys define the groups to combine in the query
|
||||
repeated string Wheres = 5; // Wheres define the restrictions on the query
|
||||
string Label = 6; // Label is the name of the Y-Axis
|
||||
Range Range = 7; // Range is the upper and lower bound of the Y-Axis
|
||||
}
|
||||
|
||||
message Range {
|
||||
int64 Upper = 1; // Upper is the upper-bound of the range
|
||||
int64 Lower = 2; // Lower is the lower-bound of the range
|
||||
}
|
||||
|
||||
message AlertRule {
|
||||
|
|
|
@ -71,3 +71,46 @@ func TestMarshalServer(t *testing.T) {
|
|||
t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalLayout(t *testing.T) {
|
||||
layout := chronograf.Layout{
|
||||
ID: "id",
|
||||
Measurement: "measurement",
|
||||
Application: "app",
|
||||
Cells: []chronograf.Cell{
|
||||
{
|
||||
X: 1,
|
||||
Y: 1,
|
||||
W: 4,
|
||||
H: 4,
|
||||
I: "anotherid",
|
||||
Name: "cell1",
|
||||
Queries: []chronograf.Query{
|
||||
{
|
||||
Range: &chronograf.Range{
|
||||
Lower: 1,
|
||||
Upper: 2,
|
||||
},
|
||||
Label: "y1",
|
||||
Command: "select mean(usage_user) as usage_user from cpu",
|
||||
Wheres: []string{
|
||||
`"host"="myhost"`,
|
||||
},
|
||||
GroupBys: []string{
|
||||
`"cpu"`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var vv chronograf.Layout
|
||||
if buf, err := internal.MarshalLayout(layout); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err := internal.UnmarshalLayout(buf, &vv); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(layout, vv) {
|
||||
t.Fatalf("source protobuf copy error: got %#v, expected %#v", vv, layout)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,13 +53,21 @@ type TimeSeries interface {
|
|||
Connect(context.Context, *Source) error
|
||||
}
|
||||
|
||||
// Range represents an upper and lower bound for data
|
||||
type Range struct {
|
||||
Upper int64 `json:"upper"` // Upper is the upper bound
|
||||
Lower int64 `json:"lower"` // Lower is the lower bound
|
||||
}
|
||||
|
||||
// Query retrieves a Response from a TimeSeries.
|
||||
type Query struct {
|
||||
Command string `json:"query"` // Command is the query itself
|
||||
DB string `json:"db,omitempty"` // DB is optional and if empty will not be used.
|
||||
RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used.
|
||||
Wheres []string `json:"wheres"` // Wheres restricts the query to certain attributes
|
||||
GroupBys []string `json:"groupbys"` // GroupBys collate the query by these tags
|
||||
Command string `json:"query"` // Command is the query itself
|
||||
DB string `json:"db,omitempty"` // DB is optional and if empty will not be used.
|
||||
RP string `json:"rp,omitempty"` // RP is a retention policy and optional; if empty will not be used.
|
||||
Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes
|
||||
GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags
|
||||
Label string `json:"label,omitempty"` // Label is the Y-Axis label for the data
|
||||
Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data
|
||||
}
|
||||
|
||||
// Response is the result of a query against a TimeSeries
|
||||
|
|
|
@ -33,6 +33,8 @@ deployment:
|
|||
- docker login -e $QUAY_EMAIL -u "$QUAY_USER" -p $QUAY_PASS quay.io
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker push quay.io/influxdb/chronograf:${CIRCLE_SHA1:0:7}
|
||||
- docker tag chronograf quay.io/influxdb/chronograf:latest
|
||||
- docker push quay.io/influxdb/chronograf:latest
|
||||
- mv ./build/* $CIRCLE_ARTIFACTS
|
||||
pre-release:
|
||||
tag: /^[0-9]+(\.[0-9]+)*(\S*)([a|rc|beta]([0-9]+))+$/
|
||||
|
|
|
@ -5,41 +5,52 @@ Chronograf's applications are built by layout configurations that can be found i
|
|||
To create a new layout use the `new_apps.sh` script in the `canned` directory. This script takes an argument, which is the name of the layout you want, often this will map to the InfluxDB measurement, but it does not have to. For this example I will be creating a layout for the zombocom daemon zombocomd. So first step is the run `new_app.sh zombocomd` this will create a file called `zombocomd.json`. The file will look something like:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "d77917f4-9305-4c9c-beba-c29bf17a0fd2",
|
||||
"measurement": "zombocomd",
|
||||
"app": "zombocomd",
|
||||
"cells": [{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Running Zombo Average",
|
||||
"queries": [{
|
||||
"query": "SELECT mean(\"zombo\") FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": ["\"pod\""],
|
||||
"wheres": []
|
||||
}]
|
||||
{
|
||||
"id": "d77917f4-9305-4c9c-beba-c29bf17a0fd2",
|
||||
"measurement": "zombocomd",
|
||||
"app": "zombocomd",
|
||||
"cells": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Running Zombo Average",
|
||||
"queries": [
|
||||
{
|
||||
"query": "SELECT mean(\"zombo\") FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": [
|
||||
"\"pod\""
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "b75694f1-5764-4d1d-9d67-564455985894",
|
||||
"name": "Anythings Per Second",
|
||||
"queries": [{
|
||||
"query": "SELECT non_negative_derivative(max(\"anything\"), 1s) FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"groupbys": [],
|
||||
"wheres": []
|
||||
}]
|
||||
}]
|
||||
}
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 4,
|
||||
"h": 4,
|
||||
"i": "7a6bc880-054e-455c-becd-c0ebbbe109ce",
|
||||
"name": "Anythings Per Second",
|
||||
"queries": [
|
||||
{
|
||||
"query": "SELECT non_negative_derivative(max(\"anything\"), 1s) FROM zombocomd",
|
||||
"db": "telegraf",
|
||||
"rp": "",
|
||||
"label": "anything/s",
|
||||
"range": {
|
||||
"upper": 100,
|
||||
"lower": 10
|
||||
},
|
||||
"wheres": ["\"status\" = 'critical'"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The meaning of the fields are as follows:
|
||||
|
@ -54,10 +65,14 @@ The meaning of the fields are as follows:
|
|||
* i - A unique ID for the graph
|
||||
* name - The displayed name of the graph
|
||||
* queries - An array of InfluxQL queries. Note: queries must use an aggregate function since chronograf adds a group by time function to keep the data points manageable.
|
||||
* db - The name of the database for the query
|
||||
* rp - The [retention policy](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#retention-policy-rp) for the database
|
||||
* groupbys - An array of GROUP BY clauses to use on the query
|
||||
* wheres - An array of WHERE clauses to add to the query
|
||||
* queries.[].db - The name of the database for the query
|
||||
* queries.[].rp - The [retention policy](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#retention-policy-rp) for the database
|
||||
* queries.[].groupbys - An optional array of GROUP BY clauses to use on the query
|
||||
* queries.[].wheres - An optional array of WHERE clauses to add to the query
|
||||
* queries.[].label - Optional key specifying the user-facing name of the y-axes
|
||||
* queries.[].range.upper - Optional key specifying the default upper bound of the y-axis
|
||||
* queries.[].range.lower - Optional key specifying the default lower bound of the y-axis
|
||||
|
||||
|
||||
The above example will create two graphs. The first graph will show the mean number of zombos created then group the zombos by pod and by time. It is important to note that all queries have a `GROUP BY` time(x) appended to them. In this case `x` is determined by the duration of the query. The second graph will show the number of the "anything" [field](https://docs.influxdata.com/influxdb/v1.1/concepts/glossary/#field) per second average.
|
||||
For real world examples of when to use the `groupbys` see the docker examples and for `wheres` examples see the kubernetes examples in the `canned` directory.
|
||||
|
|
|
@ -18,7 +18,6 @@ func newLayoutResponse(layout chronograf.Layout) layoutResponse {
|
|||
httpAPILayouts := "/chronograf/v1/layouts"
|
||||
href := fmt.Sprintf("%s/%s", httpAPILayouts, layout.ID)
|
||||
rel := "self"
|
||||
|
||||
return layoutResponse{
|
||||
Layout: layout,
|
||||
Link: link{
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"routes"
|
||||
],
|
||||
"summary": "Lists all the endpoints",
|
||||
"description": "List of the endpoints.",
|
||||
"responses": {
|
||||
|
@ -38,6 +41,9 @@
|
|||
},
|
||||
"/sources": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Configured data sources",
|
||||
"description": "These data sources store time series data.",
|
||||
"responses": {
|
||||
|
@ -56,6 +62,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Create new data source",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -92,6 +101,9 @@
|
|||
},
|
||||
"/sources/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -125,6 +137,9 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"summary": "Update data source configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -166,6 +181,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -197,6 +215,10 @@
|
|||
},
|
||||
"/sources/{id}/proxy": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"proxy"
|
||||
],
|
||||
"description": "Query the backend time series data source and return the response according to `format`",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -252,6 +274,9 @@
|
|||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "List of all users on this data source",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
@ -269,6 +294,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Create new user for this data source",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -305,6 +333,9 @@
|
|||
},
|
||||
"/users/{user_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -338,6 +369,9 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Update user configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -379,6 +413,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -410,6 +447,11 @@
|
|||
},
|
||||
"/users/{user_id}/explorations": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Returns all explorations for specified user",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -441,6 +483,10 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Create new named exploration for this user",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -490,6 +536,10 @@
|
|||
},
|
||||
"/users/{user_id}/explorations/{exploration_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -530,6 +580,10 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"summary": "Update exploration configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -578,6 +632,10 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"users",
|
||||
"explorations"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
|
@ -616,6 +674,10 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -642,6 +704,10 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"summary": "Create new kapacitor backend",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -685,6 +751,10 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -725,6 +795,10 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"summary": "Update kapacitor configuration",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -773,6 +847,10 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -809,9 +887,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/tasks": {
|
||||
"/sources/{id}/kapacitors/{kapa_id}/rules": {
|
||||
"get": {
|
||||
"description": "Get all defined alert tasks.",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"description": "Get all defined alert rules.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -830,9 +913,9 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "All alert tasks for this specific kapacitor are returned",
|
||||
"description": "All alert rules for this specific kapacitor are returned",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Tasks"
|
||||
"$ref": "#/definitions/Rules"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
|
@ -850,7 +933,12 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create kapacitor alert task",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"description": "Create kapacitor alert rule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -867,37 +955,43 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task",
|
||||
"name": "rule",
|
||||
"in": "body",
|
||||
"description": "Rule to generate alert task",
|
||||
"description": "Rule to generate alert rule",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successfully created new kapacitor alert task",
|
||||
"description": "Successfully created new kapacitor alert rule",
|
||||
"headers": {
|
||||
"Location": {
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"description": "Location of the newly created kapacitor task resource."
|
||||
"description": "Location of the newly created kapacitor rule resource."
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Kapacitor ID does not exist.",
|
||||
"description": "Source ID or Kapacitor ID does not exist.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Source ID , Kapacitor ID or alert are unprocessable",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Internal server error",
|
||||
"description": "Internal server error; generally a problem creating alert in kapacitor",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -905,8 +999,13 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/tasks/{task_id}": {
|
||||
"/sources/{id}/kapacitors/{kapa_id}/rules/{rule_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -923,24 +1022,24 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of the task",
|
||||
"description": "ID of the rule",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "Specific kapacitor alert task",
|
||||
"description": "Alerting task for kapacitor",
|
||||
"summary": "Specific kapacitor alert rule",
|
||||
"description": "Alerting rule for kapacitor",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Alert exists and has a specific TICKscript",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown data source, kapacitor id, or task id",
|
||||
"description": "Unknown data source, kapacitor id, or rule id",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -954,7 +1053,12 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"summary": "Update rule alert task configuration",
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"summary": "Update rule alert rule configuration",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -971,18 +1075,18 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of a task",
|
||||
"description": "ID of a rule",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task",
|
||||
"name": "rule",
|
||||
"in": "body",
|
||||
"description": "Task update",
|
||||
"description": "Rule update",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
|
@ -991,11 +1095,11 @@
|
|||
"200": {
|
||||
"description": "Alert configuration was changed",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Task"
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Happens when trying to access a non-existent data source, kapacitor, or task.",
|
||||
"description": "Happens when trying to access a non-existent data source, kapacitor, or rule.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -1009,6 +1113,11 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"rules"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1025,20 +1134,20 @@
|
|||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "task_id",
|
||||
"name": "rule_id",
|
||||
"in": "path",
|
||||
"type": "string",
|
||||
"description": "ID of the task",
|
||||
"description": "ID of the rule",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"summary": "This specific alert task will be removed.",
|
||||
"summary": "This specific alert rule will be removed.",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Alert task has been removed."
|
||||
"description": "Alert rule has been removed."
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown Data source, Kapacitor id, or alert task",
|
||||
"description": "Unknown Data source, Kapacitor id, or alert rule",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
|
@ -1054,6 +1163,11 @@
|
|||
},
|
||||
"/sources/{id}/kapacitors/{kapa_id}/proxy": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "GET to `path` of kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1097,6 +1211,11 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "DELETE to `path` of kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1140,6 +1259,11 @@
|
|||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "PATCH body directly to configured kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1192,6 +1316,11 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"sources",
|
||||
"kapacitors",
|
||||
"proxy"
|
||||
],
|
||||
"description": "POST body directly to configured kapacitor. The response and status code from kapacitor is directly returned.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1246,6 +1375,10 @@
|
|||
},
|
||||
"/mappings": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts",
|
||||
"mappings"
|
||||
],
|
||||
"summary": "Mappings between app names and measurements",
|
||||
"description": "Mappings provide a means to alias measurement names found within a telegraf database and application layouts found within Chronograf\n",
|
||||
"responses": {
|
||||
|
@ -1266,6 +1399,9 @@
|
|||
},
|
||||
"/layouts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Pre-configured layouts",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1308,6 +1444,9 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Create new layout",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1344,6 +1483,9 @@
|
|||
},
|
||||
"/layouts/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1377,6 +1519,9 @@
|
|||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
|
@ -1406,6 +1551,9 @@
|
|||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Replace layout configuration.",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -1505,9 +1653,9 @@
|
|||
"description": "URL location of proxy endpoint for this kapacitor",
|
||||
"format": "url"
|
||||
},
|
||||
"tasks": {
|
||||
"rules": {
|
||||
"type": "string",
|
||||
"description": "URL location of tasks endpoint for this kapacitor",
|
||||
"description": "URL location of rules endpoint for this kapacitor",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
|
@ -1522,6 +1670,101 @@
|
|||
"description": "Entire response from the kapacitor backend.",
|
||||
"type": "object"
|
||||
},
|
||||
"Rules": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rules"
|
||||
],
|
||||
"properties": {
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Rule"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rule": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"every",
|
||||
"trigger"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID for this rule; the ID is shared with kapacitor"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "User facing name of the alerting rule"
|
||||
},
|
||||
"every": {
|
||||
"type": "string",
|
||||
"description": "Golang duration string specifying how often the alert condition is checked"
|
||||
},
|
||||
"alerts": {
|
||||
"type": "array",
|
||||
"description": "Array of alerting services to warn if the alert is triggered",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"hipchat",
|
||||
"opsgenie",
|
||||
"pagerduty",
|
||||
"victorops",
|
||||
"smtp",
|
||||
"email",
|
||||
"sensu",
|
||||
"slack",
|
||||
"talk",
|
||||
"telegram"
|
||||
]
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Message to send when alert occurs."
|
||||
},
|
||||
"trigger": {
|
||||
"type": "string",
|
||||
"description": "Trigger defines the alerting structure; deadman alert if no data are received for the specified time range; relative alert if the data change relative to the data in a different time range; threshold alert if the data cross a boundary",
|
||||
"enum": [
|
||||
"deadman",
|
||||
"relative",
|
||||
"threshold"
|
||||
]
|
||||
},
|
||||
"tickscript": {
|
||||
"type": "string",
|
||||
"description": "TICKscript representing this rule"
|
||||
},
|
||||
"links": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"self",
|
||||
"kapacitor"
|
||||
],
|
||||
"properties": {
|
||||
"self": {
|
||||
"description": "Self link pointing to this rule resource",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"kapacitor": {
|
||||
"description": "Link pointing to the kapacitor proxy for this rule including the path query parameter.",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"output": {
|
||||
"description": "Link pointing to the kapacitor httpOut node of the tickscript; includes the path query argument",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Sources": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -1690,12 +1933,12 @@
|
|||
"User": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"username"
|
||||
"email"
|
||||
],
|
||||
"properties": {
|
||||
"username": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"maxLength": 64
|
||||
"maxLength": 254
|
||||
},
|
||||
"link": {
|
||||
"$ref": "#/definitions/Link"
|
||||
|
@ -1782,12 +2025,18 @@
|
|||
"Cell": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"i",
|
||||
"x",
|
||||
"y",
|
||||
"w",
|
||||
"h"
|
||||
],
|
||||
"properties": {
|
||||
"i": {
|
||||
"description": "Unique ID of Cell",
|
||||
"type": "string",
|
||||
"format": "uuid4"
|
||||
},
|
||||
"x": {
|
||||
"description": "X-coordinate of Cell in the Layout",
|
||||
"type": "integer",
|
||||
|
@ -1823,6 +2072,30 @@
|
|||
"query"
|
||||
],
|
||||
"properties": {
|
||||
"label": {
|
||||
"description": "Optional Y-axis user-facing label for this query",
|
||||
"type": "string"
|
||||
},
|
||||
"range": {
|
||||
"description": "Optional default range of the Y-axis",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"upper",
|
||||
"lower"
|
||||
],
|
||||
"properties": {
|
||||
"upper": {
|
||||
"description": "Upper bound of the display range of the Y-axis",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"lower": {
|
||||
"description": "Lower bound of the display range of the Y-axis",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -1850,6 +2123,11 @@
|
|||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"me": {
|
||||
"description": "Location of the me endpoint.",
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
},
|
||||
"layouts": {
|
||||
"description": "Location of the layouts endpoint",
|
||||
"type": "string",
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('timeSeriesToDygraph', () => {
|
|||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
fields: [
|
||||
labels: [
|
||||
'time',
|
||||
`m1.f1`,
|
||||
`m1.f2`,
|
||||
|
@ -43,6 +43,14 @@ describe('timeSeriesToDygraph', () => {
|
|||
[new Date(2000), 2, 3],
|
||||
[new Date(4000), null, 4],
|
||||
],
|
||||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
|
@ -72,7 +80,7 @@ describe('timeSeriesToDygraph', () => {
|
|||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
fields: [
|
||||
labels: [
|
||||
'time',
|
||||
'm1.f1',
|
||||
],
|
||||
|
@ -83,6 +91,81 @@ describe('timeSeriesToDygraph', () => {
|
|||
],
|
||||
};
|
||||
|
||||
expect(actual).to.deep.equal(expected);
|
||||
expect(actual.timeSeries).to.deep.equal(expected.timeSeries);
|
||||
});
|
||||
|
||||
it('can parse multiple responses into two axes', () => {
|
||||
const influxResponse = [
|
||||
{
|
||||
"response":
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m1",
|
||||
"columns": ["time","f1"],
|
||||
"values": [[1000, 1],[2000, 2]],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m1",
|
||||
"columns": ["time","f2"],
|
||||
"values": [[2000, 3],[4000, 4]],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"response":
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name":"m3",
|
||||
"columns": ["time","f3"],
|
||||
"values": [[1000, 1],[2000, 2]],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const actual = timeSeriesToDygraph(influxResponse);
|
||||
|
||||
const expected = {
|
||||
labels: [
|
||||
'time',
|
||||
`m1.f1`,
|
||||
`m1.f2`,
|
||||
`m3.f3`,
|
||||
],
|
||||
timeSeries: [
|
||||
[new Date(1000), 1, null, 1],
|
||||
[new Date(2000), 2, 3, 2],
|
||||
[new Date(4000), null, 4, null],
|
||||
],
|
||||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
},
|
||||
'm3.f3': {
|
||||
axis: 'y2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(actual.dygraphSeries).to.deep.equal(expected.dygraphSeries);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -50,14 +50,12 @@ const App = React.createClass({
|
|||
const {sourceID, base64ExplorerID} = this.props.params;
|
||||
|
||||
return (
|
||||
<div className="chronograf-wrapper--flex">
|
||||
<div className="chronograf-root">
|
||||
<SideNavContainer sourceID={sourceID} explorationID={base64ExplorerID} addFlashMessage={this.handleNotification} currentLocation={this.props.location.pathname} />
|
||||
<div className="page-wrapper">
|
||||
{this.renderNotifications()}
|
||||
{this.props.children && React.cloneElement(this.props.children, {
|
||||
addFlashMessage: this.handleNotification,
|
||||
})}
|
||||
</div>
|
||||
{this.renderNotifications()}
|
||||
{this.props.children && React.cloneElement(this.props.children, {
|
||||
addFlashMessage: this.handleNotification,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -116,7 +116,7 @@ const SearchBar = React.createClass({
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter Alerts"
|
||||
placeholder="Filter Alerts by Name..."
|
||||
ref="searchInput"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
|
|
@ -96,17 +96,17 @@ const AlertsApp = React.createClass({
|
|||
return (
|
||||
// I stole this from the Hosts page.
|
||||
// Perhaps we should create an abstraction?
|
||||
<div className="hosts hosts-page">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>
|
||||
Alert History
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hosts-page-scroll-container">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
|
|
|
@ -40,7 +40,7 @@ const RenamePanelModal = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button className="btn btn-info" data-dismiss="modal">Cancel</button>
|
||||
<button onClick={this.handleConfirm} className="btn btn-success">Rename</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,7 +35,7 @@ const App = React.createClass({
|
|||
const {base64ExplorerID} = this.props.params;
|
||||
|
||||
return (
|
||||
<div className="data-explorer-container">
|
||||
<div className="page">
|
||||
<DataExplorer source={this.props.source} explorerID={this.decodeID(base64ExplorerID)} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -108,8 +108,8 @@ const Header = React.createClass({
|
|||
{text: 'Delete', icon: 'trash', target: '#deleteExplorerModal', handler: this.openDeleteExplorerModal},
|
||||
];
|
||||
return (
|
||||
<div className="chronograf-header data-explorer__header">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page-header data-explorer__header">
|
||||
<div className="page-header__left">
|
||||
<h1 className="dropdown-title">Exploration:</h1>
|
||||
<Dropdown
|
||||
className="sessions-dropdown"
|
||||
|
@ -120,7 +120,7 @@ const Header = React.createClass({
|
|||
/>
|
||||
<div className="btn btn-sm btn-primary sessions-dropdown__btn" onClick={this.handleCreateExploration}>New Exploration</div>
|
||||
</div>
|
||||
<div className="chronograf-header__right">
|
||||
<div className="page-header__right">
|
||||
<h1>Source:</h1>
|
||||
<div className="source-indicator">
|
||||
<span className="icon cpu"></span>
|
||||
|
@ -128,10 +128,6 @@ const Header = React.createClass({
|
|||
</div>
|
||||
<h1>Range:</h1>
|
||||
<TimeRangeDropdown onChooseTimeRange={this.handleChooseTimeRange} selected={this.findSelected(timeRange)} />
|
||||
{/* Placeholder for export functionality
|
||||
<a href="#" className="btn btn-sm btn-info">Export</a> */}
|
||||
{/* Placeholder for create graph functionality
|
||||
<a href="#" className="btn btn-sm btn-primary"><span className="icon graphline"></span> Create Graph</a> */}
|
||||
</div>
|
||||
<DeleteExplorerModal onConfirm={this.confirmDeleteExplorer} />
|
||||
<EditExplorerModal onConfirm={this.confirmEditExplorer} />
|
||||
|
@ -157,7 +153,7 @@ const DeleteExplorerModal = React.createClass({
|
|||
<h4 className="modal-title">Are you sure?</h4>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-default" type="button" data-dismiss="modal">Cancel</button>
|
||||
<button className="btn btn-info" type="button" data-dismiss="modal">Cancel</button>
|
||||
<button onClick={this.handleConfirm} className="btn btn-danger" type="button" data-dismiss="modal">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -203,7 +199,7 @@ const EditExplorerModal = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-default" onClick={this.handleCancel}>Cancel</button>
|
||||
<button className="btn btn-info" onClick={this.handleCancel}>Cancel</button>
|
||||
<input type="submit" value="Rename" className="btn btn-success" />
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -85,10 +85,19 @@ const HostsTable = React.createClass({
|
|||
const sortedHosts = this.sort(this.filter(hosts, searchTerm), sortKey, sortDirection);
|
||||
const hostCount = sortedHosts.length;
|
||||
|
||||
let hostsTitle;
|
||||
if (hostCount === 1) {
|
||||
hostsTitle = `${hostCount} Host`;
|
||||
} else if (hostCount > 1) {
|
||||
hostsTitle = `${hostCount} Hosts`;
|
||||
} else {
|
||||
hostsTitle = `Loading Hosts...`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">{hostCount ? `${hostCount} Hosts` : ''}</h2>
|
||||
<h2 className="panel-title">{hostsTitle}</h2>
|
||||
<SearchBar onSearch={this.updateSearchTerm} />
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
|
@ -192,7 +201,7 @@ const SearchBar = React.createClass({
|
|||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter Hosts"
|
||||
placeholder="Filter by Hostname..."
|
||||
ref="searchInput"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,6 @@ import TimeRangeDropdown from '../../shared/components/TimeRangeDropdown';
|
|||
import timeRanges from 'hson!../../shared/data/timeRanges.hson';
|
||||
import {getMappings, getAppsForHosts, getMeasurementsForHost} from 'src/hosts/apis';
|
||||
import {fetchLayouts} from 'shared/apis';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const HostPage = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -34,7 +33,7 @@ export const HostPage = React.createClass({
|
|||
},
|
||||
|
||||
componentDidMount() {
|
||||
const {source, params} = this.props;
|
||||
const {source, params, location} = this.props;
|
||||
const hosts = {[params.hostID]: {name: params.hostID}};
|
||||
|
||||
// fetching layouts and mappings can be done at the same time
|
||||
|
@ -44,7 +43,7 @@ export const HostPage = React.createClass({
|
|||
getMeasurementsForHost(source, params.hostID).then((measurements) => {
|
||||
const host = newHosts[this.props.params.hostID];
|
||||
const filteredLayouts = layouts.filter((layout) => {
|
||||
const focusedApp = this.props.location.query.app;
|
||||
const focusedApp = location.query.app;
|
||||
if (focusedApp) {
|
||||
return layout.app === focusedApp;
|
||||
}
|
||||
|
@ -68,38 +67,28 @@ export const HostPage = React.createClass({
|
|||
const {timeRange} = this.state;
|
||||
const {source} = this.props;
|
||||
|
||||
const autoflowLayouts = _.remove(layouts, (layout) => {
|
||||
return layout.autoflow === true;
|
||||
});
|
||||
let autoflowCells = [];
|
||||
const autoflowLayouts = layouts.filter((layout) => !!layout.autoflow);
|
||||
|
||||
const cellWidth = 4;
|
||||
const cellHeight = 4;
|
||||
const pageWidth = 12;
|
||||
|
||||
autoflowLayouts.forEach((layout, i) => {
|
||||
layout.cells.forEach((cell, j) => {
|
||||
cell.w = cellWidth;
|
||||
cell.h = cellHeight;
|
||||
cell.x = ((i + j) * cellWidth % pageWidth);
|
||||
cell.y = Math.floor(((i + j) * cellWidth / pageWidth)) * cellHeight;
|
||||
autoflowCells = autoflowCells.concat(cell);
|
||||
});
|
||||
});
|
||||
const autoflowCells = autoflowLayouts.reduce((allCells, layout, i) => {
|
||||
return allCells.concat(layout.cells.map((cell, j) => {
|
||||
return Object.assign(cell, {
|
||||
w: cellWidth,
|
||||
h: cellHeight,
|
||||
x: ((i + j) * cellWidth % pageWidth),
|
||||
y: Math.floor(((i + j) * cellWidth / pageWidth)) * cellHeight,
|
||||
});
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const autoflowLayout = {
|
||||
cells: autoflowCells,
|
||||
autoflow: false,
|
||||
};
|
||||
const staticLayouts = layouts.filter((layout) => !layout.autoflow);
|
||||
staticLayouts.unshift({cells: autoflowCells});
|
||||
|
||||
const staticLayouts = _.remove(layouts, (layout) => {
|
||||
return layout.autoflow === false;
|
||||
});
|
||||
staticLayouts.unshift(autoflowLayout);
|
||||
|
||||
let layoutCells = [];
|
||||
let translateY = 0;
|
||||
staticLayouts.forEach((layout) => {
|
||||
const layoutCells = staticLayouts.reduce((allCells, layout) => {
|
||||
let maxY = 0;
|
||||
layout.cells.forEach((cell) => {
|
||||
cell.y += translateY;
|
||||
|
@ -113,9 +102,8 @@ export const HostPage = React.createClass({
|
|||
});
|
||||
translateY = maxY;
|
||||
|
||||
layoutCells = layoutCells.concat(layout.cells);
|
||||
});
|
||||
|
||||
return allCells.concat(layout.cells);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LayoutRenderer
|
||||
|
@ -133,20 +121,20 @@ export const HostPage = React.createClass({
|
|||
const {layouts, timeRange} = this.state;
|
||||
|
||||
return (
|
||||
<div className="host-dashboard hosts-page">
|
||||
<div className="chronograf-header hosts-dashboard-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>{hostID}</h1>
|
||||
</div>
|
||||
<div className="chronograf-header__right">
|
||||
<div className="page-header__right">
|
||||
<h1>Range:</h1>
|
||||
<TimeRangeDropdown onChooseTimeRange={this.handleChooseTimeRange} selected={timeRange.inputValue} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hosts-page-scroll-container">
|
||||
<div className="container-fluid hosts-dashboard">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid full-width">
|
||||
{ (layouts.length > 0) ? this.renderLayouts(layouts) : '' }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -44,17 +44,17 @@ export const HostsPage = React.createClass({
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div className="hosts hosts-page">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>
|
||||
Host List
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hosts-page-scroll-container">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
|
|
|
@ -101,12 +101,12 @@ const AlertOutputs = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<h4 className="text-center">Alert Endpoints</h4>
|
||||
<br/>
|
||||
<div>
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-body">
|
||||
<h4 className="text-center">Configure Alert Endpoints</h4>
|
||||
<br/>
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-7 col-sm-5 col-sm-offset-2">
|
||||
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2">
|
||||
<label htmlFor="alert-endpoint" className="sr-only">Alert Enpoint</label>
|
||||
<select value={this.state.selectedEndpoint} className="form-control" id="source" onChange={this.changeSelectedEndpoint}>
|
||||
<option value="hipchat">HipChat</option>
|
||||
|
@ -120,7 +120,12 @@ const AlertOutputs = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
{this.renderAlertConfig(selectedEndpoint)}
|
||||
<div className="col-xs-12 col-sm-8 col-sm-offset-2">
|
||||
<hr/>
|
||||
</div>
|
||||
<div className="col-xs-12 col-sm-8 col-sm-offset-2">
|
||||
{this.renderAlertConfig(selectedEndpoint)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@ const AlertaConfig = React.createClass({
|
|||
const {environment, origin, token, url} = this.props.config.options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div className="col-xs-12 col-sm-8 col-sm-offset-2">
|
||||
<h4 className="text-center">Alerta Alert</h4>
|
||||
<br/>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
|
|
|
@ -34,58 +34,44 @@ const HipchatConfig = React.createClass({
|
|||
const {url, global, room, token} = options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">HipChat Alert</h4>
|
||||
<br/>
|
||||
<p>Have alerts sent to HipChat.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
Have alerts sent to HipChat
|
||||
</p>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">HipChat URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="url">HipChat URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="room">Room</label>
|
||||
<input className="form-control" id="room" type="text" ref={(r) => this.room = r} defaultValue={room || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="room">Room</label>
|
||||
<input className="form-control" id="room" type="text" ref={(r) => this.room = r} defaultValue={room || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">Token</label>
|
||||
<input className="form-control" id="token" type="text" ref={(r) => this.token = r} defaultValue={token || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the HipChat token has been set</label>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="token">Token</label>
|
||||
<input className="form-control" id="token" type="text" ref={(r) => this.token = r} defaultValue={token || ''}></input>
|
||||
<span>Note: a value of <code>true</code> indicates the HipChat token has been set</span>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
Send all alerts without marking them explicitly in TICKscript
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={stateChangesOnly} ref={(r) => this.stateChangesOnly = r} />
|
||||
Send alerts on state change only
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="sendAllAlertsWithoutMarking" type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
<label htmlFor="sendAllAlertsWithoutMarking">Send all alerts without marking them explicitly in TICKscript</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="stateChangesOnly" type="checkbox" defaultChecked={stateChangesOnly} ref={(r) => this.stateChangesOnly = r} />
|
||||
<label htmlFor="stateChangesOnly">Send alerts on state change only</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -30,14 +30,14 @@ export const KapacitorRule = React.createClass({
|
|||
const {chooseTrigger, updateRuleValues} = kapacitorActions;
|
||||
|
||||
return (
|
||||
<div className="kapacitor-rule-page">
|
||||
<div className="page">
|
||||
<RuleHeader
|
||||
rule={rule}
|
||||
actions={kapacitorActions}
|
||||
onSave={isEditing ? this.handleEdit : this.handleCreate}
|
||||
validationError={this.validationError()}
|
||||
/>
|
||||
<div className="rule-builder-wrapper">
|
||||
<div className="page-contents page-contents--green-scrollbar">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-xs-12">
|
||||
|
|
|
@ -30,44 +30,31 @@ const PagerDutyConfig = React.createClass({
|
|||
const serviceKey = options['service-key'];
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">PagerDuty Alert</h4>
|
||||
<br/>
|
||||
<p>You can have alerts sent to PagerDuty by entering info below.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
You can have alerts sent to PagerDuty by entering info below.
|
||||
</p>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="service-key">Service Key</label>
|
||||
<input className="form-control" id="service-key" type="text" ref={(r) => this.serviceKey = r} defaultValue={serviceKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the PagerDuty service key has been set</label>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="service-key">Service Key</label>
|
||||
<input className="form-control" id="service-key" type="text" ref={(r) => this.serviceKey = r} defaultValue={serviceKey || ''}></input>
|
||||
<span>Note: a value of <code>true</code> indicates the PagerDuty service key has been set</span>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="url">PagerDuty URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
Send all alerts without marking them explicitly in TICKscript
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">PagerDuty URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="sendAllAlertsWithoutMarking" type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
<label htmlFor="sendAllAlertsWithoutMarking">Send all alerts without marking them explicitly in TICKscript</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -44,12 +44,12 @@ export const RuleHeader = React.createClass({
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
{this.renderEditName()}
|
||||
</div>
|
||||
<div className="chronograf-header__right">
|
||||
<div className="page-header__right">
|
||||
{this.renderSave()}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,6 +80,7 @@ export const RuleHeader = React.createClass({
|
|||
return (
|
||||
<h1 className="chronograf-header__editable" onClick={this.toggleEditName} data-for="rename-kapacitor-tooltip" data-tip="Click to Rename">
|
||||
{rule.name}
|
||||
<span className="icon pencil"></span>
|
||||
<ReactTooltip id="rename-kapacitor-tooltip" delayShow="200" effect="solid" html={true} offset={{top: 2}} place="bottom" class="influx-tooltip kapacitor-tooltip place-bottom" />
|
||||
</h1>
|
||||
);
|
||||
|
|
|
@ -52,7 +52,7 @@ export const RuleMessage = React.createClass({
|
|||
</div>
|
||||
<div className="rule-section--item bottom alert-message--endpoint">
|
||||
<p>Send this Alert to:</p>
|
||||
<Dropdown className="size-256" selected={rule.alerts[0] || 'Choose an output'} items={alerts} onChoose={this.handleChooseAlert} />
|
||||
<Dropdown className="size-256 dropdown-kapacitor" selected={rule.alerts[0] || 'Choose an output'} items={alerts} onChoose={this.handleChooseAlert} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,48 +32,38 @@ const SMTPConfig = React.createClass({
|
|||
const {host, port, from, username, password} = this.props.config.options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">SMTP Alert</h4>
|
||||
<br/>
|
||||
<p>You can have alerts sent to an email address by setting up an SMTP endpoint.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
You can have alerts sent to an email address by setting up an SMTP endpoint.
|
||||
</p>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="smtp-host">SMTP Host</label>
|
||||
<input className="form-control" id="smtp-host" type="text" ref={(r) => this.host = r} defaultValue={host || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="smtp-port">SMTP Port</label>
|
||||
<input className="form-control" id="smtp-port" type="text" ref={(r) => this.port = r} defaultValue={port || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="smtp-from">From email</label>
|
||||
<input className="form-control" id="smtp-from" type="text" ref={(r) => this.from = r} defaultValue={from || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="smtp-user">User</label>
|
||||
<input className="form-control" id="smtp-user" type="text" ref={(r) => this.username = r} defaultValue={username || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="smtp-password">Password</label>
|
||||
<input className="form-control" id="smtp-password" type="password" ref={(r) => this.password = r} defaultValue={`${password}`}></input>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-host">SMTP Host</label>
|
||||
<input className="form-control" id="smtp-host" type="text" ref={(r) => this.host = r} defaultValue={host || ''}></input>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-port">SMTP Port</label>
|
||||
<input className="form-control" id="smtp-port" type="text" ref={(r) => this.port = r} defaultValue={port || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="smtp-from">From Email</label>
|
||||
<input className="form-control" id="smtp-from" placeholder="email@domain.com" type="text" ref={(r) => this.from = r} defaultValue={from || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-user">User</label>
|
||||
<input className="form-control" id="smtp-user" type="text" ref={(r) => this.username = r} defaultValue={username || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-password">Password</label>
|
||||
<input className="form-control" id="smtp-password" type="password" ref={(r) => this.password = r} defaultValue={`${password}`}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -26,33 +26,23 @@ const SensuConfig = React.createClass({
|
|||
const {source, addr} = this.props.config.options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">Sensu Alert</h4>
|
||||
<br/>
|
||||
<p>Have alerts sent to Sensu.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
Have alerts sent to Sensu
|
||||
</p>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="source">Source</label>
|
||||
<input className="form-control" id="source" type="text" ref={(r) => this.source = r} defaultValue={source || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="address">Address</label>
|
||||
<input className="form-control" id="address" type="text" ref={(r) => this.addr = r} defaultValue={addr || ''}></input>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="source">Source</label>
|
||||
<input className="form-control" id="source" type="text" ref={(r) => this.source = r} defaultValue={source || ''}></input>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="address">Address</label>
|
||||
<input className="form-control" id="address" type="text" ref={(r) => this.addr = r} defaultValue={addr || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -47,39 +47,25 @@ const SlackConfig = React.createClass({
|
|||
const {url, channel} = this.props.config.options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">Slack Alert</h4>
|
||||
<br/>
|
||||
<p>Post alerts to a Slack channel.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
Post alerts to a Slack channel.
|
||||
</p>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="slack-url">Slack Webhook URL (<a href="https://api.slack.com/incoming-webhooks" target="_">see more on Slack webhooks</a>)</label>
|
||||
<input className="form-control" id="slack-url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<span>Note: a value of <code>true</code> indicates that the Slack channel has been set</span>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="slack-channel">Slack Channel (optional)</label>
|
||||
<input className="form-control" id="slack-channel" type="text" placeholder="#alerts" ref={(r) => this.channel = r} defaultValue={channel || ''}></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="slack-url">Slack Webhook URL (<a href="https://api.slack.com/incoming-webhooks" target="_">see more on Slack webhooks</a>)</label>
|
||||
<input className="form-control" id="slack-url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates that the Slack channel has been set</label>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="slack-channel">Slack Channel (optional)</label>
|
||||
<input className="form-control" id="slack-channel" type="text" placeholder="#alerts" ref={(r) => this.channel = r} defaultValue={channel || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<a className="btn btn-warning" onClick={this.handleTest} disabled={!this.state.testEnabled}>Send Test Message</a>
|
||||
</div>
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<a className="btn btn-warning" onClick={this.handleTest} disabled={!this.state.testEnabled}>Send Test Message</a>
|
||||
<button className="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -44,86 +44,65 @@ const TelegramConfig = React.createClass({
|
|||
const stateChangesOnly = options['state-changes-only'];
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">Telegram Alert</h4>
|
||||
<br/>
|
||||
<p>You can have alerts sent to Telegram by entering info below.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
You can have alerts sent to Telegram by entering info below.
|
||||
</p>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">Telegram URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="url">Telegram URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">Token</label>
|
||||
<input className="form-control" id="token" type="text" ref={(r) => this.token = r} defaultValue={token || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the Telegram token has been set</label>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="token">Token</label>
|
||||
<input className="form-control" id="token" type="text" ref={(r) => this.token = r} defaultValue={token || ''}></input>
|
||||
<span>Note: a value of <code>true</code> indicates the Telegram token has been set</span>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="chat-id">Chat ID</label>
|
||||
<input className="form-control" id="chat-id" type="text" ref={(r) => this.chatID = r} defaultValue={chatID || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={parseMode} ref={(r) => this.parseMode = r} />
|
||||
Enable Parse Mode
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={disableWebPagePreview} ref={(r) => this.disableWebPagePreview = r} />
|
||||
Disable Web Page Preview
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={disableNotification} ref={(r) => this.disableNotification = r} />
|
||||
Disable Notification
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
Send all alerts without marking them explicitly in TICKscript
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" defaultChecked={stateChangesOnly} ref={(r) => this.stateChangesOnly = r} />
|
||||
Send alerts on state change only
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="chat-id">Chat ID</label>
|
||||
<input className="form-control" id="chat-id" type="text" ref={(r) => this.chatID = r} defaultValue={chatID || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="enableParseMode" type="checkbox" defaultChecked={parseMode} ref={(r) => this.parseMode = r} />
|
||||
<label htmlFor="enableParseMode">Enable Parse Mode</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="disableWebPagePreview" type="checkbox" defaultChecked={disableWebPagePreview} ref={(r) => this.disableWebPagePreview = r} />
|
||||
<label htmlFor="disableWebPagePreview">Disable Web Page Preview</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="disableNotification" type="checkbox" defaultChecked={disableNotification} ref={(r) => this.disableNotification = r} />
|
||||
<label htmlFor="disableNotification">Disable Notification</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="sendAllAlertsWithoutMarking" type="checkbox" defaultChecked={global} ref={(r) => this.global = r} />
|
||||
<label htmlFor="sendAllAlertsWithoutMarking">Send all alerts without marking them explicitly in TICKscript</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="stateChangesOnly" type="checkbox" defaultChecked={stateChangesOnly} ref={(r) => this.stateChangesOnly = r} />
|
||||
<label htmlFor="stateChangesOnly">Send alerts on state change only</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -101,8 +101,8 @@ const Threshold = React.createClass({
|
|||
<p>Send Alert where</p>
|
||||
<span>{query.fields.length ? query.fields[0].field : 'Select a Metric'}</span>
|
||||
<p>is</p>
|
||||
<Dropdown className="size-176" items={operators} selected={operator} onChoose={this.handleDropdownChange} />
|
||||
<input className="form-control input-sm size-166" type="text" ref={(r) => this.valueInput = r} defaultValue={value} onKeyUp={this.handleInputChange}></input>
|
||||
<Dropdown className="size-176 dropdown-kapacitor" items={operators} selected={operator} onChoose={this.handleDropdownChange} />
|
||||
<input className="form-control input-sm size-166 form-control--green" type="text" ref={(r) => this.valueInput = r} defaultValue={value} onKeyUp={this.handleInputChange}></input>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -145,13 +145,13 @@ const Relative = React.createClass({
|
|||
return (
|
||||
<div className="value-selector">
|
||||
<p>Send Alert when</p>
|
||||
<Dropdown className="size-106"items={changes} selected={change} onChoose={this.handleDropdownChange} />
|
||||
<Dropdown className="size-106 dropdown-kapacitor"items={changes} selected={change} onChoose={this.handleDropdownChange} />
|
||||
<p>compared to previous</p>
|
||||
<Dropdown className="size-66" items={shifts} selected={shift} onChoose={this.handleDropdownChange} />
|
||||
<Dropdown className="size-66 dropdown-kapacitor" items={shifts} selected={shift} onChoose={this.handleDropdownChange} />
|
||||
<p>is</p>
|
||||
<Dropdown className="size-176" items={operators} selected={operator} onChoose={this.handleDropdownChange} />
|
||||
<Dropdown className="size-176 dropdown-kapacitor" items={operators} selected={operator} onChoose={this.handleDropdownChange} />
|
||||
<input
|
||||
className="form-control input-sm size-166"
|
||||
className="form-control input-sm size-166 form-control--green"
|
||||
ref={(r) => this.input = r}
|
||||
defaultValue={value}
|
||||
onKeyUp={this.handleInputChange}
|
||||
|
@ -186,7 +186,7 @@ const Deadman = React.createClass({
|
|||
return (
|
||||
<div className="value-selector">
|
||||
<p>Send Alert if Data is missing for</p>
|
||||
<Dropdown className="size-66" items={periods} selected={this.props.rule.values.period} onChoose={this.handleChange} />
|
||||
<Dropdown className="size-66 dropdown-kapacitor" items={periods} selected={this.props.rule.values.period} onChoose={this.handleChange} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -31,39 +31,29 @@ const VictorOpsConfig = React.createClass({
|
|||
const {url} = options;
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
<div>
|
||||
<h4 className="text-center">VictorOps Alert</h4>
|
||||
<br/>
|
||||
<p>Have alerts sent to VictorOps.</p>
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="row">
|
||||
<div className="col-xs-7 col-sm-8 col-sm-offset-2">
|
||||
<p>
|
||||
Have alerts sent to VictorOps
|
||||
</p>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="api-key">API Key</label>
|
||||
<input className="form-control" id="api-key" type="text" ref={(r) => this.apiKey = r} defaultValue={apiKey || ''}></input>
|
||||
<span>Note: a value of <code>true</code> indicates the VictorOps API key has been set</span>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="routing-key">Routing Key</label>
|
||||
<input className="form-control" id="routing-key" type="text" ref={(r) => this.routingKey = r} defaultValue={routingKey || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="url">VictorOps URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="api-key">API Key</label>
|
||||
<input className="form-control" id="api-key" type="text" ref={(r) => this.apiKey = r} defaultValue={apiKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the VictorOps API key has been set</label>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div className="row">
|
||||
<div className="form-group col-xs-5 col-sm-3 col-sm-offset-2">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="routing-key">Routing Key</label>
|
||||
<input className="form-control" id="routing-key" type="text" ref={(r) => this.routingKey = r} defaultValue={routingKey || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">VictorOps URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -117,61 +117,62 @@ export const KapacitorPage = React.createClass({
|
|||
const username = newUsername === undefined ? kapacitor && kapacitor.username || '' : newUsername;
|
||||
|
||||
return (
|
||||
<div className="kapacitor">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>
|
||||
Configure Kapacitor
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
Kapacitor is used as the monitoring and alerting agent.
|
||||
This page will let you configure which Kapacitor to use and
|
||||
set up alert end points like email, Slack, and others.
|
||||
</p>
|
||||
<hr/>
|
||||
<h4 className="text-center">Connection Details</h4>
|
||||
<br/>
|
||||
<form onSubmit={this.handleKapacitorUpdate}>
|
||||
<div>
|
||||
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-2">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input ref={(r) => this.kapacitorURL = r} className="form-control" id="connect-string" placeholder="http://localhost:9092" value={url} onChange={this.updateURL}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-0">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input ref={(r) => this.kapacitorName = r} className="form-control" id="name" placeholder="My Kapacitor" value={name} onChange={this.updateName}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-4 col-sm-offset-2 col-md-4 col-md-offset-2">
|
||||
<label htmlFor="username">Username</label>
|
||||
<input ref={(r) => this.kapacitorUser = r} className="form-control" id="username" value={username} onChange={this.updateUsername}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-4 col-md-4">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input ref={(r) => this.kapacitorPassword = r} className="form-control" id="password" type="password"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-body">
|
||||
<p>
|
||||
Kapacitor is used as the monitoring and alerting agent.
|
||||
This page will let you configure which Kapacitor to use and
|
||||
set up alert end points like email, Slack, and others.
|
||||
</p>
|
||||
<hr/>
|
||||
<h4 className="text-center">Connection Details</h4>
|
||||
<br/>
|
||||
<form onSubmit={this.handleKapacitorUpdate}>
|
||||
<div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input ref={(r) => this.kapacitorURL = r} className="form-control" id="connect-string" placeholder="http://localhost:9092" value={url} onChange={this.updateURL}></input>
|
||||
<div className="form-group form-group-submit col-xs-4 col-xs-offset-4">
|
||||
<button className="btn btn-block btn-success" type="submit">Connect Kapacitor</button>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input ref={(r) => this.kapacitorName = r} className="form-control" id="name" placeholder="My Kapacitor" value={name} onChange={this.updateName}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<label htmlFor="username">Username</label>
|
||||
<input ref={(r) => this.kapacitorUser = r} className="form-control" id="username" value={username} onChange={this.updateUsername}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input ref={(r) => this.kapacitorPassword = r} className="form-control" id="password" type="password"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-4 col-xs-offset-4">
|
||||
<button className="btn btn-block btn-success" type="submit">Connect Kapacitor</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
{this.renderAlertOutputs()}
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
{this.renderAlertOutputs()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -186,8 +187,10 @@ export const KapacitorPage = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="panel-body">
|
||||
Set your Kapacitor connection info to configure alerting endpoints.
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-body">
|
||||
<p>Set your Kapacitor connection info to configure alerting endpoints.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -90,15 +90,15 @@ export const KapacitorRulesPage = React.createClass({
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div className="kapacitor-rules-page">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>Kapacitor Rules</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hosts-page-scroll-container">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
{this.renderSubComponent()}
|
||||
</div>
|
||||
|
|
|
@ -66,20 +66,20 @@ export const KubernetesPage = React.createClass({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="host-dashboard hosts-page">
|
||||
<div className="chronograf-header hosts-dashboard-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page">
|
||||
<div className="page-header full-width">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>Kubernetes Dashboard</h1>
|
||||
</div>
|
||||
<div className="chronograf-header__right">
|
||||
<div className="page-header__right">
|
||||
<h1>Range:</h1>
|
||||
<TimeRangeDropdown onChooseTimeRange={this.handleChooseTimeRange} selected={timeRange.inputValue} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hosts-page-scroll-container">
|
||||
<div className="container-fluid hosts-dashboard">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid full-width">
|
||||
{layouts.length ? this.renderLayouts(layouts) : emptyState}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -51,7 +51,7 @@ const Dropdown = React.createClass({
|
|||
<ul className="dropdown-menu show">
|
||||
{items.map((item, i) => {
|
||||
return (
|
||||
<li className={`dropdown ${className}__item`} key={i}>
|
||||
<li className="dropdown-item" key={i}>
|
||||
<a href="#" onClick={() => self.handleSelection(item)}>
|
||||
{item.text}
|
||||
</a>
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, {PropTypes} from 'react';
|
|||
import Dygraph from '../../external/dygraph';
|
||||
import 'style/_Graph.css';
|
||||
|
||||
const {arrayOf, object, array, number, bool} = PropTypes;
|
||||
const {arrayOf, object, array, number, bool, shape} = PropTypes;
|
||||
|
||||
const LINE_COLORS = [
|
||||
'#00C9FF',
|
||||
|
@ -25,13 +25,17 @@ export default React.createClass({
|
|||
displayName: 'Dygraph',
|
||||
|
||||
propTypes: {
|
||||
yRange: arrayOf(number.isRequired),
|
||||
ranges: shape({
|
||||
y: arrayOf(number.isRequired),
|
||||
y2: arrayOf(number.isRequired),
|
||||
}),
|
||||
timeSeries: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
fields: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
labels: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
options: object, // eslint-disable-line react/forbid-prop-types
|
||||
containerStyle: object, // eslint-disable-line react/forbid-prop-types
|
||||
isGraphFilled: bool,
|
||||
overrideLineColors: array,
|
||||
dygraphSeries: shape({}).isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -50,7 +54,8 @@ export default React.createClass({
|
|||
|
||||
componentDidMount() {
|
||||
const timeSeries = this.getTimeSeries();
|
||||
const {yRange} = this.props;
|
||||
// dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'};
|
||||
const {ranges, dygraphSeries} = this.props;
|
||||
|
||||
const refs = this.refs;
|
||||
const graphContainerNode = refs.graphContainer;
|
||||
|
@ -75,7 +80,15 @@ export default React.createClass({
|
|||
strokeWidth: 1.5,
|
||||
highlightCircleSize: 3,
|
||||
colors: finalLineColors,
|
||||
valueRange: getRange(timeSeries, yRange),
|
||||
series: dygraphSeries,
|
||||
axes: {
|
||||
y: {
|
||||
valueRange: getRange(timeSeries, ranges.y),
|
||||
},
|
||||
y2: {
|
||||
valueRange: getRange(timeSeries, ranges.y2),
|
||||
},
|
||||
},
|
||||
highlightSeriesOpts: {
|
||||
strokeWidth: 2,
|
||||
highlightCircleSize: 5,
|
||||
|
@ -130,12 +143,19 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
const timeSeries = this.getTimeSeries();
|
||||
const {fields, yRange} = this.props;
|
||||
const {labels, ranges} = this.props;
|
||||
|
||||
dygraph.updateOptions({
|
||||
labels: fields,
|
||||
labels,
|
||||
file: timeSeries,
|
||||
valueRange: getRange(timeSeries, yRange),
|
||||
axes: {
|
||||
y: {
|
||||
valueRange: getRange(timeSeries, ranges.y),
|
||||
},
|
||||
y2: {
|
||||
valueRange: getRange(timeSeries, ranges.y2),
|
||||
},
|
||||
},
|
||||
underlayCallback: this.props.options.underlayCallback,
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,12 @@ export const LayoutRenderer = React.createClass({
|
|||
PropTypes.shape({
|
||||
queries: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
rp: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
range: PropTypes.shape({
|
||||
upper: PropTypes.number,
|
||||
lower: PropTypes.number,
|
||||
}),
|
||||
rp: PropTypes.string,
|
||||
text: PropTypes.string.isRequired,
|
||||
database: PropTypes.string.isRequired,
|
||||
groupbys: PropTypes.arrayOf(PropTypes.string),
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import React, {PropTypes} from 'react';
|
||||
import Dygraph from './Dygraph';
|
||||
import shallowCompare from 'react-addons-shallow-compare';
|
||||
import _ from 'lodash';
|
||||
|
||||
import timeSeriesToDygraph from 'utils/timeSeriesToDygraph';
|
||||
|
||||
const {array, string, arrayOf, number, bool} = PropTypes;
|
||||
const {array, string, arrayOf, bool, shape} = PropTypes;
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'LineGraph',
|
||||
propTypes: {
|
||||
data: array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||
data: arrayOf(shape({}).isRequired).isRequired,
|
||||
title: string,
|
||||
isFetchingInitially: PropTypes.bool,
|
||||
isRefreshing: PropTypes.bool,
|
||||
yRange: arrayOf(number.isRequired),
|
||||
underlayCallback: PropTypes.func,
|
||||
isGraphFilled: bool,
|
||||
overrideLineColors: array,
|
||||
queries: arrayOf(shape({}).isRequired).isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -30,19 +31,22 @@ export default React.createClass({
|
|||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
this._timeSeries = timeSeriesToDygraph(this.props.data);
|
||||
},
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
if (this.props.data !== nextProps.data) {
|
||||
this._timeSeries = timeSeriesToDygraph(nextProps.data);
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {fields, timeSeries} = this._timeSeries;
|
||||
|
||||
render() {
|
||||
const {isFetchingInitially, title, underlayCallback, queries} = this.props;
|
||||
const {labels, timeSeries, dygraphSeries} = this._timeSeries;
|
||||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||
if (this.props.isFetchingInitially) {
|
||||
if (isFetchingInitially) {
|
||||
return (
|
||||
<div className="graph-panel__graph-fetching">
|
||||
<h3 className="graph-panel__spinner" />
|
||||
|
@ -51,24 +55,56 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
const options = {
|
||||
labels: fields,
|
||||
labels,
|
||||
connectSeparatedPoints: true,
|
||||
labelsKMB: true,
|
||||
height: 300,
|
||||
axisLineColor: '#383846',
|
||||
gridLineColor: '#383846',
|
||||
title: this.props.title,
|
||||
title,
|
||||
rightGap: 0,
|
||||
yRangePad: 10,
|
||||
drawAxesAtZero: true,
|
||||
underlayCallback: this.props.underlayCallback,
|
||||
underlayCallback,
|
||||
ylabel: _.get(queries, ['0', 'label'], ''),
|
||||
y2label: _.get(queries, ['1', 'label'], ''),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.isRefreshing ? <h3 className="graph-panel__spinner--small" /> : null}
|
||||
<Dygraph containerStyle={{width: '100%', height: '300px'}} overrideLineColors={this.props.overrideLineColors} isGraphFilled={this.props.isGraphFilled} timeSeries={timeSeries} fields={fields} options={options} />
|
||||
<Dygraph
|
||||
containerStyle={{width: '100%', height: '300px'}}
|
||||
overrideLineColors={this.props.overrideLineColors}
|
||||
isGraphFilled={this.props.isGraphFilled}
|
||||
timeSeries={timeSeries}
|
||||
labels={labels}
|
||||
options={options}
|
||||
dygraphSeries={dygraphSeries}
|
||||
ranges={this.getRanges()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
getRanges() {
|
||||
const {queries} = this.props;
|
||||
if (!queries) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const ranges = {};
|
||||
const q0 = queries[0];
|
||||
const q1 = queries[1];
|
||||
|
||||
if (q0 && q0.range) {
|
||||
ranges.y = [q0.range.lower, q0.range.upper];
|
||||
}
|
||||
|
||||
if (q1 && q1.range) {
|
||||
ranges.y2 = [q1.range.lower, q1.range.upper];
|
||||
}
|
||||
|
||||
return ranges;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -41,27 +41,27 @@ export const CreateSource = React.createClass({
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div id="select-source-page">
|
||||
<div className="select-source-page" id="select-source-page">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel panel-summer">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading text-center">
|
||||
<h2 className="deluxe">Welcome to Chronograf</h2>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<h4 className="text-center">Connect to a New Server</h4>
|
||||
<h4 className="text-center">Connect to a New Source</h4>
|
||||
<br/>
|
||||
|
||||
<form onSubmit={this.handleNewSource}>
|
||||
<div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input ref={(r) => this.sourceURL = r} className="form-control" id="connect-string" placeholder="http://localhost:8086"></input>
|
||||
<input ref={(r) => this.sourceURL = r} className="form-control" id="connect-string" defaultValue="http://localhost:8086"></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input ref={(r) => this.sourceName = r} className="form-control" id="name" placeholder="Influx 1"></input>
|
||||
<input ref={(r) => this.sourceName = r} className="form-control" id="name" defaultValue="Influx 1"></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<label htmlFor="username">Username</label>
|
||||
|
@ -73,11 +73,11 @@ export const CreateSource = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
<div className="form-group col-xs-8 col-xs-offset-2">
|
||||
<label htmlFor="telegraf">Telegraf database</label>
|
||||
<input ref={(r) => this.sourceTelegraf = r} className="form-control" id="telegraf" type="text" value="telegraf"></input>
|
||||
<label htmlFor="telegraf">Telegraf Database</label>
|
||||
<input ref={(r) => this.sourceTelegraf = r} className="form-control" id="telegraf" type="text" defaultValue="telegraf"></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 text-center">
|
||||
<button className="btn btn-success" type="submit">Create New Server</button>
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<button className="btn btn-success" type="submit">Connect New Source</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -64,53 +64,55 @@ export const ManageSources = React.createClass({
|
|||
const sourcesTitle = `${numSources} ${numSources === 1 ? 'Source' : 'Sources'}`;
|
||||
|
||||
return (
|
||||
<div id="manage-sources-page">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page" id="manage-sources-page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>InfluxDB Sources</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">{sourcesTitle}</h2>
|
||||
<Link to={`/sources/${this.props.source.id}/manage-sources/new`} className="btn btn-sm btn-primary">Add New Source</Link>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<div className="table-responsive margin-bottom-zero">
|
||||
<table className="table v-center margin-bottom-zero">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Host</th>
|
||||
<th>Kapacitor</th>
|
||||
<th className="text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
sources.map((source) => {
|
||||
return (
|
||||
<tr key={source.id}>
|
||||
<td>{source.name}{source.default ? <span className="label label-primary">Default</span> : null}</td>
|
||||
<td>{source.url}</td>
|
||||
<td>{_.get(source, ['kapacitor', 'name'], '')}</td>
|
||||
<td className="text-right">
|
||||
<Link className="btn btn-default btn-xs" to={`${pathname}/${source.id}/edit`}>Edit</Link>
|
||||
<Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link>
|
||||
<button className="btn btn-danger btn-xs" onClick={() => this.handleDeleteSource(source)}>Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<h2 className="panel-title">{sourcesTitle}</h2>
|
||||
<Link to={`/sources/${this.props.source.id}/manage-sources/new`} className="btn btn-sm btn-primary">Add New Source</Link>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<div className="table-responsive margin-bottom-zero">
|
||||
<table className="table v-center margin-bottom-zero">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Host</th>
|
||||
<th>Kapacitor</th>
|
||||
<th className="text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
sources.map((source) => {
|
||||
return (
|
||||
<tr key={source.id}>
|
||||
<td>{source.name}{source.default ? <span className="default-source-label">Default</span> : null}</td>
|
||||
<td>{source.url}</td>
|
||||
<td>{_.get(source, ['kapacitor', 'name'], '')}</td>
|
||||
<td className="text-right">
|
||||
<Link className="btn btn-info btn-xs" to={`${pathname}/${source.id}/edit`}><span className="icon pencil"></span></Link>
|
||||
<Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link>
|
||||
<button className="btn btn-danger btn-xs" onClick={() => this.handleDeleteSource(source)}><span className="icon trash"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -82,58 +82,57 @@ export const SourceForm = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div id="source-form-page">
|
||||
<div className="chronograf-header">
|
||||
<div className="chronograf-header__container">
|
||||
<div className="chronograf-header__left">
|
||||
<div className="page" id="source-form-page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1>
|
||||
{editMode ? "Edit Source" : "Add a New Source"}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel panel-summer">
|
||||
<div className="panel-body">
|
||||
<h4 className="text-center">Connection Details</h4>
|
||||
<br/>
|
||||
<div className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-md-offset-2">
|
||||
<div className="panel panel-minimal">
|
||||
<div className="panel-body">
|
||||
<h4 className="text-center">Connection Details</h4>
|
||||
<br/>
|
||||
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input type="text" name="url" ref={(r) => this.sourceURL = r} className="form-control" id="connect-string" placeholder="http://localhost:8086" onChange={this.onInputChange} value={source.url || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4">
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input type="text" name="name" ref={(r) => this.sourceName = r} className="form-control" id="name" placeholder="Influx 1" onChange={this.onInputChange} value={source.name || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4 col-sm-offset-2">
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="username">Username</label>
|
||||
<input type="text" name="username" ref={(r) => this.sourceUsername = r} className="form-control" id="username" onChange={this.onInputChange} value={source.username || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-6 col-sm-4">
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input type="password" name="password" ref={(r) => this.sourcePassword = r} className="form-control" id="password" onChange={this.onInputChange} value={source.password || ''}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-8 col-xs-offset-2">
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="telegraf">Telegraf database</label>
|
||||
<input type="text" name="telegraf" ref={(r) => this.sourceTelegraf = r} className="form-control" id="telegraf" onChange={this.onInputChange} value={source.telegraf || 'telegraf'}></input>
|
||||
</div>
|
||||
<div className="form-group col-xs-8 col-xs-offset-2">
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input type="checkbox" id="defaultSourceCheckbox" defaultChecked={source.default} ref={(r) => this.sourceDefault = r} />
|
||||
<label htmlFor="defaultSourceCheckbox">Make this the default source</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-4 col-xs-offset-4">
|
||||
<button className={classNames('btn btn-block', {'btn-primary': editMode, 'btn-success': !editMode})} type="submit">{editMode ? "Save Changes" : "Add Source"}</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className={classNames('btn btn-block', {'btn-primary': editMode, 'btn-success': !editMode})} type="submit">{editMode ? "Save Changes" : "Add Source"}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -316,7 +316,7 @@ $sidebar-logo-color: $g8-storm;
|
|||
color: $g20-white;
|
||||
height: $sidebar-width;
|
||||
line-height: $sidebar-width;
|
||||
font-size: 20px;
|
||||
font-size: 17px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
padding: 0px $sidebar-menu-gutter;
|
||||
|
|
|
@ -137,20 +137,6 @@
|
|||
|
||||
.dropdown-toggle {
|
||||
width: 160px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: calc(50% + 1px);
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,134 +1,20 @@
|
|||
.sessions-dropdown {
|
||||
margin: 0 0 0 4px !important;
|
||||
|
||||
&__item {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
a {
|
||||
background-color: $g7-graphite;
|
||||
}
|
||||
.dropdown-item__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> a {
|
||||
padding: 8px 65px 8px 11px !important;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
margin: 0 0 0 4px;
|
||||
|
||||
.dropdown-toggle {
|
||||
text-align: left;
|
||||
position: relative;
|
||||
width: 21em;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: calc(50% + 1px);
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
max-height: 450px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 16px;
|
||||
background-color: $g3-castle;
|
||||
border-radius: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-button {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: $g3-castle;
|
||||
border-radius: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background-color: $g3-castle;
|
||||
border-radius: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: $g8-storm;
|
||||
border: 4px solid $g3-castle;
|
||||
border-radius: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-corner {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
&::-webkit-resizer {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
}
|
||||
.dropdown-item__actions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 11px;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
.dropdown-item__action {
|
||||
border: 0;
|
||||
margin-left: 4px;
|
||||
border-radius: $radius;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: transparent;
|
||||
color: $g10-wolf;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
background-color: $g9-mountain;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
width: 227px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
}
|
||||
.sessions-dropdown__btn {
|
||||
margin: 0 0 0 4px;
|
||||
margin: 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
.group-by-time-dropdown {
|
||||
.group-by-time-dropdown .dropdown-toggle {
|
||||
width: $group-by-time-dropdown-width;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: calc(50% + 1px);
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
width: $group-by-time-dropdown-width;
|
||||
min-width: 2em; // Override bootstrap min-width
|
||||
}
|
||||
}
|
||||
|
||||
.source-indicator {
|
||||
padding: 4px 8px;
|
||||
padding: 3px 9px;
|
||||
border: 2px solid $g5-pepper;
|
||||
background-color: $g5-pepper;
|
||||
color: $g13-mist;
|
||||
|
@ -136,11 +22,11 @@
|
|||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
margin-right: 18px;
|
||||
margin-right: 16px;
|
||||
|
||||
> .icon {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 0 5px 0 0;
|
||||
margin: 0 4px 0 -2px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
/*
|
||||
This is a bunch of stuff built on top of the bootstrap theme for the Chronograf app.
|
||||
*/
|
||||
|
||||
html, body {
|
||||
background-color: $g0-obsidian;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
|
@ -9,24 +11,18 @@ body {
|
|||
height: 100%;
|
||||
position: absolute;
|
||||
align-items: stretch;
|
||||
/* Ensures sidebar and page-wrapper go full-height */
|
||||
overflow: hidden;
|
||||
background-color: $g0-obsidian;
|
||||
}
|
||||
|
||||
/*
|
||||
Chronograf Select Host Page
|
||||
----------------------------------------------
|
||||
*/
|
||||
body > #react-root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
> #react-root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
.container {
|
||||
margin-top: 60px;
|
||||
.container {
|
||||
margin-top: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,76 +30,66 @@ body > #react-root {
|
|||
Layout
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.chronograf-wrapper {
|
||||
.chronograf-root {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&--flex {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: $g17-whisper;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
/* Overriding left and right padding from the theme to be more spacious */
|
||||
padding-left: $page-wrapper-padding;
|
||||
padding-right: $page-wrapper-padding;
|
||||
max-width: $page-wrapper-max-width;
|
||||
}
|
||||
.page-wrapper,
|
||||
.sidebar {
|
||||
display: block;
|
||||
}
|
||||
.page-wrapper {
|
||||
.page {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
@include gradient-v($g18-cloud, $g19-ghost);
|
||||
}
|
||||
.signup-flow-bg {
|
||||
background-image: url(/assets/images/signup-bg.png);
|
||||
background-position: center top;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.page-wrapper > div:not(.flash-messages):not(.page-spinner):not(.kapacitor-rule-page):not(.data-explorer-container) {
|
||||
.page-contents {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: $chronograf-page-header-height;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100% - #{$chronograf-page-header-height});
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
@include custom-scrollbar($g2-kevlar,$c-pool);
|
||||
@include gradient-v($g2-kevlar,$g0-obsidian);
|
||||
|
||||
&--green-scrollbar {
|
||||
@include custom-scrollbar($g2-kevlar,$c-rainforest);
|
||||
}
|
||||
&--purple-scrollbar {
|
||||
@include custom-scrollbar($g2-kevlar,$c-comet);
|
||||
}
|
||||
}
|
||||
.container-fluid {
|
||||
padding: ($chronograf-page-header-height / 2) $page-wrapper-padding ($chronograf-page-header-height / 2) $page-wrapper-padding;
|
||||
max-width: $page-wrapper-max-width;
|
||||
|
||||
&.full-width {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Chronograf Page Header
|
||||
Page Header
|
||||
----------------------------------------------
|
||||
*/
|
||||
.chronograf-header {
|
||||
background-color: $g20-white;
|
||||
.page-header {
|
||||
height: $chronograf-page-header-height;
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
padding: 0 ($page-wrapper-padding + $scrollbar-width) 0 $page-wrapper-padding;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: $g0-obsidian;
|
||||
border: none;
|
||||
margin: 0;
|
||||
|
||||
&__container {
|
||||
width: 100%;
|
||||
|
@ -115,7 +101,7 @@ body > #react-root {
|
|||
}
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
font-size: 20px;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
|
@ -140,42 +126,44 @@ body > #react-root {
|
|||
margin: 0 0 0 4px;
|
||||
}
|
||||
}
|
||||
&.full-width .page-header__container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.minimal-dropdown {
|
||||
.dropdown-toggle {
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
/*
|
||||
Custom Search Widget
|
||||
----------------------------------------------
|
||||
*/
|
||||
.users__search-widget {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.button-text {
|
||||
color: $g10-wolf;
|
||||
}
|
||||
.caret {
|
||||
color: $g10-wolf;
|
||||
}
|
||||
input.form-control {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
padding-left: 34px;
|
||||
border-radius: 4px !important;
|
||||
|
||||
&:focus + .input-group-addon {
|
||||
color: $c-pool;
|
||||
}
|
||||
}
|
||||
.button-text,
|
||||
.caret {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.button-text {
|
||||
transition: color 0.25s ease;
|
||||
}
|
||||
.caret {
|
||||
transition: color 0.25s ease;
|
||||
}
|
||||
&.open {
|
||||
.button-text {
|
||||
color: $g10-wolf;
|
||||
}
|
||||
.caret {
|
||||
color: $g10-wolf;
|
||||
}
|
||||
.input-group-addon {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
line-height: 38px;
|
||||
position: absolute;
|
||||
color: $g10-wolf;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
z-index: 4;
|
||||
border: 0;
|
||||
width: 40px;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
color 0.25s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +171,33 @@ body > #react-root {
|
|||
Unsorted
|
||||
----------------------------------------------
|
||||
*/
|
||||
.select-source-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
@include custom-scrollbar($g2-kevlar, $c-pool);
|
||||
@include gradient-v($g2-kevlar, $g0-obsidian);
|
||||
}
|
||||
.text-right .btn {
|
||||
margin: 0 0 0 4px;
|
||||
}
|
||||
.text-center .btn {
|
||||
margin: 0 2px;
|
||||
}
|
||||
.default-source-label {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
background-color: $g5-pepper;
|
||||
color: $c-pool;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.progress-label {
|
||||
margin-bottom: 4px;
|
||||
color: $g11-sidewalk;
|
||||
|
@ -193,31 +208,6 @@ body > #react-root {
|
|||
color: $g8-storm;
|
||||
}
|
||||
}
|
||||
.flex-group {
|
||||
margin: 0;
|
||||
|
||||
.flex-space {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
.main {
|
||||
margin-top: 60px;
|
||||
}
|
||||
.fake-panel-title {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
&.match-search {
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
}
|
||||
.modal#dbModal {
|
||||
.modal-body {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.btn-block.dropdown-toggle {
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
@ -233,9 +223,6 @@ body > #react-root {
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.modal {
|
||||
form {
|
||||
padding: 0;
|
||||
|
@ -251,146 +238,6 @@ body > #react-root {
|
|||
}
|
||||
}
|
||||
}
|
||||
#accordion {
|
||||
.panel-heading {
|
||||
color: gray;
|
||||
align-items: center;
|
||||
|
||||
.progress {
|
||||
width: 200px;
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.continuous-queries {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
}
|
||||
pre code {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
&__empty {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
color: $g15-platinum;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.product-buttons {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$telegraf: #f95f53;
|
||||
$influxdb: #4591ED;
|
||||
$kapacitor: #4ED8A0;
|
||||
$chronograf: #C557FF;
|
||||
|
||||
.influxdb-bg {
|
||||
background-color: $influxdb;
|
||||
}
|
||||
|
||||
.telegraf-bg {
|
||||
background-color: $telegraf;
|
||||
}
|
||||
|
||||
.chronograf-bg {
|
||||
background-color: $chronograf;
|
||||
}
|
||||
|
||||
.kapacitor-bg {
|
||||
background-color: $kapacitor;
|
||||
}
|
||||
|
||||
.btn-telegraf {
|
||||
@extend .telegraf-bg;
|
||||
|
||||
&.inactive {
|
||||
background-color: lighten($telegraf, 25%);
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-influxdb {
|
||||
@extend .influxdb-bg;
|
||||
|
||||
&.inactive {
|
||||
background-color: lighten($influxdb, 25%);
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-chronograf {
|
||||
@extend .chronograf-bg;
|
||||
|
||||
&.inactive {
|
||||
background-color: lighten($chronograf, 25%);
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-kapacitor {
|
||||
@extend .kapacitor-bg;
|
||||
|
||||
&.inactive {
|
||||
background-color: lighten($kapacitor, 25%);
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
.pinger {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ping-pending {
|
||||
color: yellow;
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
.ping-success {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.ping-failure {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
color: #fff;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
a {
|
||||
&.btn {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.caret {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Custom Tabs
|
||||
|
@ -436,55 +283,6 @@ a {
|
|||
}
|
||||
}
|
||||
|
||||
.flash-messages {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 570px;
|
||||
top: 36px;
|
||||
z-index: 9999;
|
||||
|
||||
.alert {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-width: 0;
|
||||
color: $g20-white;
|
||||
|
||||
&.alert-success {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
&.alert-primary {
|
||||
background-color: $c-pool;
|
||||
}
|
||||
&.alert-warning {
|
||||
background-color: $c-comet;
|
||||
|
||||
}
|
||||
&.alert-danger {
|
||||
background-color: $c-dreamsicle;
|
||||
}
|
||||
|
||||
button.close {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 16px;
|
||||
text-shadow: none;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
.icon {
|
||||
color: $g20-white;
|
||||
text-shadow: none;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Error Pages
|
||||
|
@ -528,64 +326,6 @@ table.table.error-table {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Database Manager polish
|
||||
----------------------------------------------
|
||||
*/
|
||||
.db-manager-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
p {
|
||||
color: $g12-forge;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Cluster Renaming UI
|
||||
----------------------------------------------
|
||||
*/
|
||||
.cluster-dropdown {
|
||||
&__item {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.cluster-dropdown__rename {
|
||||
opacity: 1;
|
||||
}
|
||||
a {
|
||||
background-color: $c-laser;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__rename {
|
||||
z-index: 2;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
width: 34px;
|
||||
height: 100%;
|
||||
transform: translateY(-50%);
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: $c-hydrogen;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
color: $g20-white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Generic Empty State
|
||||
----------------------------------------------
|
||||
|
@ -603,20 +343,16 @@ table.table.error-table {
|
|||
}
|
||||
}
|
||||
|
||||
.cluster-accounts > *:not(:last-child) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/*
|
||||
Static Form Controls
|
||||
----------------------------------------------
|
||||
*/
|
||||
$form-static-checkbox-size: 16px;
|
||||
.form-control-static {
|
||||
border: 2px solid $g17-whisper;
|
||||
height: 40px;
|
||||
border: 2px solid $g5-pepper;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
padding: 0 18px;
|
||||
line-height: 36px;
|
||||
padding: 7px 12px;
|
||||
position: relative;
|
||||
|
||||
input[type="checkbox"] {
|
||||
|
@ -629,8 +365,13 @@ table.table.error-table {
|
|||
|
||||
// Faux Checkbox
|
||||
& + label {
|
||||
font-size: 14px !important;
|
||||
line-height: 16px;
|
||||
color: $g11-sidewalk;
|
||||
font-weight: 500;
|
||||
transition: color 0.25s ease;
|
||||
margin-left: 24px;
|
||||
margin: 0;
|
||||
padding: 0 0 0 (12px + $form-static-checkbox-size + 6px);
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-moz-user-selct: none;
|
||||
|
@ -640,22 +381,22 @@ table.table.error-table {
|
|||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 18px;
|
||||
left: 12px;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: $g18-cloud;
|
||||
border: 2px solid $g17-whisper;
|
||||
width: $form-static-checkbox-size;
|
||||
height: $form-static-checkbox-size;
|
||||
background-color: $g2-kevlar;
|
||||
border: 2px solid $g5-pepper;
|
||||
border-radius: 3px;
|
||||
z-index: 2;
|
||||
transition:
|
||||
background-color 0.25s ease;
|
||||
border-color 0.25s ease;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: (18px + (16px / 2));
|
||||
left: (12px + ($form-static-checkbox-size / 2));
|
||||
transform: translate(-50%,-50%) scale(2,2);
|
||||
opacity: 0;
|
||||
width: 6px;
|
||||
|
@ -669,15 +410,16 @@ table.table.error-table {
|
|||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $g20-white;
|
||||
|
||||
&:before {
|
||||
background-color: $g20-white;
|
||||
border-color: $g6-smoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Faux Checkbox (Checked)
|
||||
&:checked + label {
|
||||
color: $c-pool;
|
||||
color: $g20-white;
|
||||
|
||||
&:after {
|
||||
opacity: 1;
|
||||
|
@ -686,15 +428,6 @@ table.table.error-table {
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Button alignment in tables
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
table .text-right .btn {
|
||||
margin: 0 0 0 6px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stuff for making Tables of Data more readable
|
||||
|
@ -731,20 +464,6 @@ table .monotype {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sources / Kapacitor Config Pages
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
#source-form-page,
|
||||
#manage-sources-page,
|
||||
.kapacitor {
|
||||
.chronograf-header {
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Empty State for Tables
|
||||
----------------------------------------------
|
||||
|
@ -766,4 +485,4 @@ table .monotype {
|
|||
margin-bottom: 75px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,14 +4,14 @@
|
|||
@import 'font-awesome';
|
||||
@import 'fonts';
|
||||
@import 'theme';
|
||||
@import 'theme-dark';
|
||||
@import 'tasks';
|
||||
@import 'users';
|
||||
@import 'sub-page';
|
||||
@import 'roles';
|
||||
@import 'retention-policies';
|
||||
@import 'signup';
|
||||
@import 'modals';
|
||||
@import 'enterprise-custom';
|
||||
@import 'flash-messages';
|
||||
@import 'dygraph-override';
|
||||
@import 'auth-page';
|
||||
@import 'hosts';
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Styles for Flash Messages
|
||||
----------------------------------------------
|
||||
*/
|
||||
.flash-messages {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 570px;
|
||||
top: 36px;
|
||||
z-index: 9999;
|
||||
|
||||
.alert {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-width: 0;
|
||||
color: $g20-white;
|
||||
|
||||
&.alert-success {
|
||||
background-color: $c-rainforest;
|
||||
}
|
||||
&.alert-primary {
|
||||
background-color: $c-pool;
|
||||
}
|
||||
&.alert-warning {
|
||||
background-color: $c-comet;
|
||||
|
||||
}
|
||||
&.alert-danger {
|
||||
background-color: $c-dreamsicle;
|
||||
}
|
||||
|
||||
button.close {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 13px;
|
||||
transform: translateY(-50%);
|
||||
font-size: 16px;
|
||||
text-shadow: none;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
.icon {
|
||||
color: $g20-white;
|
||||
text-shadow: none;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +1,7 @@
|
|||
/*
|
||||
Hosts Dashboard
|
||||
Styles for Hosts List & Host View
|
||||
----------------------------------------------
|
||||
*/
|
||||
.kapacitor-rules-page,
|
||||
.hosts-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden !important;
|
||||
@include gradient-v($g2-kevlar,$g0-obsidian);
|
||||
color: $g17-whisper;
|
||||
|
||||
.chronograf-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
background-color: $g0-obsidian;
|
||||
}
|
||||
.hosts-page-scroll-container {
|
||||
position: absolute;
|
||||
top: $chronograf-page-header-height;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - #{$chronograf-page-header-height});
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
@include custom-scrollbar($g2-kevlar,$c-pool);
|
||||
|
||||
.container-fluid {
|
||||
padding-bottom: ($chronograf-page-header-height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-minimal {
|
||||
border: 0;
|
||||
|
||||
.panel-title {
|
||||
color: $g17-whisper !important;
|
||||
}
|
||||
.panel-body {
|
||||
padding: 30px;
|
||||
background-color: $g3-castle;
|
||||
border: 0;
|
||||
color: $g17-whisper;
|
||||
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
> *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
th,td {
|
||||
border-color: $g5-pepper;
|
||||
}
|
||||
th {
|
||||
color: $g17-whisper;
|
||||
}
|
||||
td {
|
||||
color: $g14-chromium;
|
||||
}
|
||||
tbody tr:last-child td {
|
||||
border-bottom: 2px solid $g5-pepper;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.users__search-widget {
|
||||
position: relative;
|
||||
|
||||
input.form-control {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
background-color: $g2-kevlar;
|
||||
border-color: $g5-pepper;
|
||||
color: $g17-whisper;
|
||||
border-radius: 4px;
|
||||
padding-left: 34px;
|
||||
|
||||
&:focus {
|
||||
border-color: $c-pool !important;
|
||||
color: $c-pool;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: $c-pool;
|
||||
}
|
||||
&::-moz-placeholder {
|
||||
color: $c-pool;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: $c-pool;
|
||||
}
|
||||
&:-moz-placeholder {
|
||||
color: $c-pool;
|
||||
}
|
||||
|
||||
& + .input-group-addon {
|
||||
color: $c-pool;
|
||||
}
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: $g11-sidewalk;
|
||||
}
|
||||
&::-moz-placeholder {
|
||||
color: $g11-sidewalk;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: $g11-sidewalk;
|
||||
}
|
||||
&:-moz-placeholder {
|
||||
color: $g11-sidewalk;
|
||||
}
|
||||
}
|
||||
.input-group-addon {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
line-height: 38px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
border: 0;
|
||||
width: 40px;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
color 0.25s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hosts-dashboard {
|
||||
max-width: 100%;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
.graph-panel__graph-container.hosts-graph {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
@ -155,26 +16,6 @@
|
|||
color: $g14-chromium;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.hosts-dashboard-header {
|
||||
.chronograf-header__container {
|
||||
max-width: 100%;
|
||||
}
|
||||
.chronograf-header__left {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
margin-right: 15px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chronograf-header__right {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.host-list--active-source {
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
|
@ -233,7 +74,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.host-table-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -21,38 +21,6 @@ $kap-dot-color: $c-rainforest;
|
|||
|
||||
$kapacitor-font-sm: 13px;
|
||||
|
||||
.kapacitor-rule-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
@include gradient-v($g2-kevlar,$g0-obsidian);
|
||||
color: $g17-whisper;
|
||||
|
||||
.chronograf-header {
|
||||
background-color: $g0-obsidian;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.rule-builder-wrapper {
|
||||
position: absolute;
|
||||
top: $chronograf-page-header-height;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - #{$chronograf-page-header-height});
|
||||
overflow: auto;
|
||||
@include custom-scrollbar($g0-obsidian,$kapacitor-accent);
|
||||
|
||||
.container-fluid {
|
||||
padding-bottom: $chronograf-page-header-height;
|
||||
}
|
||||
}
|
||||
.rule-builder {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -204,6 +172,10 @@ div.query-editor.kapacitor-metric-selector {
|
|||
border-radius: $kap-radius-lg $kap-radius-lg 0 0;
|
||||
border: 0;
|
||||
padding: $kap-padding-md $kap-padding-lg;
|
||||
|
||||
code {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
// Tabs
|
||||
|
@ -553,8 +525,7 @@ div.query-editor.kapacitor-metric-selector {
|
|||
.rule-builder {
|
||||
input[type="text"] {
|
||||
background-color: $kapacitor-graphic-color;
|
||||
color: $kapacitor-accent;
|
||||
border-color: $g5-pepper;
|
||||
color: $kapacitor-accent !important;
|
||||
font-weight: 600;
|
||||
font-size: $kapacitor-font-sm;
|
||||
font-family: Consolas, "Lucida Console", Monaco, monospace;
|
||||
|
@ -564,7 +535,7 @@ div.query-editor.kapacitor-metric-selector {
|
|||
border-color: $kapacitor-accent;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
color: $g20-white;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
.dropdown-toggle,
|
||||
|
@ -615,40 +586,6 @@ div.query-editor.kapacitor-metric-selector {
|
|||
.size-49 {
|
||||
width: 49px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
@include gradient-v($c-viridian,$c-ocean);
|
||||
|
||||
> li {
|
||||
width: 100%;
|
||||
}
|
||||
> li > a {
|
||||
text-transform: capitalize;
|
||||
font-size: $kapacitor-font-sm;
|
||||
padding: ($kap-padding-sm / 2) $kap-padding-sm;
|
||||
font-weight: 600;
|
||||
color: transparentize($g20-white, 0.25);
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $c-rainforest;
|
||||
color: $g20-white;
|
||||
}
|
||||
}
|
||||
> li:first-child > a {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
> li:last-child > a {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chronograf-header__editable {
|
||||
|
@ -656,6 +593,13 @@ div.query-editor.kapacitor-metric-selector {
|
|||
|
||||
&:hover {
|
||||
color: $g13-mist;
|
||||
cursor: text !important;
|
||||
}
|
||||
.icon {
|
||||
cursor: inherit;
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
top: -1.5px;
|
||||
}
|
||||
}
|
||||
.chronograf-header__editing {
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#welcomeModal {
|
||||
.modal-header,
|
||||
.modal-footer, {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-error {
|
||||
color: red;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
.panel-group.sub-page {
|
||||
|
||||
.panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.panel-collapse > .panel-body {
|
||||
border-top: 2px solid $g19-ghost;
|
||||
}
|
||||
.panel-heading {
|
||||
padding: 10px 30px;
|
||||
|
||||
.panel-title a[data-toggle="collapse"] {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 20px 0 0;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
Dark Theme Styles
|
||||
----------------------------------------------
|
||||
This stylesheet has overrides for the theme, so you are
|
||||
going to see al ot of !important =(
|
||||
|
||||
This is design debt. One day the theme will not have to be overrided
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Dark Panel Styles
|
||||
----------------------------------------------
|
||||
*/
|
||||
.row:only-child .panel,
|
||||
.row:last-child .panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.panel hr {
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
.panel-minimal {
|
||||
border: 0;
|
||||
|
||||
.panel-title {
|
||||
color: $g10-wolf !important;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
padding: 30px;
|
||||
background-color: $g3-castle;
|
||||
border: 0;
|
||||
color: $g13-mist;
|
||||
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
> *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
th,td {
|
||||
border-color: $g5-pepper;
|
||||
}
|
||||
th {
|
||||
color: $g17-whisper;
|
||||
}
|
||||
td {
|
||||
color: $g14-chromium;
|
||||
}
|
||||
tbody tr:last-child td {
|
||||
border-bottom: 2px solid $g5-pepper;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Dark Buttons
|
||||
----------------------------------------------
|
||||
*/
|
||||
.btn {
|
||||
border: 0;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease,
|
||||
box-shadow 0.25s ease;
|
||||
}
|
||||
.btn.btn-sm {
|
||||
font-size: 13px;
|
||||
line-height: 30px !important;
|
||||
height: 30px !important;
|
||||
padding: 0 9px !important;
|
||||
}
|
||||
.btn.btn-xs .icon {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/*
|
||||
Dark Inputs
|
||||
----------------------------------------------
|
||||
*/
|
||||
.form-group {
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
.form-group label {
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 13px;
|
||||
}
|
||||
.form-control {
|
||||
padding: 0 13px;
|
||||
background-color: $g2-kevlar !important;
|
||||
border-color: $g5-pepper !important;
|
||||
color: $g15-platinum !important;
|
||||
|
||||
&::-webkit-input-placeholder { color: $g10-wolf; }
|
||||
&::-moz-placeholder { color: $g10-wolf; }
|
||||
&:-ms-input-placeholder { color: $g10-wolf; }
|
||||
&:-moz-placeholder { color: $g10-wolf; }
|
||||
|
||||
&:hover {
|
||||
border-color: $g6-smoke !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $c-pool !important;
|
||||
box-shadow: 0 0 6px 0px $c-pool !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
|
||||
&--green:focus {
|
||||
border-color: $c-rainforest !important;
|
||||
box-shadow: 0 0 6px 0px $c-rainforest !important;
|
||||
}
|
||||
}
|
||||
.form-helper {
|
||||
margin: 4px 0 !important;
|
||||
font-weight: 400 !important;
|
||||
font-style: italic;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
.form-group-submit {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Dark Dropdowns
|
||||
----------------------------------------------
|
||||
*/
|
||||
.dropdown-toggle {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
|
||||
.caret {
|
||||
position: absolute;
|
||||
top: calc(50% + 1px);
|
||||
right: 8px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
.dropdown-menu {
|
||||
float: none !important;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
min-height: 70px;
|
||||
max-height: 290px;
|
||||
overflow: auto;
|
||||
@include custom-scrollbar($c-pool,$c-laser);
|
||||
@include gradient-h($c-ocean,$c-pool);
|
||||
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.8);
|
||||
|
||||
> li {
|
||||
width: 100%;
|
||||
font-size: 0px;
|
||||
|
||||
&:hover {
|
||||
@include gradient-h($c-laser,$c-pool);
|
||||
}
|
||||
}
|
||||
> li > a {
|
||||
width: 100%;
|
||||
border-radius: 0 !important;
|
||||
display: inline-block;
|
||||
padding: 7px 9px;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
font-weight: 500;
|
||||
color: $c-yeti !important;
|
||||
background-color: transparent;
|
||||
transition:
|
||||
color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
> li:last-child a {
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
> li:first-child a {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
}
|
||||
.dropdown.dropdown-kapacitor .dropdown-menu {
|
||||
@include custom-scrollbar($c-rainforest,$c-honeydew);
|
||||
@include gradient-h($c-pool,$c-rainforest);
|
||||
|
||||
> li:hover {
|
||||
@include gradient-h($c-laser,$c-rainforest);
|
||||
}
|
||||
> li > a {
|
||||
color: $c-mint !important;
|
||||
&:hover {
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dropdown.dropdown-chronograf .dropdown-menu {
|
||||
@include custom-scrollbar($c-comet,$c-potassium);
|
||||
@include gradient-h($c-ocean,$c-comet);
|
||||
|
||||
> li:hover {
|
||||
@include gradient-h($c-laser,$c-comet);
|
||||
}
|
||||
> li > a {
|
||||
color: $c-cremedeviolette !important;
|
||||
&:hover {
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dropdown Actions */
|
||||
.dropdown-item {
|
||||
position: relative;
|
||||
|
||||
> a {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.dropdown-item__actions {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 3px;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.dropdown-item__action {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
border: none !important;
|
||||
font-size: 13px;
|
||||
transition:
|
||||
text-shadow 0.25s ease,
|
||||
color 0.25s ease;
|
||||
color: $c-sapphire;
|
||||
|
||||
&[data-target="#deleteExplorerModal"] .icon {
|
||||
position: relative;
|
||||
right: -1px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $g20-white;
|
||||
text-shadow:
|
||||
0 0 2px $c-hydrogen,
|
||||
0 0 3px $c-laser,
|
||||
0 0 6px $c-ocean;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Dark Code Samples
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
code {
|
||||
display: inline-block;
|
||||
background-color: $g0-obsidian;
|
||||
color: $c-comet;
|
||||
border: 0 !important;
|
||||
border-radius: 2px;
|
||||
padding: 2.5px 5px 2.5px 4px;
|
||||
margin: 0 1px 0 2px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Dark Modals
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
.modal-backdrop {
|
||||
background: $c-pool;
|
||||
background: -moz-linear-gradient(-45deg, $c-pool 0%, $c-comet 100%);
|
||||
background: -webkit-linear-gradient(-45deg, $c-pool 0%,$c-comet 100%);
|
||||
background: linear-gradient(135deg, $c-pool 0%,$c-comet 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$c-pool', endColorstr='$c-comet',GradientType=1 );
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: $g3-castle;
|
||||
}
|
||||
.modal-header,
|
||||
.modal-body,
|
||||
.modal-footer {
|
||||
background-color: transparent;
|
||||
color: $g13-mist;
|
||||
padding-left: ($sidebar-width / 2);
|
||||
padding-right: ($sidebar-width / 2);
|
||||
}
|
||||
.modal-body {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.modal-title {
|
||||
color: $g18-cloud !important;
|
||||
}
|
||||
.modal-header .close {
|
||||
transition: none;
|
||||
outline: none;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
display: block;
|
||||
content: '';
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
border-radius: 1px;
|
||||
background-color: $g7-graphite;
|
||||
transition: background-color 0.25s ease;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
&:before { transform: translate(-50%,-50%) rotate(45deg); }
|
||||
&:after { transform: translate(-50%,-50%) rotate(-45deg); }
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $g13-mist;
|
||||
}
|
||||
}
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -4,9 +4,10 @@
|
|||
*/
|
||||
|
||||
export default function timeSeriesToDygraph(raw = []) {
|
||||
const fields = ['time']; // all of the effective field names (i.e. <measurement>.<field>)
|
||||
const labels = ['time']; // all of the effective field names (i.e. <measurement>.<field>)
|
||||
const fieldToIndex = {}; // see parseSeries
|
||||
const dates = {}; // map of date as string to date value to minimize string coercion
|
||||
const dygraphSeries = {}; // dygraphSeries is a graph legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'};
|
||||
|
||||
/**
|
||||
* dateToFieldValue will look like:
|
||||
|
@ -26,7 +27,7 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
*/
|
||||
const dateToFieldValue = {};
|
||||
|
||||
raw.forEach(({response}) => {
|
||||
raw.forEach(({response}, queryIndex) => {
|
||||
// If a response is an empty result set or a query returned an error
|
||||
// from InfluxDB, don't try and parse.
|
||||
if (response.results.length) {
|
||||
|
@ -87,8 +88,9 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
|
||||
// Given a field name, identify which column in the timeSeries result should hold the field's value
|
||||
// ex given this timeSeries [Date, 10, 20, 30] field index at 2 would correspond to value 20
|
||||
fieldToIndex[effectiveFieldName] = fields.length;
|
||||
fields.push(effectiveFieldName);
|
||||
fieldToIndex[effectiveFieldName] = labels.length;
|
||||
labels.push(effectiveFieldName);
|
||||
dygraphSeries[effectiveFieldName] = {axis: queryIndex === 0 ? 'y' : 'y2'};
|
||||
});
|
||||
|
||||
(series.values || []).forEach(parseRow);
|
||||
|
@ -121,7 +123,7 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
function buildTimeSeries() {
|
||||
const allDates = Object.keys(dateToFieldValue);
|
||||
allDates.sort((a, b) => a - b);
|
||||
const rowLength = fields.length;
|
||||
const rowLength = labels.length;
|
||||
return allDates.map((date) => {
|
||||
const row = new Array(rowLength);
|
||||
|
||||
|
@ -138,8 +140,9 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
}
|
||||
|
||||
return {
|
||||
fields,
|
||||
labels,
|
||||
timeSeries: buildTimeSeries(),
|
||||
dygraphSeries,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue