forked from hswaw/hscloud
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