Commit 770eb1ca authored by MaxSol1's avatar MaxSol1
Browse files

wip:perf data mode

parent 50e8f983
......@@ -81,6 +81,8 @@ type Option struct {
UnknownSound string `json:"unknownSound"`
CriticalSound string `json:"criticalSound"`
MuteAlerts bool `json:"muteAlerts"`
PerfDataMode bool `json:"perfDataMode"`
PerfDataSelection string `json:"perfDataSelection"`
}
//Rect helper struct for positions
......
......@@ -59,6 +59,132 @@ type icingaObject struct {
Groups []string `json:"groups"`
}
type icingaAPILastCheckResults struct {
CheckResults []results `json:"results"`
}
type results struct {
Attributes icingaCheckAttributes `json:"attrs"`
Joins joins `json:"joins,omitempty"`
Meta types `json:"meta,omitempty"`
Name string `json:"name"`
Type string `json:"type"`
}
type joins struct {
}
type types struct {
}
type icingaCheckAttributes struct {
Attributes checkResult `json:"last_check_result"`
}
type checkResult struct {
Active bool `json:"active"`
CheckSource string `json:"check_source"`
Command []string `json:"command"`
ExecutionEnd float64 `json:"execution_end"`
ExecutionStart float64 `json:"execution_start"`
ExitStatus float64 `json:"exit_status"`
Output string `json:"output"`
PerformanceData []string `json:"performance_data"`
ScheduleEnd float64 `json:"schedule_end"`
ScheduleStart float64 `json:"schedule_start"`
State float64 `json:"state"`
TTL float64 `json:"ttl"`
Type string `json:"type"`
VarsAfter varsAfter `json:"vars_after"`
VarsBefore varsBefore `json:"vars_before"`
}
type varsAfter struct {
Attempt float64 `json:"attempt"`
Reachable bool `json:"reachable"`
State float64 `json:"state"`
StateType float64 `json:"state_type"`
}
type varsBefore struct {
Attempt float64 `json:"attempt"`
Reachable bool `json:"reachable"`
State float64 `json:"state"`
StateType float64 `json:"state_type"`
}
func handleCheckResult(w http.ResponseWriter, r *http.Request) {
service := r.URL.Query().Get("service")
attrs := r.URL.Query().Get("attrs")
fmt.Println(service)
if service == "" {
http.Error(w, "No unique queue name specified", http.StatusBadRequest)
return
}
client := &http.Client{}
//Disable TLS verification if config says so
if config.IcingaInsecureTLS {
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
//Create HTTP request
req_url, err := url.Parse(config.IcingaURL)
if err != nil {
log.Printf("Failed to parse IcingaURL: %w", err)
http.Error(w, "Server Error", http.StatusInternalServerError)
return
}
req_url.Path = path.Join(req_url.Path, "/v1/objects/services", service)
req_url.RawQuery = strings.ReplaceAll(url.Values{"attrs": []string{attrs}}.Encode(), "+", "%20")
req, err := http.NewRequest("GET", req_url.String(), nil)
req.Header.Set("Accept", "application/json")
req.SetBasicAuth(config.IcingaUsername, config.IcingaPassword)
fmt.Println(req)
if err != nil {
fmt.Println("Failed to create HTTP request: %w", err)
http.Error(w, "Error creating http request: "+err.Error(), http.StatusInternalServerError)
return
}
//Make request
res, err := client.Do(req)
if err != nil {
fmt.Println("Icinga2 API error: %w", err.Error())
http.Error(w, "Icinga2 API error: "+err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(res)
fmt.Println("Response status:", res.Status)
fmt.Println(res.Body)
defer res.Body.Close()
var checkResults icingaAPILastCheckResults
dec := json.NewDecoder(res.Body)
err = dec.Decode(&checkResults)
if err != nil {
log.Printf("Error decoding Icinga2 API response: %w", err)
http.Error(w, "Error decoding Icinga2 API response: "+err.Error(), http.StatusInternalServerError)
return
}
enc := json.NewEncoder(w)
enc.Encode(checkResults)
}
func handleIcingaCheckState(w http.ResponseWriter, r *http.Request) {
object_type := r.URL.Query().Get("object_type")
filter := r.URL.Query().Get("filter")
......@@ -95,7 +221,7 @@ func handleIcingaCheckState(w http.ResponseWriter, r *http.Request) {
return
}
req_url.Path = path.Join(req_url.Path, "/v1/objects", object_type + "s")
req_url.Path = path.Join(req_url.Path, "/v1/objects", object_type+"s")
req_url.RawQuery = strings.ReplaceAll(url.Values{"filter": []string{filter}}.Encode(), "+", "%20")
req, err := http.NewRequest("GET", req_url.String(), nil)
......@@ -105,6 +231,8 @@ func handleIcingaCheckState(w http.ResponseWriter, r *http.Request) {
return
}
fmt.Println(req)
req.SetBasicAuth(config.IcingaUsername, config.IcingaPassword)
//Make request
......@@ -119,6 +247,7 @@ func handleIcingaCheckState(w http.ResponseWriter, r *http.Request) {
var results icingaAPIResults
dec := json.NewDecoder(res.Body)
err = dec.Decode(&results)
if err != nil {
log.Printf("Error decoding Icinga2 API response: %w", err)
http.Error(w, "Error decoding Icinga2 API response: "+err.Error(), http.StatusInternalServerError)
......
......@@ -93,6 +93,7 @@ func main() {
r.Get("/icinga/{check-type}/{object-id}", handleIcingaCheck)
r.Get("/icinga/check_state", handleIcingaCheckState)
r.Get("/icinga/check_result", handleCheckResult)
r.Post("/upload", handleUpload)
r.Handle("/dashboards-data/*", http.StripPrefix("/dashboards-data/", http.FileServer(http.Dir("./dashboards-data"))))
......
......@@ -6,6 +6,8 @@ import { icingaResultCodeToCheckState, icingaCheckTypeFromId, IcingaCheckList }
//The rendered view (in the actual dashboard) of the Card Element
export function CheckCard({options, slug, dashboard}) {
const [checkState, setCheckState] = useState(false);
const [perfData, setPerfData] = useState(null);
const [perfValue, setPerfValue] = useState(null);
let ok = false;
let warning = false;
......@@ -81,9 +83,33 @@ export function CheckCard({options, slug, dashboard}) {
});
}
const perfDataSelected = async (perfData) => {
let waitPerf = await perfData;
for (const [key, value] of Object.entries(waitPerf)) {
if (options.perfDataSelection === key) {
setPerfValue(value)
}
}
}
//Setup check refresher
useEffect(() => {
if(options.objectType !== null && options.filter !== null) {
meerkat.getCheckResult(options.id).then(async c => {
let perfData = c.results[0].attrs.last_check_result.performance_data.join().replace(',', ';').split(';');
if (typeof perfData !== "undefined") {
let arrPerf = [];
for( var i = 0; i < perfData.length; i++){
if (perfData[i].includes('=')) {
arrPerf.push(perfData[i].replace(',', ''));
}
}
let objPerf = Object.fromEntries(arrPerf.map(s => s.split('=')));
setPerfData(objPerf);
perfDataSelected(objPerf);
console.log(objPerf)
}
});
initState();
updateState();
const intervalID = window.setInterval(updateState, 30*1000)
......@@ -93,7 +119,11 @@ export function CheckCard({options, slug, dashboard}) {
return <div class={"check-content card " + checkState}>
<div class="check-state" style={`font-size: ${options.statusFontSize}px`}>
{checkState === null ? 'Unconfigured' : checkState}
{/* {checkState === null ? 'Unconfigured' : checkState} */}
{/* {perfValue === null ? 'Unconfigured' : perfValue} */}
{/* {perfDataSelected()} */}
{/* <br/> */}
{perfValue ? perfValue : checkState}
</div>
</div>
}
......@@ -115,12 +145,74 @@ export function CheckCardOptions({options, updateOptions}) {
<label for="status-font-size">Status Font Size</label>
<input class="form-control" id="status-font-size" name="status-font-size" type="number" min="0"
onInput={e => updateOptions({statusFontSize: Number(e.currentTarget.value)})}/>
<PerfDataOptions options={options} updateOptions={updateOptions}/>
<br/>
<button class="rounded btn-primary btn-large" onClick={onClickAdvanced}>{showAdvanced ? 'Hide Options' : 'Advanced Options'}</button>
<AdvancedCheckOptions options={options} updateOptions={updateOptions} display={showAdvanced}/>
</div>
}
const PerfDataOptions = ({options, updateOptions}) => {
const [showPerfOptions, setShowPerf] = useState(null)
const [perfData, setPerfData] = useState(null);
useEffect(() => {
options.perfDataMode ? setShowPerf(true) : setShowPerf(false);
clearPerfData();
setShowPerf(false)
meerkat.getCheckResult(options.id).then(async c => {
let erfData = c.results[0].attrs.last_check_result.performance_data
console.log(erfData)
let perfData = c.results[0].attrs.last_check_result.performance_data.join().replace(',', ';').split(';');
if (typeof perfData !== "undefined") {
let arrPerf = [];
for( var i = 0; i < perfData.length; i++){
if (perfData[i].includes('=')) {
arrPerf.push(perfData[i].replace(',', ''));
}
}
let objPerf = Object.fromEntries(arrPerf.map(s => s.split('=')));
setPerfData(objPerf)
}
});
}, [options.perfDataMode])
const clearPerfData = () => {
if (!options.perfDataMode) {
updateOptions({
perfDataSelection: ''
})
}
}
const perfDataMode = (e) => {
let perfDataModeChecked = options.perfDataMode;
perfDataModeChecked = !perfDataModeChecked;
updateOptions({
perfDataMode: perfDataModeChecked
})
}
if(perfData === null) {
return <div><label>No Performance Data Available</label><br/></div>
}
return <div>
<label class="status-font-size">Performance Data Mode</label>
<input type="checkbox" defaultChecked={options.perfDataMode} onChange={e => perfDataMode(e)} class="form-control perf-data-mode"/>
{options.perfDataMode || showPerfOptions ?
<select onInput={e => updateOptions({perfDataSelection: e.currentTarget.value})}>
<option value={null} selected disabled>Choose away...</option>
{Object.keys(perfData).map(perf => (
<option key={perf} value={perf}>
{perf.toUpperCase()}
</option>
))}
</select>
: null}
</div>
}
const AdvancedCheckOptions = ({options, updateOptions, display}) => {
const handleAudioFile = async (fieldName, files) => {
const res = await meerkat.uploadFile(files[0]);
......
......@@ -30,6 +30,16 @@ export async function getIcingaObjectState(objectType, filter) {
}
}
export async function getCheckResult(service, attrs="last_check_result") {
const res = await fetch(`/icinga/check_result?service=${encodeURIComponent(service)};attrs=${encodeURIComponent(attrs)}`);
if (res.status !== 200) {
return console.log("query succesful");
} else {
return res.json();
}
}
export async function getAllDashboards() {
const res = await fetch('/dashboard')
const data = await res.json();
......
......@@ -536,6 +536,13 @@ input[type=checkbox].form-control.mute-sounds {
position: absolute;
}
input[type=checkbox].form-control.perf-data-mode {
float: right;
margin-left: 14em;
margin-top: -3em;
position: absolute;
}
.dashboard.view-only .check {
cursor: default;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment