mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-29 03:50:18 +00:00
279 lines
8 KiB
Go
279 lines
8 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// registerPMGTools registers the pulse_pmg tool
|
|
func (e *PulseToolExecutor) registerPMGTools() {
|
|
e.registry.Register(RegisteredTool{
|
|
Definition: Tool{
|
|
Name: "pulse_pmg",
|
|
Description: `Query Proxmox Mail Gateway status and statistics. Types: status, mail_stats, queues, spam.`,
|
|
InputSchema: InputSchema{
|
|
Type: "object",
|
|
Properties: map[string]PropertySchema{
|
|
"type": {
|
|
Type: "string",
|
|
Description: "PMG query type",
|
|
Enum: []string{"status", "mail_stats", "queues", "spam"},
|
|
},
|
|
"instance": {
|
|
Type: "string",
|
|
Description: "Optional: specific PMG instance name or ID",
|
|
},
|
|
},
|
|
Required: []string{"type"},
|
|
},
|
|
},
|
|
Handler: func(ctx context.Context, exec *PulseToolExecutor, args map[string]interface{}) (CallToolResult, error) {
|
|
return exec.executePMG(ctx, args)
|
|
},
|
|
})
|
|
}
|
|
|
|
// executePMG routes to the appropriate PMG handler based on type
|
|
func (e *PulseToolExecutor) executePMG(ctx context.Context, args map[string]interface{}) (CallToolResult, error) {
|
|
pmgType, _ := args["type"].(string)
|
|
switch pmgType {
|
|
case "status":
|
|
return e.executeGetPMGStatus(ctx, args)
|
|
case "mail_stats":
|
|
return e.executeGetMailStats(ctx, args)
|
|
case "queues":
|
|
return e.executeGetMailQueues(ctx, args)
|
|
case "spam":
|
|
return e.executeGetSpamStats(ctx, args)
|
|
default:
|
|
return NewErrorResult(fmt.Errorf("unknown type: %s. Use: status, mail_stats, queues, spam", pmgType)), nil
|
|
}
|
|
}
|
|
|
|
func (e *PulseToolExecutor) executeGetPMGStatus(_ context.Context, args map[string]interface{}) (CallToolResult, error) {
|
|
if e.stateProvider == nil {
|
|
return NewTextResult("State provider not available."), nil
|
|
}
|
|
|
|
instanceFilter, _ := args["instance"].(string)
|
|
|
|
state := e.stateProvider.GetState()
|
|
|
|
if len(state.PMGInstances) == 0 {
|
|
return NewTextResult("No Proxmox Mail Gateway instances found. PMG monitoring may not be configured."), nil
|
|
}
|
|
|
|
var instances []PMGInstanceSummary
|
|
for _, pmg := range state.PMGInstances {
|
|
if instanceFilter != "" && pmg.ID != instanceFilter && pmg.Name != instanceFilter {
|
|
continue
|
|
}
|
|
|
|
var nodes []PMGNodeSummary
|
|
for _, node := range pmg.Nodes {
|
|
nodes = append(nodes, PMGNodeSummary{
|
|
Name: node.Name,
|
|
Status: node.Status,
|
|
Role: node.Role,
|
|
Uptime: node.Uptime,
|
|
LoadAvg: node.LoadAvg,
|
|
})
|
|
}
|
|
|
|
instances = append(instances, PMGInstanceSummary{
|
|
ID: pmg.ID,
|
|
Name: pmg.Name,
|
|
Host: pmg.Host,
|
|
Status: pmg.Status,
|
|
Version: pmg.Version,
|
|
Nodes: nodes,
|
|
})
|
|
}
|
|
|
|
if len(instances) == 0 && instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("PMG instance '%s' not found.", instanceFilter)), nil
|
|
}
|
|
|
|
// Ensure non-nil slices
|
|
for i := range instances {
|
|
if instances[i].Nodes == nil {
|
|
instances[i].Nodes = []PMGNodeSummary{}
|
|
}
|
|
}
|
|
|
|
response := PMGStatusResponse{
|
|
Instances: instances,
|
|
Total: len(instances),
|
|
}
|
|
|
|
return NewJSONResult(response), nil
|
|
}
|
|
|
|
func (e *PulseToolExecutor) executeGetMailStats(_ context.Context, args map[string]interface{}) (CallToolResult, error) {
|
|
if e.stateProvider == nil {
|
|
return NewTextResult("State provider not available."), nil
|
|
}
|
|
|
|
instanceFilter, _ := args["instance"].(string)
|
|
|
|
state := e.stateProvider.GetState()
|
|
|
|
if len(state.PMGInstances) == 0 {
|
|
return NewTextResult("No Proxmox Mail Gateway instances found. PMG monitoring may not be configured."), nil
|
|
}
|
|
|
|
// If filtering, find that specific instance
|
|
for _, pmg := range state.PMGInstances {
|
|
if instanceFilter != "" && pmg.ID != instanceFilter && pmg.Name != instanceFilter {
|
|
continue
|
|
}
|
|
|
|
if pmg.MailStats == nil {
|
|
if instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("No mail statistics available for PMG instance '%s'.", instanceFilter)), nil
|
|
}
|
|
continue
|
|
}
|
|
|
|
response := MailStatsResponse{
|
|
Instance: pmg.Name,
|
|
Stats: PMGMailStatsSummary{
|
|
Timeframe: pmg.MailStats.Timeframe,
|
|
TotalIn: pmg.MailStats.CountIn,
|
|
TotalOut: pmg.MailStats.CountOut,
|
|
SpamIn: pmg.MailStats.SpamIn,
|
|
SpamOut: pmg.MailStats.SpamOut,
|
|
VirusIn: pmg.MailStats.VirusIn,
|
|
VirusOut: pmg.MailStats.VirusOut,
|
|
BouncesIn: pmg.MailStats.BouncesIn,
|
|
BouncesOut: pmg.MailStats.BouncesOut,
|
|
BytesIn: pmg.MailStats.BytesIn,
|
|
BytesOut: pmg.MailStats.BytesOut,
|
|
GreylistCount: pmg.MailStats.GreylistCount,
|
|
RBLRejects: pmg.MailStats.RBLRejects,
|
|
AverageProcessTimeMs: pmg.MailStats.AverageProcessTimeMs,
|
|
},
|
|
}
|
|
|
|
return NewJSONResult(response), nil
|
|
}
|
|
|
|
if instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("PMG instance '%s' not found.", instanceFilter)), nil
|
|
}
|
|
|
|
return NewTextResult("No mail statistics available from any PMG instance."), nil
|
|
}
|
|
|
|
func (e *PulseToolExecutor) executeGetMailQueues(_ context.Context, args map[string]interface{}) (CallToolResult, error) {
|
|
if e.stateProvider == nil {
|
|
return NewTextResult("State provider not available."), nil
|
|
}
|
|
|
|
instanceFilter, _ := args["instance"].(string)
|
|
|
|
state := e.stateProvider.GetState()
|
|
|
|
if len(state.PMGInstances) == 0 {
|
|
return NewTextResult("No Proxmox Mail Gateway instances found. PMG monitoring may not be configured."), nil
|
|
}
|
|
|
|
// Collect queue data from all instances (or filtered instance)
|
|
for _, pmg := range state.PMGInstances {
|
|
if instanceFilter != "" && pmg.ID != instanceFilter && pmg.Name != instanceFilter {
|
|
continue
|
|
}
|
|
|
|
var queues []PMGQueueSummary
|
|
for _, node := range pmg.Nodes {
|
|
if node.QueueStatus != nil {
|
|
queues = append(queues, PMGQueueSummary{
|
|
Node: node.Name,
|
|
Active: node.QueueStatus.Active,
|
|
Deferred: node.QueueStatus.Deferred,
|
|
Hold: node.QueueStatus.Hold,
|
|
Incoming: node.QueueStatus.Incoming,
|
|
Total: node.QueueStatus.Total,
|
|
OldestAgeSeconds: node.QueueStatus.OldestAge,
|
|
})
|
|
}
|
|
}
|
|
|
|
if len(queues) == 0 {
|
|
if instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("No queue data available for PMG instance '%s'.", instanceFilter)), nil
|
|
}
|
|
continue
|
|
}
|
|
|
|
response := MailQueuesResponse{
|
|
Instance: pmg.Name,
|
|
Queues: queues,
|
|
}
|
|
|
|
return NewJSONResult(response), nil
|
|
}
|
|
|
|
if instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("PMG instance '%s' not found.", instanceFilter)), nil
|
|
}
|
|
|
|
return NewTextResult("No mail queue data available from any PMG instance."), nil
|
|
}
|
|
|
|
func (e *PulseToolExecutor) executeGetSpamStats(_ context.Context, args map[string]interface{}) (CallToolResult, error) {
|
|
if e.stateProvider == nil {
|
|
return NewTextResult("State provider not available."), nil
|
|
}
|
|
|
|
instanceFilter, _ := args["instance"].(string)
|
|
|
|
state := e.stateProvider.GetState()
|
|
|
|
if len(state.PMGInstances) == 0 {
|
|
return NewTextResult("No Proxmox Mail Gateway instances found. PMG monitoring may not be configured."), nil
|
|
}
|
|
|
|
for _, pmg := range state.PMGInstances {
|
|
if instanceFilter != "" && pmg.ID != instanceFilter && pmg.Name != instanceFilter {
|
|
continue
|
|
}
|
|
|
|
quarantine := PMGQuarantineSummary{}
|
|
if pmg.Quarantine != nil {
|
|
quarantine = PMGQuarantineSummary{
|
|
Spam: pmg.Quarantine.Spam,
|
|
Virus: pmg.Quarantine.Virus,
|
|
Attachment: pmg.Quarantine.Attachment,
|
|
Blacklisted: pmg.Quarantine.Blacklisted,
|
|
Total: pmg.Quarantine.Spam + pmg.Quarantine.Virus + pmg.Quarantine.Attachment + pmg.Quarantine.Blacklisted,
|
|
}
|
|
}
|
|
|
|
var distribution []PMGSpamBucketSummary
|
|
for _, bucket := range pmg.SpamDistribution {
|
|
distribution = append(distribution, PMGSpamBucketSummary{
|
|
Score: bucket.Score,
|
|
Count: bucket.Count,
|
|
})
|
|
}
|
|
|
|
response := SpamStatsResponse{
|
|
Instance: pmg.Name,
|
|
Quarantine: quarantine,
|
|
Distribution: distribution,
|
|
}
|
|
|
|
if response.Distribution == nil {
|
|
response.Distribution = []PMGSpamBucketSummary{}
|
|
}
|
|
|
|
return NewJSONResult(response), nil
|
|
}
|
|
|
|
if instanceFilter != "" {
|
|
return NewTextResult(fmt.Sprintf("PMG instance '%s' not found.", instanceFilter)), nil
|
|
}
|
|
|
|
return NewTextResult("No spam statistics available from any PMG instance."), nil
|
|
}
|