Merge branch 'master' of /home/q3k/Projects/hscloud/go/src/code.hackerspace.pl/q3k/arista-proxy
commit
5f5a0353c2
|
@ -0,0 +1,2 @@
|
||||||
|
arista-proxy
|
||||||
|
*swp
|
|
@ -0,0 +1,49 @@
|
||||||
|
Old Shitty Arista eAPI/Capi <-> gRPC proxy
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
Our Arista 7148S does not support gRPC/OpenConfig, so we have to make our own damn gRPC proxy.
|
||||||
|
|
||||||
|
The schema is supposed to be 1:1 mapped to the JSON-RPC EAPI. This is just a dumb proxy.
|
||||||
|
|
||||||
|
PKI
|
||||||
|
---
|
||||||
|
|
||||||
|
This service uses [HSPKI](https://code.hackerspace.pl/q3k/hspki), you will need to generate development TLS certificates for local use.
|
||||||
|
|
||||||
|
Getting and Building
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
go get -d -u code.hackerspace.pl/q3k/arista-proxy
|
||||||
|
go generate code.hackerspace.pl/q3k/arista-proxy/proto
|
||||||
|
go build code.hackerspace.pl/q3k/arista-proxy
|
||||||
|
|
||||||
|
Debug Status Page
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The `debug_address` flag controls spawning an HTTP server useful for debugging. You can use it to inspect gRPC request and view general status information of the proxy.
|
||||||
|
|
||||||
|
Flags
|
||||||
|
-----
|
||||||
|
|
||||||
|
./arista-proxy -help
|
||||||
|
Usage of ./arista-proxy:
|
||||||
|
-alsologtostderr
|
||||||
|
log to standard error as well as files
|
||||||
|
-arista_api string
|
||||||
|
Arista remote endpoint (default "http://admin:password@1.2.3.4:80/command-api")
|
||||||
|
-debug_address string
|
||||||
|
Debug HTTP listen address, or empty to disable (default "127.0.0.1:42000")
|
||||||
|
-listen_address string
|
||||||
|
gRPC listen address (default "127.0.0.1:43001")
|
||||||
|
-log_backtrace_at value
|
||||||
|
when logging hits line file:N, emit a stack trace
|
||||||
|
-log_dir string
|
||||||
|
If non-empty, write log files in this directory
|
||||||
|
-logtostderr
|
||||||
|
log to standard error instead of files
|
||||||
|
-stderrthreshold value
|
||||||
|
logs at or above this threshold go to stderr
|
||||||
|
-v value
|
||||||
|
log level for V logs
|
||||||
|
-vmodule value
|
||||||
|
comma-separated list of pattern=N settings for file-filtered logging
|
|
@ -0,0 +1,31 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package proto;
|
||||||
|
|
||||||
|
message ShowVersionRequest {
|
||||||
|
};
|
||||||
|
|
||||||
|
message ShowVersionResponse {
|
||||||
|
string model_name = 1;
|
||||||
|
string internal_version = 2;
|
||||||
|
string system_mac_address = 3;
|
||||||
|
string serial_number = 4;
|
||||||
|
int64 mem_total = 5;
|
||||||
|
double bootup_timestamp = 6;
|
||||||
|
int64 mem_free = 7;
|
||||||
|
string version = 8;
|
||||||
|
string architecture = 9;
|
||||||
|
string internal_build_id = 10;
|
||||||
|
string hardware_revision = 11;
|
||||||
|
};
|
||||||
|
|
||||||
|
message ShowEnvironmentTemperatureRequest {
|
||||||
|
};
|
||||||
|
|
||||||
|
message ShowEnvironmentTemperatureResponse {
|
||||||
|
};
|
||||||
|
|
||||||
|
service AristaProxy {
|
||||||
|
rpc ShowVersion(ShowVersionRequest) returns (ShowVersionResponse);
|
||||||
|
rpc ShowEnvironmentTemperature(ShowEnvironmentTemperatureRequest) returns (ShowEnvironmentTemperatureResponse);
|
||||||
|
};
|
|
@ -0,0 +1,67 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.hackerspace.pl/q3k/mirko"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/ybbus/jsonrpc"
|
||||||
|
|
||||||
|
pb "code.hackerspace.pl/q3k/arista-proxy/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagAristaAPI string
|
||||||
|
)
|
||||||
|
|
||||||
|
type aristaClient struct {
|
||||||
|
rpc jsonrpc.RPCClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aristaClient) structuredCall(res interface{}, command ...string) error {
|
||||||
|
cmd := struct {
|
||||||
|
Version int `json:"version"`
|
||||||
|
Cmds []string `json:"cmds"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
}{
|
||||||
|
Version: 1,
|
||||||
|
Cmds: command,
|
||||||
|
Format: "json",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.rpc.CallFor(res, "runCmds", cmd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not execute structured call: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
arista *aristaClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.StringVar(&flagAristaAPI, "arista_api", "http://admin:password@1.2.3.4:80/command-api", "Arista remote endpoint")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
arista := &aristaClient{
|
||||||
|
rpc: jsonrpc.NewClient(flagAristaAPI),
|
||||||
|
}
|
||||||
|
|
||||||
|
m := mirko.New()
|
||||||
|
if err := m.Listen(); err != nil {
|
||||||
|
glog.Exitf("Listen(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &server{
|
||||||
|
arista: arista,
|
||||||
|
}
|
||||||
|
pb.RegisterAristaProxyServer(m.GRPC(), s)
|
||||||
|
|
||||||
|
if err := m.Serve(); err != nil {
|
||||||
|
glog.Exitf("Serve(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
arista.pb.go
|
|
@ -0,0 +1,3 @@
|
||||||
|
//go:generate protoc -I.. ../arista.proto --go_out=plugins=grpc:.
|
||||||
|
|
||||||
|
package proto
|
|
@ -0,0 +1,97 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
pb "code.hackerspace.pl/q3k/arista-proxy/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *server) ShowVersion(ctx context.Context, req *pb.ShowVersionRequest) (*pb.ShowVersionResponse, error) {
|
||||||
|
var version []struct {
|
||||||
|
ModelName string `json:"modelName"`
|
||||||
|
InternalVersion string `json:"internalVersion"`
|
||||||
|
SystemMacAddress string `json:"systemMacAddress"`
|
||||||
|
SerialNumber string `json:"serialNumber"`
|
||||||
|
MemTotal int64 `json:"memTotal"`
|
||||||
|
BootupTimestamp float64 `json:"bootupTimestamp"`
|
||||||
|
MemFree int64 `json:"memFree"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
InternalBuildId string `json:"internalBuildId"`
|
||||||
|
HardwareRevision string `json:"hardwareRevision"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.arista.structuredCall(&version, "show version")
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("EOS Capi: show version: %v", err)
|
||||||
|
return nil, status.Error(codes.Unavailable, "EOS Capi call failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(version) != 1 {
|
||||||
|
glog.Errorf("Expected 1-length result, got %d", len(version))
|
||||||
|
return nil, status.Error(codes.Internal, "Internal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := version[0]
|
||||||
|
|
||||||
|
return &pb.ShowVersionResponse{
|
||||||
|
ModelName: d.ModelName,
|
||||||
|
InternalVersion: d.InternalVersion,
|
||||||
|
SystemMacAddress: d.SystemMacAddress,
|
||||||
|
SerialNumber: d.SerialNumber,
|
||||||
|
MemTotal: d.MemTotal,
|
||||||
|
BootupTimestamp: d.BootupTimestamp,
|
||||||
|
MemFree: d.MemFree,
|
||||||
|
Version: d.Version,
|
||||||
|
Architecture: d.Architecture,
|
||||||
|
InternalBuildId: d.InternalBuildId,
|
||||||
|
HardwareRevision: d.HardwareRevision,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type temperatureSensor struct {
|
||||||
|
InAlertState bool `json:"inAlertState"`
|
||||||
|
MaxTemperature float64 `json:"maxTemperature"`
|
||||||
|
RelPos int64 `json:"relPos"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AlertCount int64 `json:"alertCount"`
|
||||||
|
CurrentTemperature float64 `json:"currentTemperature"`
|
||||||
|
OverheatThreshold float64 `json:"overheatThreshold"`
|
||||||
|
CriticalThreshold float64 `json:"criticalThreshold"`
|
||||||
|
HwStatus string `json:"hwStatus"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) ShowEnvironmentTemperature(ctx context.Context, req *pb.ShowEnvironmentTemperatureRequest) (*pb.ShowEnvironmentTemperatureResponse, error) {
|
||||||
|
var response []struct {
|
||||||
|
PowerSuppplySlots []struct {
|
||||||
|
TempSensors []temperatureSensor `json:"tempSensors"`
|
||||||
|
EntPhysicalClass string `json:"entPhysicalClass"`
|
||||||
|
RelPos int64 `json:"relPos"`
|
||||||
|
} `json:"powerSupplySlots"`
|
||||||
|
|
||||||
|
ShutdownOnOverheat bool `json:"shutdownOnOverheat"`
|
||||||
|
TempSensors []temperatureSensor `json:"tempSensors"`
|
||||||
|
SystemStatus string `json:"systemStatus"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.arista.structuredCall(&response, "show environment temperature")
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("EOS Capi: show environment temperature: %v", err)
|
||||||
|
return nil, status.Error(codes.Unavailable, "EOS Capi call failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response) != 1 {
|
||||||
|
glog.Errorf("Expected 1-length result, got %d", len(response))
|
||||||
|
return nil, status.Error(codes.Internal, "Internal error")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := response[0]
|
||||||
|
glog.Infof("%+v", d)
|
||||||
|
|
||||||
|
return &pb.ShowEnvironmentTemperatureResponse{}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue