commit 753d63f893b94e35b6eced1b08838ebf1db7acde Author: Serge Bazanski Date: Sun Oct 14 15:21:46 2018 -0700 initial commit diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..50df36f --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,189 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + digest = "1:2af0bc306f77bef606f6f6033652f0de3cc80e77008f4da52197b4b0a6be5853" + name = "code.hackerspace.pl/q3k/hspki" + packages = ["."] + pruneopts = "UT" + revision = "624295da664fea57a1e3bd8cee639ae04f4c0ccf" + +[[projects]] + branch = "master" + digest = "1:b2619fc3d4ced0542d16962c73812d2e70ed09eb29cdfc3d767a85b726dab150" + name = "code.hackerspace.pl/q3k/mirko" + packages = ["."] + pruneopts = "UT" + revision = "4f7ee51e9a73e05e5b2dbc6cfa178ace2f83b155" + +[[projects]] + digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214" + name = "github.com/StackExchange/wmi" + packages = ["."] + pruneopts = "UT" + revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" + version = "1.0.0" + +[[projects]] + digest = "1:64a5a67c69b70c2420e607a8545d674a23778ed9c3e80607bfd17b77c6c87f6a" + name = "github.com/go-ole/go-ole" + packages = [ + ".", + "oleutil", + ] + pruneopts = "UT" + revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" + version = "v1.2.1" + +[[projects]] + branch = "master" + digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" + name = "github.com/golang/glog" + packages = ["."] + pruneopts = "UT" + revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" + +[[projects]] + digest = "1:5d1b5a25486fc7d4e133646d834f6fca7ba1cef9903d40e7aa786c41b89e9e91" + name = "github.com/golang/protobuf" + packages = [ + "proto", + "protoc-gen-go/descriptor", + "ptypes", + "ptypes/any", + "ptypes/duration", + "ptypes/timestamp", + ] + pruneopts = "UT" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:a7cc4447e0c2432f217fd2bed937c100ec475272a7a9306477170c2934297357" + name = "github.com/q3k/statusz" + packages = ["."] + pruneopts = "UT" + revision = "924f04ea71149b75f5b10f426cc4c39d6d90f6f2" + +[[projects]] + branch = "master" + digest = "1:8f042d4b4c48d38d7a1201096299bd26d044c97c032ab20ee171fe5b0d201d2e" + name = "github.com/shirou/gopsutil" + packages = [ + "internal/common", + "load", + ] + pruneopts = "UT" + revision = "a11c78ba2c13c5b1ee59c53296ba35f92f0ce658" + +[[projects]] + branch = "master" + digest = "1:505dbee0833715a72a529bb57c354826ad42a4496fad787fa143699b4de1a6d0" + name = "golang.org/x/net" + packages = [ + "context", + "http/httpguts", + "http2", + "http2/hpack", + "idna", + "internal/timeseries", + "trace", + ] + pruneopts = "UT" + revision = "49bb7cea24b1df9410e1712aa6433dae904ff66a" + +[[projects]] + branch = "master" + digest = "1:f5aa274a0377f85735edc7fedfb0811d3cbc20af91633797cb359e29c3272271" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + ] + pruneopts = "UT" + revision = "fa43e7bc11baaae89f3f902b2b4d832b68234844" + +[[projects]] + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" + name = "golang.org/x/text" + packages = [ + "collate", + "collate/build", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "language", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + ] + pruneopts = "UT" + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "master" + digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" + name = "google.golang.org/genproto" + packages = ["googleapis/rpc/status"] + pruneopts = "UT" + revision = "af9cb2a35e7f169ec875002c1829c9b315cddc04" + +[[projects]] + digest = "1:dc0c170b110c22d9a4eccf08ab58490608053eac450bf456f3aaf9b30a668781" + name = "google.golang.org/grpc" + packages = [ + ".", + "balancer", + "balancer/base", + "balancer/roundrobin", + "codes", + "connectivity", + "credentials", + "encoding", + "encoding/proto", + "grpclog", + "internal", + "internal/backoff", + "internal/channelz", + "internal/envconfig", + "internal/grpcrand", + "internal/transport", + "keepalive", + "metadata", + "naming", + "peer", + "reflection", + "reflection/grpc_reflection_v1alpha", + "resolver", + "resolver/dns", + "resolver/passthrough", + "stats", + "status", + "tap", + ] + pruneopts = "UT" + revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1" + version = "v1.15.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "code.hackerspace.pl/q3k/mirko", + "github.com/golang/glog", + "github.com/golang/protobuf/proto", + "golang.org/x/net/context", + "google.golang.org/grpc", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/status", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..8e54d33 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,50 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "code.hackerspace.pl/q3k/mirko" + +[[constraint]] + branch = "master" + name = "github.com/golang/glog" + +[[constraint]] + name = "github.com/golang/protobuf" + version = "1.2.0" + +[[constraint]] + branch = "master" + name = "golang.org/x/net" + +[[constraint]] + name = "google.golang.org/grpc" + version = "1.15.0" + +[prune] + go-tests = true + unused-packages = true diff --git a/client.go b/client.go new file mode 100644 index 0000000..e421068 --- /dev/null +++ b/client.go @@ -0,0 +1,348 @@ +package main + +import ( + "context" + "crypto/tls" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "strings" + + "code.hackerspace.pl/q3k/mirko" + "github.com/cenkalti/backoff" + "github.com/golang/glog" +) + +var ( + reSessionCookie = regexp.MustCompile("'SESSION_COOKIE' : '([^']*)'") + reIpmiPriv = regexp.MustCompile("'IPMI_PRIV' : ([^,]*)") + reExtPriv = regexp.MustCompile("'EXT_PRIV' : ([^,]*)") + reSystemModel = regexp.MustCompile("'SYSTEM_MODEL' : '([^']*)'") + reArgument = regexp.MustCompile("([^<]*)") +) + +var ( + ErrorNoFreeSlot = fmt.Errorf("iDRAC reports no free slot") +) + +type cmcRequestType int + +const ( + cmcRequestKVMDetails cmcRequestType = iota +) + +type cmcResponse struct { + data interface{} + err error +} + +type cmcRequest struct { + t cmcRequestType + req interface{} + res chan cmcResponse + canceled bool +} + +type KVMDetails struct { + arguments []string +} + +type cmcClient struct { + session string + req chan *cmcRequest +} + +func (c *cmcClient) RequestKVMDetails(ctx context.Context, slot int) (*KVMDetails, error) { + r := &cmcRequest{ + t: cmcRequestKVMDetails, + req: slot, + res: make(chan cmcResponse, 1), + } + mirko.Trace(ctx, "cmcRequestKVMDetails: requesting...") + c.req <- r + mirko.Trace(ctx, "cmcRequestKVMDetails: requested.") + + select { + case <-ctx.Done(): + r.canceled = true + return nil, context.Canceled + case res := <-r.res: + mirko.Trace(ctx, "cmcRequestKVMDetails: got response") + if res.err != nil { + return nil, res.err + } + return res.data.(*KVMDetails), nil + } +} + +func NewCMCClient() *cmcClient { + return &cmcClient{ + req: make(chan *cmcRequest, 4), + } +} + +func (c *cmcClient) Run(ctx context.Context) { + for { + select { + case <-ctx.Done(): + c.logout() + return + case msg := <-c.req: + c.handle(msg) + } + } +} + +func (c *cmcClient) handle(r *cmcRequest) { + switch { + case r.t == cmcRequestKVMDetails: + var details *KVMDetails + slot := r.req.(int) + err := backoff.Retry(func() error { + if err := c.login(); err != nil { + return err + } + url, err := c.getiDRACURL(slot) + if err != nil { + return err + } + details, err = c.getiDRACJNLP(url) + return err + }, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 2)) + + if err != nil { + r.res <- cmcResponse{err: err} + } + + r.res <- cmcResponse{data: details} + default: + panic("invalid cmcRequestType") + } +} + +func makeUrl(path string) string { + if strings.HasSuffix(flagCMCAddress, "/") { + return flagCMCAddress + path + } + return flagCMCAddress + "/" + path +} + +func (c *cmcClient) transport() *http.Transport { + return &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } +} + +func (c *cmcClient) addCookies(req *http.Request) { + req.AddCookie(&http.Cookie{Name: "custom_domain", Value: ""}) + req.AddCookie(&http.Cookie{Name: "domain_selected", Value: "This Chassis"}) + if c.session != "" { + glog.Infof("Adding session: %v", c.session) + req.AddCookie(&http.Cookie{Name: "sid", Value: c.session}) + } +} + +func (c *cmcClient) getiDRACURL(slot int) (string, error) { + if c.session == "" { + return "", fmt.Errorf("not logged in") + } + + url := makeUrl(pathiDRACURL) + fmt.Sprintf("?vKVM=1&serverSlot=%d", slot) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", fmt.Errorf("GET prepare to %s failed: %v", pathLogin, err) + } + c.addCookies(req) + + cl := &http.Client{ + Transport: c.transport(), + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + resp, err := cl.Do(req) + if err != nil { + return "", fmt.Errorf("GET to %s failed: %v", pathLogin, err) + } + + if resp.StatusCode != 302 { + return "", fmt.Errorf("expected 302 on iDRAC URL redirect, got %v instead", resp.Status) + } + + loc, _ := resp.Location() + + if !strings.Contains(loc.String(), "cmc_sess_id") { + c.session = "" + return "", fmt.Errorf("redirect URL contains no session ID - session timed out?") + } + + return loc.String(), nil +} + +func (c *cmcClient) getiDRACJNLP(loginUrl string) (*KVMDetails, error) { + lurl, err := url.Parse(loginUrl) + if err != nil { + return nil, err + } + + sessid := lurl.Query().Get("cmc_sess_id") + if sessid == "" { + return nil, fmt.Errorf("no cmc_sess_id in iDRAC login URL") + } + + createURL := *lurl + createURL.Path = "/Applications/dellUI/RPC/WEBSES/create.asp" + createURL.RawQuery = "" + + values := url.Values{} + values.Set("WEBVAR_USERNAME", "cmc") + values.Set("WEBVAR_PASSWORD", sessid) + values.Set("WEBVAR_ISCMCLOGIN", "1") + valuesString := values.Encode() + req, err := http.NewRequest("POST", createURL.String(), strings.NewReader(valuesString)) + + cl := &http.Client{ + Transport: c.transport(), + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + resp, err := cl.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, _ := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + first := func(v [][]byte) string { + if len(v) < 1 { + return "" + } + return string(v[1]) + } + + sessionCookie := first(reSessionCookie.FindSubmatch(data)) + ipmiPriv := first(reIpmiPriv.FindSubmatch(data)) + extPriv := first(reExtPriv.FindSubmatch(data)) + systemModel := first(reSystemModel.FindSubmatch(data)) + + if sessionCookie == "Failure_No_Free_Slot" { + return nil, ErrorNoFreeSlot + } + + jnlpURL := *lurl + jnlpURL.Path = "/Applications/dellUI/Java/jviewer.jnlp" + jnlpURL.RawQuery = "" + + req, err = http.NewRequest("GET", jnlpURL.String(), nil) + for _, cookie := range resp.Cookies() { + glog.Infof("%+v", cookie) + req.AddCookie(cookie) + } + req.AddCookie(&http.Cookie{Name: "SessionCookie", Value: sessionCookie}) + req.AddCookie(&http.Cookie{Name: "SessionCookieUser", Value: "cmc"}) + req.AddCookie(&http.Cookie{Name: "IPMIPriv", Value: ipmiPriv}) + req.AddCookie(&http.Cookie{Name: "ExtPriv", Value: extPriv}) + req.AddCookie(&http.Cookie{Name: "SystemModel", Value: systemModel}) + + resp, err = cl.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + // yes we do parse xml with regex why are you asking + matches := reArgument.FindAllSubmatch(data, -1) + + res := &KVMDetails{ + arguments: []string{}, + } + for _, match := range matches { + res.arguments = append(res.arguments, string(match[1])) + } + + return res, nil +} + +func (c *cmcClient) login() error { + if c.session != "" { + return nil + } + + values := url.Values{} + values.Set("ST2", "NOTSET") + values.Set("user", flagCMCUsername) + values.Set("user_id", flagCMCUsername) + values.Set("password", flagCMCPassword) + values.Set("WEBSERVER_timeout", "1800") + values.Set("WEBSERVER_timeout_select", "1800") + valuesString := values.Encode() + glog.Info(valuesString) + req, err := http.NewRequest("POST", makeUrl(pathLogin), strings.NewReader(valuesString)) + if err != nil { + return fmt.Errorf("POST prepare to %s failed: %v", pathLogin, err) + } + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + c.addCookies(req) + + cl := &http.Client{ + Transport: c.transport(), + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + resp, err := cl.Do(req) + if err != nil { + return fmt.Errorf("POST to %s failed: %v", pathLogin, err) + } + glog.Infof("Login response: %s", resp.Status) + defer resp.Body.Close() + for _, cookie := range resp.Cookies() { + if cookie.Name == "sid" { + c.session = cookie.Value + break + } + } + if c.session == "" { + return fmt.Errorf("login unsuccesful") + } + return nil +} + +func (c *cmcClient) logout() { + glog.Infof("Killing session..") + if c.session == "" { + return + } + + req, err := http.NewRequest("GET", makeUrl(pathLogout), nil) + if err != nil { + glog.Errorf("GET prepare to %s failed: %v", pathLogin, err) + } + c.addCookies(req) + + cl := &http.Client{ + Transport: c.transport(), + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + resp, err := cl.Do(req) + if err != nil { + glog.Errorf("GET to %s failed: %v", pathLogin, err) + } + glog.Infof("Logout response: %s", resp.Status) + return +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..266de97 --- /dev/null +++ b/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "flag" + + "code.hackerspace.pl/q3k/mirko" + "github.com/golang/glog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + pb "code.hackerspace.pl/q3k/cmc-proxy/proto" +) + +var ( + flagCMCAddress string + flagCMCUsername string + flagCMCPassword string +) + +const ( + pathLogin = "cgi-bin/webcgi/login" + pathLogout = "cgi-bin/webcgi/logout" + pathiDRACURL = "cgi-bin/webcgi/blade_iDRAC_url" +) + +func init() { + flag.Set("logtostderr", "true") +} + +type service struct { + cmc *cmcClient +} + +func (s *service) GetKVMData(ctx context.Context, req *pb.GetKVMDataRequest) (*pb.GetKVMDataResponse, error) { + if req.BladeNum < 1 || req.BladeNum > 16 { + return nil, status.Error(codes.InvalidArgument, "blade_num must be [1,16]") + } + + details, err := s.cmc.RequestKVMDetails(ctx, int(req.BladeNum)) + if err != nil { + glog.Errorf("RequestKVMDetails(_, %d): %v", req.BladeNum, err) + return nil, status.Error(codes.Unavailable, "CMC unavailable") + } + + return &pb.GetKVMDataResponse{ + Arguments: details.arguments, + }, nil +} + +func main() { + flag.StringVar(&flagCMCAddress, "cmc_address", "https://10.10.10.10", "URL of Dell M1000e CMC") + flag.StringVar(&flagCMCUsername, "cmc_username", "root", "Login username for CMC") + flag.StringVar(&flagCMCPassword, "cmc_password", "", "Login password for CMC") + flag.Parse() + + m := mirko.New() + if err := m.Listen(); err != nil { + glog.Exitf("Could not listen: %v", err) + } + + s := &service{ + cmc: NewCMCClient(), + } + pb.RegisterCMCProxyServer(m.GRPC(), s) + + if err := m.Serve(); err != nil { + glog.Exitf("Could not run: %v", err) + } + + go s.cmc.Run(m.Context()) + glog.Info("Running.") + + <-m.Done() +} diff --git a/proto/generate.go b/proto/generate.go new file mode 100644 index 0000000..fc6193d --- /dev/null +++ b/proto/generate.go @@ -0,0 +1,3 @@ +//go:generate protoc -I.. ../proxy.proto --go_out=plugins=grpc:. + +package proto diff --git a/proto/proxy.pb.go b/proto/proxy.pb.go new file mode 100644 index 0000000..ff1d00b --- /dev/null +++ b/proto/proxy.pb.go @@ -0,0 +1,194 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: proxy.proto + +package proto + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type GetKVMDataRequest struct { + BladeNum int64 `protobuf:"varint,1,opt,name=blade_num,json=bladeNum,proto3" json:"blade_num,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKVMDataRequest) Reset() { *m = GetKVMDataRequest{} } +func (m *GetKVMDataRequest) String() string { return proto.CompactTextString(m) } +func (*GetKVMDataRequest) ProtoMessage() {} +func (*GetKVMDataRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_700b50b08ed8dbaf, []int{0} +} + +func (m *GetKVMDataRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKVMDataRequest.Unmarshal(m, b) +} +func (m *GetKVMDataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKVMDataRequest.Marshal(b, m, deterministic) +} +func (m *GetKVMDataRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKVMDataRequest.Merge(m, src) +} +func (m *GetKVMDataRequest) XXX_Size() int { + return xxx_messageInfo_GetKVMDataRequest.Size(m) +} +func (m *GetKVMDataRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetKVMDataRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKVMDataRequest proto.InternalMessageInfo + +func (m *GetKVMDataRequest) GetBladeNum() int64 { + if m != nil { + return m.BladeNum + } + return 0 +} + +type GetKVMDataResponse struct { + Arguments []string `protobuf:"bytes,1,rep,name=arguments,proto3" json:"arguments,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetKVMDataResponse) Reset() { *m = GetKVMDataResponse{} } +func (m *GetKVMDataResponse) String() string { return proto.CompactTextString(m) } +func (*GetKVMDataResponse) ProtoMessage() {} +func (*GetKVMDataResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_700b50b08ed8dbaf, []int{1} +} + +func (m *GetKVMDataResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetKVMDataResponse.Unmarshal(m, b) +} +func (m *GetKVMDataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetKVMDataResponse.Marshal(b, m, deterministic) +} +func (m *GetKVMDataResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetKVMDataResponse.Merge(m, src) +} +func (m *GetKVMDataResponse) XXX_Size() int { + return xxx_messageInfo_GetKVMDataResponse.Size(m) +} +func (m *GetKVMDataResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetKVMDataResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetKVMDataResponse proto.InternalMessageInfo + +func (m *GetKVMDataResponse) GetArguments() []string { + if m != nil { + return m.Arguments + } + return nil +} + +func init() { + proto.RegisterType((*GetKVMDataRequest)(nil), "proto.GetKVMDataRequest") + proto.RegisterType((*GetKVMDataResponse)(nil), "proto.GetKVMDataResponse") +} + +func init() { proto.RegisterFile("proxy.proto", fileDescriptor_700b50b08ed8dbaf) } + +var fileDescriptor_700b50b08ed8dbaf = []byte{ + // 156 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x28, 0xca, 0xaf, + 0xa8, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x06, 0x5c, 0x82, 0xee, + 0xa9, 0x25, 0xde, 0x61, 0xbe, 0x2e, 0x89, 0x25, 0x89, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, + 0x42, 0xd2, 0x5c, 0x9c, 0x49, 0x39, 0x89, 0x29, 0xa9, 0xf1, 0x79, 0xa5, 0xb9, 0x12, 0x8c, 0x0a, + 0x8c, 0x1a, 0xcc, 0x41, 0x1c, 0x60, 0x01, 0xbf, 0xd2, 0x5c, 0x25, 0x23, 0x2e, 0x21, 0x64, 0x1d, + 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x32, 0x5c, 0x9c, 0x89, 0x45, 0xe9, 0xa5, 0xb9, 0xa9, + 0x79, 0x25, 0xc5, 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0x08, 0x01, 0x23, 0x5f, 0x2e, 0x0e, + 0x67, 0x5f, 0xe7, 0x00, 0x90, 0xf5, 0x42, 0x8e, 0x5c, 0x5c, 0x08, 0xfd, 0x42, 0x12, 0x10, 0xe7, + 0xe8, 0x61, 0x38, 0x42, 0x4a, 0x12, 0x8b, 0x0c, 0xc4, 0xb2, 0x24, 0x36, 0xb0, 0x8c, 0x31, 0x20, + 0x00, 0x00, 0xff, 0xff, 0x09, 0x00, 0x2b, 0x54, 0xd1, 0x00, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// CMCProxyClient is the client API for CMCProxy service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type CMCProxyClient interface { + GetKVMData(ctx context.Context, in *GetKVMDataRequest, opts ...grpc.CallOption) (*GetKVMDataResponse, error) +} + +type cMCProxyClient struct { + cc *grpc.ClientConn +} + +func NewCMCProxyClient(cc *grpc.ClientConn) CMCProxyClient { + return &cMCProxyClient{cc} +} + +func (c *cMCProxyClient) GetKVMData(ctx context.Context, in *GetKVMDataRequest, opts ...grpc.CallOption) (*GetKVMDataResponse, error) { + out := new(GetKVMDataResponse) + err := c.cc.Invoke(ctx, "/proto.CMCProxy/GetKVMData", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CMCProxyServer is the server API for CMCProxy service. +type CMCProxyServer interface { + GetKVMData(context.Context, *GetKVMDataRequest) (*GetKVMDataResponse, error) +} + +func RegisterCMCProxyServer(s *grpc.Server, srv CMCProxyServer) { + s.RegisterService(&_CMCProxy_serviceDesc, srv) +} + +func _CMCProxy_GetKVMData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetKVMDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CMCProxyServer).GetKVMData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.CMCProxy/GetKVMData", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CMCProxyServer).GetKVMData(ctx, req.(*GetKVMDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _CMCProxy_serviceDesc = grpc.ServiceDesc{ + ServiceName: "proto.CMCProxy", + HandlerType: (*CMCProxyServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetKVMData", + Handler: _CMCProxy_GetKVMData_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proxy.proto", +} diff --git a/proxy.proto b/proxy.proto new file mode 100644 index 0000000..f34b905 --- /dev/null +++ b/proxy.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package proto; + +message GetKVMDataRequest { + int64 blade_num = 1; +} + +message GetKVMDataResponse { + repeated string arguments = 1; +} + +service CMCProxy { + rpc GetKVMData(GetKVMDataRequest) returns (GetKVMDataResponse); +}