diff --git a/cmd/localclient.go b/cmd/localclient.go index dcd9765..2f34770 100644 --- a/cmd/localclient.go +++ b/cmd/localclient.go @@ -33,7 +33,7 @@ var localClientCmd = &cobra.Command{ Short: "Local LQL Client", Long: `Local LQL Client -Connects to the local socket`, +Requires a local lql unix socket.`, Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { sReplacer := strings.NewReplacer("{site}", args[0]) diff --git a/cmd/localserver.go b/cmd/localserver.go new file mode 100644 index 0000000..7c0f994 --- /dev/null +++ b/cmd/localserver.go @@ -0,0 +1,98 @@ +package cmd + +import ( + "os" + "os/signal" + "strings" + "syscall" + + "github.com/spf13/cobra" + "github.com/webmeisterei/lql_api/lql" + + log "github.com/sirupsen/logrus" +) + +func init() { + localServerMinConns := 0 + localServerMaxConns := 0 + localServerCmd.Flags().IntVarP(&localServerMinConns, "min-conns", "m", 2, "minimal Client Connections") + localServerCmd.Flags().IntVarP(&localServerMaxConns, "max-conns", "x", 5, "maximal Client Connections") + + localServerCmd.Flags().StringP("socket", "s", "/opt/omd/sites/{site}/tmp/run/live", "Socket") + localServerCmd.Flags().StringP("htpasswd", "t", "/opt/omd/sites/{site}/etc/htpasswd", "htpasswd file") + localServerCmd.Flags().BoolP("debug", "d", false, "Enable Debug on stderr") + localServerCmd.Flags().StringP("listen", "l", ":8080", "Address to listen on") + rootCmd.AddCommand(localServerCmd) +} + +var localServerCmd = &cobra.Command{ + Use: "localserver [site]", + Short: "Local LQL Server", + Long: `Local LQL Server + +Requires a local lql unix socket.`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + sReplacer := strings.NewReplacer("{site}", args[0]) + logger := log.New() + logger.SetOutput(os.Stderr) + if !cmd.Flag("debug").Changed { + logger.SetLevel(log.InfoLevel) + } else { + logger.SetLevel(log.TraceLevel) + } + + socket, err := cmd.Flags().GetString("socket") + if err != nil { + logger.WithField("error", err).Error() + return + } + localSocket := sReplacer.Replace(socket) + var lqlClient *lql.Client + + logger.WithFields(log.Fields{"localSocket": localSocket}).Debug("Sockets") + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM) + go func(c chan os.Signal) { + // Wait for a SIGINT or SIGKILL: + sig := <-c + logger.WithFields(log.Fields{"signal": sig}).Info("Caught signal shutting down.") + + // Stop listening (and unlink the socket if unix type): + if lqlClient != nil { + lqlClient.Close() + } + + os.Exit(1) + }(sigc) + + minConns, err := cmd.Flags().GetInt("min-conns") + if err != nil { + logger.WithField("error", err).Error() + return + } + maxConns, err := cmd.Flags().GetInt("max-conns") + if err != nil { + logger.WithField("error", err).Error() + return + } + + lqlClient, err = lql.NewClient(minConns, maxConns, "unix", localSocket) + if err != nil { + logger.WithField("error", err).Error() + return + } + defer lqlClient.Close() + lqlClient.SetLogger(logger) + + htpasswd := sReplacer.Replace(cmd.Flag("htpasswd").Value.String()) + server, err := lql.NewServer(lqlClient, logger, htpasswd) + if err != nil { + logger.WithField("error", err).Error() + return + } + + server.ListenAndServe(cmd.Flag("listen").Value.String()) + }, +} diff --git a/cmd/sshserver.go b/cmd/sshserver.go index 0184938..4e9a876 100644 --- a/cmd/sshserver.go +++ b/cmd/sshserver.go @@ -31,6 +31,7 @@ func init() { sshServerCmd.Flags().StringP("ssh-user", "U", "root", "SSH User") sshServerCmd.Flags().StringP("ssh-keyfile", "k", "~/.ssh/id_rsa", "Keyfile") sshServerCmd.Flags().StringP("ssh-password", "p", "", "Password") + sshServerCmd.Flags().StringP("listen", "l", ":8080", "Address to listen on") rootCmd.AddCommand(sshServerCmd) } @@ -45,11 +46,7 @@ If you don't provide ssh-keyfile and ssh-password it will use your local agent. `, Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { - sReplacer := strings.NewReplacer("{site}", args[0]) - destSocket := sReplacer.Replace(cmd.Flag("socket").Value.String()) - localSocket := path.Join(os.TempDir(), "lql-client.sock") - var tunnel *myssh.Tunnel - var lqlClient *lql.Client + logger := log.New() logger.SetOutput(os.Stderr) if !cmd.Flag("debug").Changed { @@ -58,6 +55,18 @@ If you don't provide ssh-keyfile and ssh-password it will use your local agent. logger.SetLevel(log.TraceLevel) } + destSocket, err := cmd.Flags().GetString("socket") + if err != nil { + logger.WithField("error", err).Error() + return + } + sReplacer := strings.NewReplacer("{site}", args[0]) + destSocket = sReplacer.Replace(destSocket) + + localSocket := path.Join(os.TempDir(), "lql-client.sock") + var tunnel *myssh.Tunnel + var lqlClient *lql.Client + logger.WithFields(log.Fields{"destSocket": destSocket, "localSocket": localSocket}).Debug("Sockets") sigc := make(chan os.Signal, 1) @@ -134,12 +143,13 @@ If you don't provide ssh-keyfile and ssh-password it will use your local agent. defer lqlClient.Close() lqlClient.SetLogger(logger) - server, err := lql.NewServer(lqlClient, logger, "") + htpasswd := sReplacer.Replace(cmd.Flag("htpasswd").Value.String()) + server, err := lql.NewServer(lqlClient, logger, htpasswd) if err != nil { logger.WithField("error", err).Error() return } - server.ListenAndServe(":8080") + server.ListenAndServe(cmd.Flag("listen").Value.String()) }, } diff --git a/example.sh b/example.sh index 0ccf831..2f6ec3c 100755 --- a/example.sh +++ b/example.sh @@ -1,14 +1,18 @@ #!/bin/bash -set -ex - +ARGS=$2 SERVER="http://localhost:8080" +if [ -n "$1" ]; then + SERVER=$1 +fi + +set -ex # GET Hosts -curl -X POST -d '{"method": "GET", "table": "hosts", "columns": ["name", "address", "groups"]}' $SERVER/v1/raw +curl $2 -X POST -d '{"method": "GET", "table": "hosts", "columns": ["name", "address", "groups"]}' $SERVER/v1/raw # GET Hosts with limit -curl -X POST -d '{"method": "GET", "table": "hosts", "columns": ["name", "address", "groups"], "limit": 3}' $SERVER/v1/raw +curl $2 -X POST -d '{"method": "GET", "table": "hosts", "columns": ["name", "address", "groups"], "limit": 3}' $SERVER/v1/raw # host stats from the tactical_overview widget -curl -X POST -d '{"method": "GET", "table": "hosts", "query": [["Stats", "state >= 0"], ["Stats", "state > 0"], ["Stats", "scheduled_downtime_depth = 0"], ["StatsAnd", "2"], ["Stats", "state > 0"], ["Stats", "scheduled_downtime_depth = 0"], ["Stats", "acknowledged = 0"], ["StatsAnd", "3"], ["Stats", "host_staleness >= 1.5"], ["Stats", "host_scheduled_downtime_depth = 0"], ["StatsAnd", "2"]]}' $SERVER/v1/raw \ No newline at end of file +curl $2 -X POST -d '{"method": "GET", "table": "hosts", "query": [["Stats", "state >= 0"], ["Stats", "state > 0"], ["Stats", "scheduled_downtime_depth = 0"], ["StatsAnd", "2"], ["Stats", "state > 0"], ["Stats", "scheduled_downtime_depth = 0"], ["Stats", "acknowledged = 0"], ["StatsAnd", "3"], ["Stats", "host_staleness >= 1.5"], ["Stats", "host_scheduled_downtime_depth = 0"], ["StatsAnd", "2"]]}' $SERVER/v1/raw \ No newline at end of file diff --git a/lql_api@.service b/lql_api@.service new file mode 100644 index 0000000..88fc102 --- /dev/null +++ b/lql_api@.service @@ -0,0 +1,12 @@ +[Unit] +Description=LQL API Server +After=network.target + +[Service] +Type=simple +User=%i +Group=%i +ExecStart=/usr/local/bin/lql_api localserver %i --listen localhost:8080 -d + +[Install] +WantedBy=multi-user.target \ No newline at end of file