├── agent ├── README.md ├── plugins_suite_test.go ├── plugin_host_test.go ├── plugin_process_test.go ├── grpc │ ├── plugin │ │ ├── plugin.proto │ │ └── plugin.pb.go │ └── host │ │ ├── host.proto │ │ └── host.pb.go ├── Makefile ├── plugin.go ├── plugin_test.go ├── plugin_host.go └── plugin_process.go ├── plugins ├── lldp │ ├── README.md │ ├── inservice_lldp_suite_test.go │ ├── neighbors │ │ ├── neighbors_suite_test.go │ │ ├── decode.go │ │ ├── neighbors_test.go │ │ └── neighbors.go │ ├── grpc │ │ └── lldp │ │ │ ├── lldp.proto │ │ │ └── lldp.pb.go │ ├── plugin_test.go │ ├── main.go │ ├── Makefile │ └── plugin.go └── catalog-compute │ ├── README.md │ ├── inservice_catalog_compute_suite_test.go │ ├── main.go │ ├── Makefile │ └── plugin.go ├── cmd ├── cli │ ├── README.md │ ├── cmd │ │ ├── plugins.go │ │ ├── lldp.go │ │ ├── catalog.go │ │ ├── version.go │ │ ├── stop.go │ │ ├── start.go │ │ ├── catalog_lshw.go │ │ ├── status.go │ │ ├── lldp_list.go │ │ ├── lldp_interface.go │ │ ├── lldp_neighbors_list.go │ │ ├── lldp_neighbors.go │ │ ├── root.go │ │ └── discover.go │ ├── inservice_cli_suite_test.go │ ├── main.go │ └── Makefile └── ssdp │ └── main.go ├── inservice_agent_suite_test.go ├── uuid └── uuid.go ├── .gitignore ├── glide.yaml ├── inservice.json ├── http.go ├── ssdp.go ├── plugins.go ├── .travis.yml ├── main.go ├── Makefile ├── glide.lock ├── README.md └── LICENSE /agent/README.md: -------------------------------------------------------------------------------- 1 | # Plugins Library 2 | -------------------------------------------------------------------------------- /plugins/lldp/README.md: -------------------------------------------------------------------------------- 1 | # InService LLDP Plugin 2 | -------------------------------------------------------------------------------- /plugins/catalog-compute/README.md: -------------------------------------------------------------------------------- 1 | # InService Catalog Compute Plugin 2 | -------------------------------------------------------------------------------- /cmd/cli/README.md: -------------------------------------------------------------------------------- 1 | # InService CLI 2 | 3 | # Adding Commands 4 | 5 | https://github.com/spf13/cobra#cobra-add 6 | -------------------------------------------------------------------------------- /agent/plugins_suite_test.go: -------------------------------------------------------------------------------- 1 | package plugins_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestPlugins(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Plugins Suite") 13 | } 14 | -------------------------------------------------------------------------------- /cmd/cli/cmd/plugins.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | var pluginsCmd = &cobra.Command{ 6 | Use: "plugins", 7 | Short: "A collection of commands related to Plugin management.", 8 | } 9 | 10 | func init() { 11 | RootCmd.AddCommand(pluginsCmd) 12 | } 13 | -------------------------------------------------------------------------------- /cmd/cli/inservice_cli_suite_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestInserviceCli(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "InserviceCli Suite") 13 | } 14 | -------------------------------------------------------------------------------- /inservice_agent_suite_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestInserviceAgent(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "InServiceAgent Suite") 13 | } 14 | -------------------------------------------------------------------------------- /plugins/lldp/inservice_lldp_suite_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestInserviceLldp(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Inservice LLDP Main Suite") 13 | } 14 | -------------------------------------------------------------------------------- /cmd/cli/cmd/lldp.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // lldpCmd represents the lldp command 8 | var lldpCmd = &cobra.Command{ 9 | Use: "lldp", 10 | Short: "lldp plugin control commands", 11 | } 12 | 13 | func init() { 14 | RootCmd.AddCommand(lldpCmd) 15 | } 16 | -------------------------------------------------------------------------------- /plugins/lldp/neighbors/neighbors_suite_test.go: -------------------------------------------------------------------------------- 1 | package neighbors_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestNeighbors(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Inservice LLDP Neighbors Suite") 13 | } 14 | -------------------------------------------------------------------------------- /agent/plugin_host_test.go: -------------------------------------------------------------------------------- 1 | package plugins_test 2 | 3 | import ( 4 | . "github.com/RackHD/inservice/agent" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("PluginHost", func() { 11 | It("passes", func() { 12 | NewPlugin(nil) 13 | Expect(true).To(BeTrue()) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /cmd/cli/cmd/catalog.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // catalogCmd represents the catalog command 8 | var catalogCmd = &cobra.Command{ 9 | Use: "catalog", 10 | Short: "Catalog Compute plugin control commands", 11 | } 12 | 13 | func init() { 14 | RootCmd.AddCommand(catalogCmd) 15 | } 16 | -------------------------------------------------------------------------------- /agent/plugin_process_test.go: -------------------------------------------------------------------------------- 1 | package plugins_test 2 | 3 | import ( 4 | . "github.com/RackHD/inservice/agent" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("PluginProcess", func() { 11 | It("passes", func() { 12 | NewPlugin(nil) 13 | Expect(true).To(BeTrue()) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /plugins/catalog-compute/inservice_catalog_compute_suite_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestInserviceCatalogCompute(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "InserviceCatalogCompute Suite") 13 | } 14 | -------------------------------------------------------------------------------- /uuid/uuid.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import twinj "github.com/twinj/uuid" 4 | 5 | // GetUUID returns a cached (via file) or new UUID. 6 | func GetUUID(_ string) string { 7 | // TODO: Use _ argument as a file cache with which we re-use the cached uuid 8 | // if present in the file or generate (and cache) a new one. This will ensure 9 | // we don't generate a new uuid on each startup. 10 | return twinj.NewV4().String() 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | bin 27 | vendor 28 | 29 | *.coverprofile 30 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/RackHD/inservice 2 | import: 3 | - package: github.com/golang/protobuf 4 | subpackages: 5 | - proto 6 | - package: github.com/google/gopacket 7 | subpackages: 8 | - layers 9 | - pcap 10 | - package: github.com/king-jam/gossdp 11 | - package: github.com/spf13/cobra 12 | - package: github.com/spf13/viper 13 | - package: github.com/twinj/uuid 14 | - package: golang.org/x/net 15 | subpackages: 16 | - context 17 | - package: google.golang.org/grpc 18 | -------------------------------------------------------------------------------- /agent/grpc/plugin/plugin.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package plugin; 4 | 5 | service Plugin { 6 | rpc Start(StartRequest) returns (StartResponse) {} 7 | rpc Stop(StopRequest) returns (StopResponse) {} 8 | rpc Status(StatusRequest) returns (StatusResponse) {} 9 | } 10 | 11 | message StartRequest { 12 | } 13 | 14 | message StartResponse { 15 | } 16 | 17 | message StopRequest { 18 | } 19 | 20 | message StopResponse { 21 | } 22 | 23 | message StatusRequest { 24 | } 25 | 26 | message StatusResponse { 27 | } 28 | -------------------------------------------------------------------------------- /agent/grpc/host/host.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package host; 4 | 5 | service Host { 6 | rpc Start(StartRequest) returns (StartResponse) {} 7 | rpc Stop(StopRequest) returns (StopResponse) {} 8 | rpc Status(StatusRequest) returns (stream StatusResponse) {} 9 | } 10 | 11 | message StartRequest { 12 | string name = 1; 13 | } 14 | 15 | message StartResponse { 16 | } 17 | 18 | message StopRequest { 19 | string name = 1; 20 | } 21 | 22 | message StopResponse { 23 | } 24 | 25 | message StatusRequest { 26 | } 27 | 28 | message StatusResponse { 29 | string name = 1; 30 | string status = 2; 31 | } 32 | -------------------------------------------------------------------------------- /inservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "grpc": { 4 | "address": "127.0.0.1", 5 | "port": 8080 6 | }, 7 | "http": { 8 | "address": "127.0.0.1", 9 | "port": 8081, 10 | "uri": "inservice" 11 | }, 12 | "ssdp": { 13 | "serviceType": "urn:skunkworxs:inservice:agent:0", 14 | "maxAge": 3600, 15 | "cacheFile": ".inservice-agent.uuid" 16 | } 17 | }, 18 | "plugins": { 19 | "inservice-lldp": { 20 | "port": 8082, 21 | "interfaces": ["eth0"] 22 | }, 23 | "inservice-catalog-compute": { 24 | "port": 8084 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // HTTPServer serves the InService Agent configuration file as part of the SSDP 9 | // advertisement process for consumers to identify the configuration of the Agent. 10 | type HTTPServer struct { 11 | Address string 12 | Port int 13 | ConfigFile string 14 | URI string 15 | } 16 | 17 | // Serve runs the HTTPServer in blocking mode. 18 | func (s *HTTPServer) Serve() { 19 | http.HandleFunc(fmt.Sprintf("/%s", s.URI), s.handler) 20 | http.ListenAndServe(fmt.Sprintf("%s:%d", s.Address, s.Port), nil) 21 | } 22 | 23 | func (s *HTTPServer) handler(w http.ResponseWriter, r *http.Request) { 24 | http.ServeFile(w, r, s.ConfigFile) 25 | } 26 | -------------------------------------------------------------------------------- /cmd/cli/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 NAME HERE 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import "github.com/RackHD/inservice/cmd/cli/cmd" 18 | 19 | func main() { 20 | cmd.Execute() 21 | } 22 | -------------------------------------------------------------------------------- /plugins/lldp/grpc/lldp/lldp.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package lldp; 4 | 5 | service Lldp { 6 | rpc ListInterfaces(EmptyMessage) returns (stream Interface) {} 7 | rpc GetInterfaceDetails(Interface) returns (Interface) {} 8 | rpc ListInterfaceNeighbors(Interface) returns (stream Neighbor) {} 9 | rpc ListNeighbors(EmptyMessage) returns (stream Neighbor) {} 10 | } 11 | 12 | message EmptyMessage{ 13 | } 14 | 15 | message Neighbor { 16 | string portid = 1; 17 | string portdescription = 2; 18 | string sysname = 3; 19 | string sysdesc = 4; 20 | string address = 5; 21 | string vlan = 6; 22 | string type = 7; 23 | } 24 | 25 | message Interface { 26 | int32 index = 1; 27 | int32 mtu = 2; 28 | string name = 3; 29 | string hardwareaddr = 4; 30 | uint32 flags = 5; 31 | } 32 | -------------------------------------------------------------------------------- /cmd/cli/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var binaryName, buildDate, buildUser, commitHash, goVersion, osArch, releaseVersion string 10 | 11 | // versioinCmd represents the version command 12 | var versionCmd = &cobra.Command{ 13 | Use: "version", 14 | Short: "Prints metadata version information about this CLI tool.", 15 | Long: `inservice-cli version`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | log.Println(binaryName) 18 | log.Println(" Release version: " + releaseVersion) 19 | log.Println(" Built On: " + buildDate) 20 | log.Println(" Build By: " + buildUser) 21 | log.Println(" Commit Hash: " + commitHash) 22 | log.Println(" Go version: " + goVersion) 23 | log.Println(" OS/Arch: " + osArch) 24 | }, 25 | } 26 | 27 | func init() { 28 | RootCmd.AddCommand(versionCmd) 29 | } 30 | -------------------------------------------------------------------------------- /ssdp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/king-jam/gossdp" 7 | ) 8 | 9 | // SSDPServer provides SSDP advertisment for the InService Agent and it's plugins. 10 | type SSDPServer struct { 11 | ServiceType string 12 | DeviceUUID string 13 | Location string 14 | MaxAge int 15 | 16 | ssdp *gossdp.Ssdp 17 | } 18 | 19 | // Serve runs the SSDPServer in blocking mode. 20 | func (s *SSDPServer) Serve() { 21 | var err error 22 | 23 | s.ssdp, err = gossdp.NewSsdp(nil) 24 | if err != nil { 25 | log.Fatalf("Error creating SSDP Server: %s\n", err) 26 | } 27 | 28 | defer s.ssdp.Stop() 29 | 30 | definition := gossdp.AdvertisableServer{ 31 | ServiceType: s.ServiceType, 32 | DeviceUuid: s.DeviceUUID, 33 | Location: s.Location, 34 | MaxAge: s.MaxAge, 35 | } 36 | 37 | s.ssdp.AdvertiseServer(definition) 38 | s.ssdp.Start() 39 | } 40 | -------------------------------------------------------------------------------- /plugins.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/RackHD/inservice/agent" 7 | ) 8 | 9 | // PluginServer provides PluginHost configuration and management. 10 | type PluginServer struct { 11 | plugins []string 12 | 13 | address string 14 | port int 15 | 16 | host *plugins.PluginHost 17 | } 18 | 19 | // NewPluginServer returns a new PluginServer instance. 20 | func NewPluginServer(address string, port int, plugins []string) (*PluginServer, error) { 21 | return &PluginServer{ 22 | address: address, 23 | port: port, 24 | plugins: plugins, 25 | }, nil 26 | } 27 | 28 | // Serve runs the PluginServer in blocking mode. 29 | func (s *PluginServer) Serve() { 30 | var err error 31 | 32 | s.host, err = plugins.NewPluginHost(s.address, s.port, s.plugins) 33 | if err != nil { 34 | log.Fatalf("Error creating Plugin Host: %s\n", err) 35 | } 36 | 37 | s.host.Serve() 38 | } 39 | -------------------------------------------------------------------------------- /cmd/ssdp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/king-jam/gossdp" 9 | ) 10 | 11 | type ssdpHandler struct{} 12 | 13 | // Response is the callback to process inbound SSDP messages. 14 | func (h *ssdpHandler) Response(message gossdp.ResponseMessage) { 15 | fmt.Printf("%+v\n", message) 16 | } 17 | 18 | func main() { 19 | handler := ssdpHandler{} 20 | 21 | client, err := gossdp.NewSsdpClient(&handler) 22 | if err != nil { 23 | log.Println("Failed to start client: ", err) 24 | return 25 | } 26 | // call stop when we are done 27 | defer client.Stop() 28 | // run! this will block until stop is called. so open it in a goroutine here 29 | go client.Start() 30 | // send a request for the server type we are listening for. 31 | err = client.ListenFor("urn:skunkworxs:inservice:agent:0") 32 | if err != nil { 33 | log.Println("Error ", err) 34 | } 35 | 36 | time.Sleep(60 * time.Second) 37 | } 38 | -------------------------------------------------------------------------------- /agent/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Test Arguments 3 | test_args = -cover -race -trace -randomizeAllSpecs 4 | 5 | # Linter Arguments 6 | # dupl linter appears to identify errors inaccurately. 7 | lint_args = --vendor --fast --disable=dupl --disable=gas --skip=grpc ./... 8 | 9 | default: test 10 | 11 | deps: 12 | go get -u github.com/onsi/ginkgo/ginkgo 13 | go get -u github.com/onsi/gomega 14 | go get -u github.com/alecthomas/gometalinter 15 | gometalinter --install --update 16 | go get ./... 17 | 18 | build: lint 19 | go build *.go 20 | 21 | lint: 22 | gometalinter $(lint_args) 23 | 24 | test: lint 25 | ginkgo $(test_args) 26 | 27 | cover: test 28 | go tool cover -html=plugins.coverprofile 29 | 30 | watch: 31 | ginkgo watch $(test_args) 32 | 33 | grpc: 34 | rm -f ./grpc/plugin/plugin.pb.go 35 | protoc -I ./grpc/plugin ./grpc/plugin/plugin.proto --go_out=plugins=grpc:grpc/plugin 36 | rm -f ./grpc/host/host.pb.go 37 | protoc -I ./grpc/host ./grpc/host/host.proto --go_out=plugins=grpc:grpc/host 38 | 39 | .PHONY: grpc 40 | -------------------------------------------------------------------------------- /cmd/cli/cmd/stop.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | 10 | "github.com/RackHD/inservice/agent/grpc/host" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // stopCmd represents the stop command 15 | var stopCmd = &cobra.Command{ 16 | Use: "stop", 17 | Short: "Stops the specified Plugin.", 18 | Long: `inservice-cli plugins stop `, 19 | RunE: func(cmd *cobra.Command, args []string) error { 20 | if len(args) != 1 { 21 | return fmt.Errorf("Plugin Name Required") 22 | } 23 | 24 | conn, err := grpc.Dial( 25 | fmt.Sprintf("%s:%d", AgentHost, AgentPort), 26 | grpc.WithInsecure(), 27 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 28 | ) 29 | if err != nil { 30 | return err 31 | } 32 | defer conn.Close() 33 | 34 | client := host.NewHostClient(conn) 35 | 36 | _, err = client.Stop( 37 | context.Background(), 38 | &host.StopRequest{ 39 | Name: args[0], 40 | }, 41 | ) 42 | 43 | return err 44 | }, 45 | } 46 | 47 | func init() { 48 | pluginsCmd.AddCommand(stopCmd) 49 | } 50 | -------------------------------------------------------------------------------- /cmd/cli/cmd/start.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/RackHD/inservice/agent/grpc/host" 8 | "github.com/spf13/cobra" 9 | "golang.org/x/net/context" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | // startCmd represents the start command 14 | var startCmd = &cobra.Command{ 15 | Use: "start", 16 | Short: "Starts the specified Plugin.", 17 | Long: `inservice-cli plugins start `, 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | if len(args) != 1 { 20 | return fmt.Errorf("Plugin Name Required") 21 | } 22 | 23 | conn, err := grpc.Dial( 24 | fmt.Sprintf("%s:%d", AgentHost, AgentPort), 25 | grpc.WithInsecure(), 26 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 27 | ) 28 | if err != nil { 29 | return err 30 | } 31 | defer conn.Close() 32 | 33 | client := host.NewHostClient(conn) 34 | 35 | _, err = client.Start( 36 | context.Background(), 37 | &host.StartRequest{ 38 | Name: args[0], 39 | }, 40 | ) 41 | 42 | return err 43 | }, 44 | } 45 | 46 | func init() { 47 | pluginsCmd.AddCommand(startCmd) 48 | } 49 | -------------------------------------------------------------------------------- /cmd/cli/cmd/catalog_lshw.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | "github.com/spf13/viper" 7 | "io/ioutil" 8 | "net/http" 9 | ) 10 | 11 | // lshwCmd represents the lshw command 12 | var lshwCmd = &cobra.Command{ 13 | Use: "lshw", 14 | Short: "Retrieves the HW catalog from the Catalog Compute plugin.", 15 | Long: `inservice-cli catalog lshw`, 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | err := loadViperConfig() 18 | if err != nil { 19 | return fmt.Errorf("Could not load viper config: %s", err) 20 | } 21 | catalogPort := viper.GetInt("plugins.inservice-catalog-compute.port") 22 | 23 | resp, err := http.Get(fmt.Sprintf("http://%s:%d/lshw", AgentHost, catalogPort)) 24 | 25 | if err != nil { 26 | return fmt.Errorf("Unable to get HW Catalog: %s", err) 27 | } 28 | defer resp.Body.Close() 29 | 30 | hardware, err := ioutil.ReadAll(resp.Body) 31 | if err != nil { 32 | return fmt.Errorf("Unable to read HW Catalog: %s", err) 33 | } 34 | 35 | fmt.Printf("%s", hardware) 36 | return err 37 | }, 38 | } 39 | 40 | func init() { 41 | catalogCmd.AddCommand(lshwCmd) 42 | } 43 | -------------------------------------------------------------------------------- /plugins/lldp/plugin_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/RackHD/inservice/plugins/lldp" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Main", func() { 11 | Describe("NewLLDPPlugin", func() { 12 | It("should check IP address input variables", func() { 13 | ifaces := []string{"eth0", "eth1"} 14 | _, err := NewLLDPPlugin("0", 8080, ifaces) 15 | Expect(err).To(HaveOccurred()) 16 | }) 17 | 18 | It("should check port input variable for out of range", func() { 19 | ifaces := []string{"eth0", "eth1"} 20 | _, err := NewLLDPPlugin("10.10.10.10", 0, ifaces) 21 | Expect(err).To(HaveOccurred()) 22 | }) 23 | 24 | It("should check port input variable for out of range", func() { 25 | ifaces := []string{"eth0", "eth1"} 26 | _, err := NewLLDPPlugin("10.10.10.10", 100000, ifaces) 27 | Expect(err).To(HaveOccurred()) 28 | }) 29 | 30 | It("should return an LLDP Plugin struct", func() { 31 | ifaces := []string{"eth0", "eth1"} 32 | lldp, err := NewLLDPPlugin("10.10.10.10", 8080, ifaces) 33 | Expect(err).To(Succeed()) 34 | Expect(lldp).To(BeAssignableToTypeOf(&LLDPPlugin{})) 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /cmd/cli/cmd/status.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | 11 | "github.com/RackHD/inservice/agent/grpc/host" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | // statusCmd represents the status command 16 | var statusCmd = &cobra.Command{ 17 | Use: "status", 18 | Short: "Lists the status of all Plugins.", 19 | Long: `inservice-cli plugins status`, 20 | RunE: func(cmd *cobra.Command, args []string) error { 21 | conn, err := grpc.Dial( 22 | fmt.Sprintf("%s:%d", AgentHost, AgentPort), 23 | grpc.WithInsecure(), 24 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 25 | ) 26 | if err != nil { 27 | return fmt.Errorf("Unable to connect to host: %s", err) 28 | } 29 | defer conn.Close() 30 | 31 | client := host.NewHostClient(conn) 32 | 33 | stream, err := client.Status(context.Background(), &host.StatusRequest{}) 34 | if err != nil { 35 | return fmt.Errorf("Unable to list status: %s", err) 36 | } 37 | 38 | for { 39 | item, err := stream.Recv() 40 | if err == io.EOF { 41 | return nil 42 | } 43 | 44 | if err != nil { 45 | return fmt.Errorf("Unable to stream status: %s", err) 46 | } 47 | 48 | fmt.Printf("%+v\n", item) 49 | } 50 | }, 51 | } 52 | 53 | func init() { 54 | pluginsCmd.AddCommand(statusCmd) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/cli/Makefile: -------------------------------------------------------------------------------- 1 | # Build Output Directory 2 | GOOUT ?= ./bin 3 | 4 | # Test Arguments 5 | test_args = -cover -race -trace -randomizeAllSpecs 6 | 7 | # variable definitions 8 | SHELL = /bin/bash 9 | PKGPATH = github.com/skunkworxs/inservice-cli 10 | NAME = inservice-cli 11 | COMMITHASH = $(shell git describe --tags --always --dirty) 12 | BUILDDATE = $(shell date -u) 13 | BUILDER = $(shell echo "`git config user.name` <`git config user.email`>") 14 | GOVERSION = $(shell go version) 15 | OSARCH = $(shell uname -sm) 16 | RELEASEVERSION = 0.1 17 | 18 | #Flags to pass to cmd/version.go 19 | LDFLAGS = -ldflags "-X '$(PKGPATH)/cmd.binaryName=$(NAME)'\ 20 | -X '$(PKGPATH)/cmd.buildDate=$(BUILDDATE)'\ 21 | -X '$(PKGPATH)/cmd.buildUser=$(BUILDER)'\ 22 | -X '$(PKGPATH)/cmd.commitHash=$(COMMITHASH)'\ 23 | -X '$(PKGPATH)/cmd.goVersion=$(GOVERSION)'\ 24 | -X '$(PKGPATH)/cmd.osArch=$(OSARCH)'\ 25 | -X '$(PKGPATH)/cmd.releaseVersion=$(RELEASEVERSION)'" 26 | 27 | # Linter Arguments 28 | # dupl linter appears to identify errors inaccurately. 29 | lint_args = --vendor --fast --disable=dupl --disable=gotype --skip=grpc ./... 30 | 31 | default: test 32 | 33 | deps: 34 | go get -u github.com/onsi/ginkgo/ginkgo 35 | go get -u github.com/onsi/gomega 36 | go get -u github.com/alecthomas/gometalinter 37 | gometalinter --install --update 38 | go get ./... 39 | 40 | build: lint 41 | go build -o $(GOOUT)/$(NAME) *.go 42 | 43 | lint: 44 | gometalinter $(lint_args) 45 | 46 | test: lint 47 | ginkgo $(test_args) 48 | 49 | cover: test 50 | go tool cover -html=main.coverprofile 51 | 52 | watch: 53 | ginkgo watch $(test_args) 54 | -------------------------------------------------------------------------------- /plugins/lldp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/RackHD/inservice/agent" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | var binaryName, buildDate, buildUser, commitHash, goVersion, osArch, releaseVersion string 13 | 14 | func main() { 15 | log.Println(binaryName) 16 | log.Println(" Release version: " + releaseVersion) 17 | log.Println(" Built On: " + buildDate) 18 | log.Println(" Build By: " + buildUser) 19 | log.Println(" Commit Hash: " + commitHash) 20 | log.Println(" Go version: " + goVersion) 21 | log.Println(" OS/Arch: " + osArch) 22 | 23 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 24 | if err != nil { 25 | log.Fatalf("InService LLDP Configuration Error: %s\n", err) 26 | } 27 | 28 | viper.SetConfigName("inservice") 29 | viper.SetConfigType("json") 30 | viper.AddConfigPath("/etc/inservice.d") 31 | viper.AddConfigPath(dir) 32 | viper.AddConfigPath("$GOPATH/bin") 33 | viper.AddConfigPath("$HOME") 34 | 35 | err = viper.ReadInConfig() 36 | if err != nil { 37 | log.Fatalf("InService LLDP Configuration Error: %s\n", err) 38 | } 39 | 40 | log.Printf("InService LLDP Configuration: %s\n", viper.ConfigFileUsed()) 41 | 42 | lldp, err := NewLLDPPlugin( 43 | viper.GetString("agent.http.address"), 44 | viper.GetInt("plugins.inservice-lldp.port"), 45 | viper.GetStringSlice("plugins.inservice-lldp.interfaces"), 46 | ) 47 | if err != nil { 48 | log.Fatalf("Unable to initialize Plugin: %s\n", err) 49 | } 50 | 51 | p, err := plugins.NewPlugin(lldp) 52 | if err != nil { 53 | log.Fatalf("Unable to host Plugin: %s\n", err) 54 | } 55 | 56 | p.Serve() 57 | } 58 | -------------------------------------------------------------------------------- /plugins/catalog-compute/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/RackHD/inservice/agent" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | var binaryName, buildDate, buildUser, commitHash, goVersion, osArch, releaseVersion string 13 | 14 | func main() { 15 | log.Println(binaryName) 16 | log.Println(" Release version: " + releaseVersion) 17 | log.Println(" Built On: " + buildDate) 18 | log.Println(" Build By: " + buildUser) 19 | log.Println(" Commit Hash: " + commitHash) 20 | log.Println(" Go version: " + goVersion) 21 | log.Println(" OS/Arch: " + osArch) 22 | 23 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 24 | if err != nil { 25 | log.Fatalf("InService Catalog Compute Configuration Error: %s\n", err) 26 | } 27 | 28 | viper.SetConfigName("inservice") 29 | viper.SetConfigType("json") 30 | viper.AddConfigPath("/etc/inservice.d") 31 | viper.AddConfigPath(dir) 32 | viper.AddConfigPath("$GOPATH/bin") 33 | viper.AddConfigPath("$HOME") 34 | 35 | err = viper.ReadInConfig() 36 | if err != nil { 37 | log.Fatalf("InService Catalog Compute Configuration Error: %s\n", err) 38 | } 39 | 40 | log.Printf("InService Catalog Compute Configuration: %s\n", viper.ConfigFileUsed()) 41 | 42 | catalog, err := NewCatalogComputePlugin( 43 | viper.GetString("agent.http.address"), 44 | viper.GetInt("plugins.inservice-catalog-compute.port"), 45 | ) 46 | if err != nil { 47 | log.Fatalf("Unable to initialize Plugin: %s\n", err) 48 | } 49 | 50 | p, err := plugins.NewPlugin(catalog) 51 | if err != nil { 52 | log.Fatalf("Unable to host Plugin: %s\n", err) 53 | } 54 | 55 | p.Serve() 56 | } 57 | -------------------------------------------------------------------------------- /cmd/cli/cmd/lldp_list.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | "io" 11 | "time" 12 | ) 13 | 14 | // lldpInterfaceListCmd represents the list command 15 | var lldpInterfaceListCmd = &cobra.Command{ 16 | Use: "list", 17 | Short: "Information about LLDP interfaces", 18 | Long: `Displays list of interfaces in use by LLDP Plugin: 19 | Usage: 20 | Interface List - inservice-cli lldp interface list`, 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | err := loadViperConfig() 23 | if err != nil { 24 | return fmt.Errorf("Could not load viper config: %s", err) 25 | } 26 | LLDPPort := viper.GetInt("plugins.inservice-lldp.port") 27 | 28 | conn, err := grpc.Dial( 29 | fmt.Sprintf("%s:%d", AgentHost, LLDPPort), 30 | grpc.WithInsecure(), 31 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 32 | ) 33 | if err != nil { 34 | return fmt.Errorf("Unable to connect to host: %s", err) 35 | } 36 | defer conn.Close() 37 | 38 | client := lldp.NewLldpClient(conn) 39 | 40 | stream, err := client.ListInterfaces(context.Background(), &lldp.EmptyMessage{}) 41 | if err != nil { 42 | return fmt.Errorf("Unable to list interfaces: %s", err) 43 | } 44 | 45 | for { 46 | item, err := stream.Recv() 47 | if err == io.EOF { 48 | return nil 49 | } 50 | if err != nil { 51 | return fmt.Errorf("Unable to stream interfaces: %s", err) 52 | } 53 | fmt.Printf("%+v\n", item) 54 | } 55 | }, 56 | } 57 | 58 | func init() { 59 | interfaceCmd.AddCommand(lldpInterfaceListCmd) 60 | } 61 | -------------------------------------------------------------------------------- /plugins/catalog-compute/Makefile: -------------------------------------------------------------------------------- 1 | # Build Output Directory 2 | GOOUT ?= $(GOPATH)/bin 3 | 4 | # Test Arguments 5 | test_args = -r -cover -race -trace -randomizeAllSpecs 6 | 7 | # variable definitions 8 | SHELL = /bin/bash 9 | NAME = inservice-catalog-compute 10 | COMMITHASH = $(shell git describe --tags --always --dirty) 11 | BUILDDATE = $(shell date -u) 12 | BUILDER = $(shell echo "`git config user.name` <`git config user.email`>") 13 | GOVERSION = $(shell go version) 14 | OSARCH = $(shell uname -sm) 15 | RELEASEVERSION = 0.1 16 | 17 | #Flags to pass to main.go 18 | LDFLAGS = -ldflags "-X 'main.binaryName=$(NAME)'\ 19 | -X 'main.buildDate=$(BUILDDATE)'\ 20 | -X 'main.buildUser=$(BUILDER)'\ 21 | -X 'main.commitHash=$(COMMITHASH)'\ 22 | -X 'main.goVersion=$(GOVERSION)'\ 23 | -X 'main.osArch=$(OSARCH)'\ 24 | -X 'main.releaseVersion=$(RELEASEVERSION)'" 25 | 26 | # Linter Arguments 27 | # dupl linter appears to identify errors inaccurately. 28 | # gas is requiring an absolute path to lshw binary, which will cause 29 | # issues when running on various different systems 30 | lint_args = --vendor --fast --disable=dupl --disable=gas --skip=grpc ./... 31 | 32 | default: test 33 | 34 | deps: 35 | go get -u github.com/onsi/ginkgo/ginkgo 36 | go get -u github.com/onsi/gomega 37 | go get -u github.com/alecthomas/gometalinter 38 | gometalinter --install --update 39 | go get ./... 40 | 41 | build: lint 42 | go build -o $(GOOUT)/$(NAME) $(LDFLAGS) *.go 43 | 44 | run: build 45 | $(GOOUT)/$(NAME) 46 | 47 | lint: 48 | gometalinter $(lint_args) 49 | 50 | test: lint 51 | ginkgo $(test_args) 52 | 53 | cover: test 54 | go tool cover -html=main.coverprofile 55 | 56 | watch: 57 | ginkgo watch $(test_args) 58 | -------------------------------------------------------------------------------- /cmd/cli/cmd/lldp_interface.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | "time" 11 | ) 12 | 13 | // interfaceCmd represents the interface command 14 | var interfaceCmd = &cobra.Command{ 15 | Use: "interface", 16 | Short: "Information about LLDP interfaces", 17 | Long: `Displays list of interfaces in use by LLDP Plugin or details about a 18 | specific interface: 19 | 20 | Usage: 21 | Interface Details - inservice-cli lldp interface 22 | Interface List - inservice-cli lldp interface list`, 23 | RunE: func(cmd *cobra.Command, args []string) error { 24 | err := loadViperConfig() 25 | if err != nil { 26 | return fmt.Errorf("Could not load viper config: %s", err) 27 | } 28 | LLDPPort := viper.GetInt("plugins.inservice-lldp.port") 29 | 30 | if len(args) != 1 { 31 | return fmt.Errorf("Interface Name or [command] Required") 32 | } 33 | 34 | conn, err := grpc.Dial( 35 | fmt.Sprintf("%s:%d", AgentHost, LLDPPort), 36 | grpc.WithInsecure(), 37 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 38 | ) 39 | if err != nil { 40 | return fmt.Errorf("Unable to connect to host: %s", err) 41 | } 42 | defer conn.Close() 43 | 44 | client := lldp.NewLldpClient(conn) 45 | 46 | details, err := client.GetInterfaceDetails( 47 | context.Background(), 48 | &lldp.Interface{ 49 | Name: args[0], 50 | }, 51 | ) 52 | if err != nil { 53 | return fmt.Errorf("Unable to show details: %s", err) 54 | } 55 | fmt.Printf("%+v\n", details) 56 | return nil 57 | }, 58 | } 59 | 60 | func init() { 61 | lldpCmd.AddCommand(interfaceCmd) 62 | } 63 | -------------------------------------------------------------------------------- /plugins/lldp/Makefile: -------------------------------------------------------------------------------- 1 | # Build Output Directory 2 | GOOUT ?= $(GOPATH)/bin 3 | 4 | # Test Arguments 5 | test_args = -r -cover -race -trace -randomizeAllSpecs 6 | 7 | # variable definitions 8 | SHELL = /bin/bash 9 | NAME = inservice-lldp 10 | COMMITHASH = $(shell git describe --tags --always --dirty) 11 | BUILDDATE = $(shell date -u) 12 | BUILDER = $(shell echo "`git config user.name` <`git config user.email`>") 13 | GOVERSION = $(shell go version) 14 | OSARCH = $(shell uname -sm) 15 | RELEASEVERSION = 0.1 16 | 17 | #Flags to pass to main.go 18 | LDFLAGS = -ldflags "-X 'main.binaryName=$(NAME)'\ 19 | -X 'main.buildDate=$(BUILDDATE)'\ 20 | -X 'main.buildUser=$(BUILDER)'\ 21 | -X 'main.commitHash=$(COMMITHASH)'\ 22 | -X 'main.goVersion=$(GOVERSION)'\ 23 | -X 'main.osArch=$(OSARCH)'\ 24 | -X 'main.releaseVersion=$(RELEASEVERSION)'" 25 | 26 | # Linter Arguments 27 | # dupl linter appears to identify errors inaccurately. 28 | lint_args = --vendor --fast --disable=dupl --skip=grpc ./... 29 | 30 | default: test 31 | 32 | deps: 33 | go get -u github.com/onsi/ginkgo/ginkgo 34 | go get -u github.com/onsi/gomega 35 | go get -u github.com/alecthomas/gometalinter 36 | gometalinter --install --update 37 | go get -u github.com/golang/protobuf/{proto,protoc-gen-go} 38 | go get ./... 39 | 40 | build: lint 41 | go build -o $(GOOUT)/$(NAME) $(LDFLAGS) *.go 42 | 43 | run: build 44 | $(GOOUT)/$(NAME) 45 | 46 | lint: 47 | gometalinter $(lint_args) 48 | 49 | test: lint 50 | ginkgo $(test_args) 51 | 52 | cover: test 53 | go tool cover -html=main.coverprofile 54 | 55 | watch: 56 | ginkgo watch $(test_args) 57 | 58 | grpc: deps 59 | rm -f ./grpc/lldp/lldp.pb.go 60 | protoc -I ./grpc/lldp ./grpc/lldp/lldp.proto --go_out=plugins=grpc:grpc/lldp 61 | 62 | .PHONY: grpc 63 | -------------------------------------------------------------------------------- /cmd/cli/cmd/lldp_neighbors_list.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 6 | "golang.org/x/net/context" 7 | "google.golang.org/grpc" 8 | "io" 9 | "time" 10 | 11 | "github.com/spf13/cobra" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | // lldpNeighborsListCmd represents the list command 16 | var lldpNeighborsListCmd = &cobra.Command{ 17 | Use: "list", 18 | Short: "Show information on the LLDP neighbors", 19 | Long: `Displays list of neighbors seen by LLDP Plugin or those seen from specific interface: 20 | Usage: 21 | Neighbor List - inservice-cli lldp neighbors list 22 | Interface Neighbors - inservice-cli lldp neighbors `, 23 | RunE: func(cmd *cobra.Command, args []string) error { 24 | err := loadViperConfig() 25 | if err != nil { 26 | return fmt.Errorf("Could not load viper config: %s", err) 27 | } 28 | LLDPPort := viper.GetInt("plugins.inservice-lldp.port") 29 | 30 | conn, err := grpc.Dial( 31 | fmt.Sprintf("%s:%d", AgentHost, LLDPPort), 32 | grpc.WithInsecure(), 33 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 34 | ) 35 | if err != nil { 36 | return fmt.Errorf("Unable to connect to host: %s", err) 37 | } 38 | defer conn.Close() 39 | 40 | client := lldp.NewLldpClient(conn) 41 | 42 | stream, err := client.ListNeighbors(context.Background(), &lldp.EmptyMessage{}) 43 | if err != nil { 44 | return fmt.Errorf("Unable to list neighbors: %s", err) 45 | } 46 | 47 | for { 48 | item, err := stream.Recv() 49 | if err == io.EOF { 50 | return nil 51 | } 52 | if err != nil { 53 | return fmt.Errorf("Unable to stream neighbors: %s", err) 54 | } 55 | fmt.Printf("%+v\n", item) 56 | } 57 | }, 58 | } 59 | 60 | func init() { 61 | neighborsCmd.AddCommand(lldpNeighborsListCmd) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/cli/cmd/lldp_neighbors.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/viper" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | "io" 11 | "time" 12 | ) 13 | 14 | // neighborsCmd represents the neighbors command 15 | var neighborsCmd = &cobra.Command{ 16 | Use: "neighbors", 17 | Short: "Show information on the LLDP neighbors", 18 | Long: `Displays list of neighbors seen by LLDP Plugin or those seen from specific interface: 19 | Usage: 20 | Neighbor List - inservice-cli lldp neighbors list 21 | Interface Neighbors - inservice-cli lldp neighbors `, 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | err := loadViperConfig() 24 | if err != nil { 25 | return fmt.Errorf("Could not load viper config: %s", err) 26 | } 27 | LLDPPort := viper.GetInt("plugins.inservice-lldp.port") 28 | 29 | if len(args) != 1 { 30 | return fmt.Errorf("Interface Name or [command] Required") 31 | } 32 | 33 | conn, err := grpc.Dial( 34 | fmt.Sprintf("%s:%d", AgentHost, LLDPPort), 35 | grpc.WithInsecure(), 36 | grpc.WithTimeout(time.Duration(AgentTimeout)*time.Second), 37 | ) 38 | if err != nil { 39 | return fmt.Errorf("Unable to connect to host: %s", err) 40 | } 41 | defer conn.Close() 42 | 43 | client := lldp.NewLldpClient(conn) 44 | 45 | stream, err := client.ListInterfaceNeighbors( 46 | context.Background(), 47 | &lldp.Interface{ 48 | Name: args[0], 49 | }, 50 | ) 51 | if err != nil { 52 | return fmt.Errorf("Unable to list neighbors: %s", err) 53 | } 54 | 55 | for { 56 | item, err := stream.Recv() 57 | if err == io.EOF { 58 | return nil 59 | } 60 | if err != nil { 61 | return fmt.Errorf("Unable to stream neighbors: %s", err) 62 | } 63 | fmt.Printf("%+v\n", item) 64 | } 65 | }, 66 | } 67 | 68 | func init() { 69 | lldpCmd.AddCommand(neighborsCmd) 70 | } 71 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | language: go 5 | go: 6 | - 1.7 7 | before_install: 8 | - echo -e "machine github.com\n  login $GITHUB_USER\n  password $GITHUB_PASS" >> ~/.netrc 9 | script: 10 | - make 11 | after_success: 12 | - make coveralls 13 | notifications: 14 | slack: 15 | secure: qc4QO0mvbHY9d1t9lIjpi8wK2mKpV698CGYOMWdEfdv+rj8gbuCPX/BMJ/55YIy+n1V9RSs+4WnWDv/RhcOF2A6vQUDycqBfU8li94s7pMNPu8gsNEyUUDG8Y+ya9ooGjW6gRFwFR7tiF0x45VrkiWXmLnEe/ka7v9Vu3pfCmV6mqF/13Dn9iZJHXvMmBGFiNQFjZfsBjO0rlcEkrYTVo9wBfZHYVbbVWOUYyl2F8GlXmqWCckEQkAPRqu2GijwI+NhyaIfPrW7kuwGforjbLpxgmRgER8F+9w6XGSANlWB0pzOfGAuCPKlGsfQTVsuSaEd2/eqVTcKGZJjy3mjUo3vBpifkoIbBiartInFRqSg+n2zmoF3FAPeCrMnX0YqacNrWrf/YmJuQzIVEhnmQiCGCVB0d9B9vzYpaXqWy8BQaBl4Wnwikmw1aNE5XXGfKUBhKj7dBczNTAu7KmgsAo67Og/CzxR7vGp+COjs3Ns5Ly+ec97slbQcejx0erpTLzzzanrcCe4qMny9FMVYQvxI4myLE3qpRYHWRHzlblkPZG/n8oplKD4vMYNbFDm2rNv9Y7v8naLMCun8Ux7vt7vg+msykuXj9T+kxCzw1F7V9E2RN5utEDGL+notR5a7bwEBPbIFU99ZjLemJZ54KhVXs23Sk8VfJd0cLHRo7j6Q= 16 | env: 17 | global: 18 | - secure: RWDefObXIl3AMKVlXtUDWEDx/j3qHxHAmJ6/t/P2ak2FKx0gtEpyDe6gOY4x+svVOMjVe1k6BfZcfDfZx80UbFirZhDbCj2dU0j8sgxhbnprVZ6nFoM6tcJWu2dGYJVwnaGrNE+m/dA3Zl0fp7HeMi+p4jaVZSi/BL96bSTdygjAQEO9cuqv7w0cA33hBSi8HGSsZ67cEhOFqlEL70alz051L85FFMn2MmKaPPylQ5lm6If/CF14hqW18/Ci0/XBRUp5+yDq4ohzw/+A62Yt/RXJUzg4XkMPbM7hfbPcgEUzurwmv0fn4h5F0vaek2n5GTkS11ArsjDVudE3Iti45FwKAIUNDa3wxIF1wH/qW9VRr10E6y6Nz1RhEB+NV4zpedcUMnHwMOAQ8OP93WkfHuVGV4PZ3g1T1vmQ3+Hf8qUGXp2ljngFqpQF0ksvnXIm9pByFezJbMrsM2ApGvL9yngrAp4rOEQicQmI6YTuSXEoSufTf4ordwbRxyRGb0kZmPZi6cj2cym3UKA/9QzF4GBNSkOftbOP/T68DUI5r64zpDYmqlaB+0CmscS6zNxWKmQdCcfKhhOqobUryE7IK7U8gCWFg+ZGWyRL34CcoqmY/bqsHkbEWsTxSMdjaRktg8BhfDVlvMHqcIOO8OxgOXNRuaXISPlsXUFg0l8wi1E= 19 | - secure: B6erGGsuUOITyNWTr5p57ymQULpgxIDMRfz5m/RgzYrWcQstqrihb5STC/XMv8fsfvj2hAQXtfZ9bj9Jms5XJe4tyCCRW47gpfP/opfAs2zwEMLXumm+2Q+JENvHWpg437Azf/KU4ERsaa8bLdYpZdeOguSoxc6tt+4OsDZfcgD+/2iCV03DWEyirp3XOR8DK3pn2/BDKU7f6PAGW1fGnHOSD5RwSYR7nq2SE/lN/+RT3h6SKmITTGKbnS246wxzOtMDhweft9q23JBTzwjYYKGsq0mdBtpprXYZvcykqbfPBGba2KjBIK7qNloGFGoFGKXvkAPExzPKRjQ+/iiBN/Scii6yryhCkO2mmcNSbKYtxx6WfBGH3aeKxevGWgI1fMRKsrdBQPkxd98ebZcDhsVoiGUJqBm5qcUwPCQPo5ZoYFJ6xxxzbrRh4c2CUY+dUazJDt6K8nfWE2a7ohM55BrOl3D0fpNCxZCghiOnGASnnEeOWiTgfb3zosWSq9DPLlFhp3TP0iLwiM45vdCbcc6LywTrBseIKoPhfR7L8LdRo95apHZjZm+4z/Bu+UgKpeyNeSWq85izcgwLys3X975S0b79UEufLUBfQqHaa2yYzU5P6mMlqTZonk/y++6zj8miPV03r5uUsTmnngJOa1//iQaOXUV8KUNxaJ1ErdA= 20 | -------------------------------------------------------------------------------- /cmd/cli/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | var cfgFile string 13 | 14 | // AgentHost is the remote agent hostname or IP address. 15 | var AgentHost string 16 | 17 | // AgentPort is the remote agent port number. 18 | var AgentPort int 19 | 20 | // AgentTimeout is the timeout used for calls to the remote agent. 21 | var AgentTimeout int 22 | 23 | // RootCmd represents the base command when called without any subcommands 24 | var RootCmd = &cobra.Command{ 25 | Use: "inservice-cli", 26 | Short: "Provides CLI access to the InService Agent and Plugins.", 27 | } 28 | 29 | // Execute adds all child commands to the root command sets flags appropriately. 30 | // This is called by main.main(). It only needs to happen once to the rootCmd. 31 | func Execute() { 32 | if err := RootCmd.Execute(); err != nil { 33 | fmt.Println(err) 34 | os.Exit(-1) 35 | } 36 | } 37 | 38 | func init() { 39 | cobra.OnInitialize(initConfig) 40 | 41 | RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.inservice-cli.yaml)") 42 | 43 | RootCmd.PersistentFlags().StringVarP(&AgentHost, "host", "H", "127.0.0.1", "InService Agent Host Address") 44 | RootCmd.PersistentFlags().IntVarP(&AgentPort, "port", "P", 8080, "InService Agent Host Port") 45 | RootCmd.PersistentFlags().IntVarP(&AgentTimeout, "timeout", "t", 5, "InService Agent Request Timeout (Seconds)") 46 | } 47 | 48 | func initConfig() { 49 | if cfgFile != "" { // enable ability to specify config file via flag 50 | viper.SetConfigFile(cfgFile) 51 | } 52 | 53 | viper.SetConfigName(".inservice-cli") // name of config file (without extension) 54 | viper.AddConfigPath("$HOME") // adding home directory as first search path 55 | viper.AutomaticEnv() // read in environment variables that match 56 | 57 | // If a config file is found, read it in. 58 | if err := viper.ReadInConfig(); err == nil { 59 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 60 | } 61 | } 62 | 63 | func loadViperConfig() error { 64 | 65 | viper.SetConfigType("json") 66 | resp, err := http.Get(fmt.Sprintf("http://%s:%d/inservice", AgentHost, AgentPort+1)) 67 | if err != nil { 68 | return fmt.Errorf("Unable to get Config: %s", err) 69 | } 70 | defer resp.Body.Close() 71 | 72 | viper.ReadConfig(resp.Body) 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /plugins/catalog-compute/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "net/http" 8 | "log" 9 | "os/exec" 10 | 11 | "golang.org/x/net/context" 12 | ) 13 | 14 | // CatalogComputePlugin implements the Plugin interface. 15 | type CatalogComputePlugin struct { 16 | address string 17 | port int 18 | 19 | laddr *net.TCPAddr 20 | listener *net.TCPListener 21 | } 22 | 23 | // NewCatalogComputePlugin initializes a new CatalogComputePlugin struct. 24 | func NewCatalogComputePlugin(address string, port int) (*CatalogComputePlugin, error) { 25 | var err error 26 | 27 | plugin := &CatalogComputePlugin{ 28 | address: address, 29 | port: port, 30 | } 31 | 32 | plugin.laddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", address, port)) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return plugin, nil 38 | } 39 | 40 | // Start handles plugin startup. This creates goroutines to handle the packet capture 41 | // and serve the gRPC API or REST API 42 | func (p *CatalogComputePlugin) Start(ctx context.Context) error { 43 | log.Println("Catalog-Compute Plugin Started.") 44 | if p.listener != nil { 45 | return fmt.Errorf("Plugin already started.") 46 | } 47 | 48 | var err error 49 | 50 | p.listener, err = net.ListenTCP("tcp", p.laddr) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | http.HandleFunc("/lshw", p.HandleLshw) 56 | http.Serve(p.listener, nil) 57 | 58 | return nil 59 | } 60 | 61 | // Stop closes a channel that should stop all capture 62 | func (p *CatalogComputePlugin) Stop(ctx context.Context) error { 63 | if p.listener == nil { 64 | return fmt.Errorf("Plugin not started.") 65 | } 66 | 67 | err := p.listener.Close() 68 | 69 | p.listener = nil 70 | 71 | return err 72 | } 73 | 74 | // Status is a dummy function for now until a better status mechanism is identified 75 | func (p *CatalogComputePlugin) Status(ctx context.Context) error { 76 | if p.listener == nil { 77 | return fmt.Errorf("Plugin not started.") 78 | } 79 | 80 | return nil 81 | } 82 | 83 | // HandleLshw replies to HTTP requests for the lshw JSON catalog. 84 | func (p *CatalogComputePlugin) HandleLshw(w http.ResponseWriter, r *http.Request) { 85 | w.Header().Set("Content-Type", "application/json") 86 | 87 | out, err := exec.Command("lshw", "-json").Output() 88 | if err != nil { 89 | io.WriteString(w, err.Error()) 90 | w.WriteHeader(500) 91 | } else { 92 | io.WriteString(w, string(out)) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/RackHD/inservice/uuid" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | func keys(m map[string]interface{}) []string { 14 | keys := []string{} 15 | for key := range m { 16 | keys = append(keys, key) 17 | } 18 | 19 | return keys 20 | } 21 | 22 | var binaryName, buildDate, buildUser, commitHash, goVersion, osArch, releaseVersion string 23 | 24 | func main() { 25 | log.Println(binaryName) 26 | log.Println(" Release version: " + releaseVersion) 27 | log.Println(" Built On: " + buildDate) 28 | log.Println(" Build By: " + buildUser) 29 | log.Println(" Commit Hash: " + commitHash) 30 | log.Println(" Go version: " + goVersion) 31 | log.Println(" OS/Arch: " + osArch) 32 | 33 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 34 | if err != nil { 35 | log.Fatalf("InService Agent Configuration Error: %s\n", err) 36 | } 37 | 38 | viper.SetConfigName("inservice") 39 | viper.SetConfigType("json") 40 | viper.AddConfigPath("/etc/inservice.d") 41 | viper.AddConfigPath(dir) 42 | viper.AddConfigPath("$GOPATH/bin") 43 | viper.AddConfigPath("$HOME") 44 | 45 | err = viper.ReadInConfig() 46 | if err != nil { 47 | log.Fatalf("InService Agent Configuration Error: %s\n", err) 48 | } 49 | 50 | log.Printf("InService Agent Configuration: %s\n", viper.ConfigFileUsed()) 51 | 52 | // Host configuration end point for SSDP location advertisement. 53 | http := HTTPServer{ 54 | Address: viper.GetString("agent.http.address"), 55 | Port: viper.GetInt("agent.http.port"), 56 | ConfigFile: viper.ConfigFileUsed(), 57 | URI: viper.GetString("agent.http.uri"), 58 | } 59 | 60 | go http.Serve() 61 | 62 | // Host SSDP Server for advertising Agent/Plugin capabilities. 63 | ssdp := SSDPServer{ 64 | ServiceType: viper.GetString("agent.ssdp.serviceType"), 65 | DeviceUUID: uuid.GetUUID(viper.GetString("agent.ssdp.cacheFile")), 66 | Location: fmt.Sprintf( 67 | "http://%s:%d/%s", 68 | viper.GetString("agent.http.address"), 69 | viper.GetInt("agent.http.port"), 70 | viper.GetString("agent.http.uri"), 71 | ), 72 | MaxAge: viper.GetInt("agent.ssdp.maxAge"), 73 | } 74 | 75 | go ssdp.Serve() 76 | 77 | // Host Plugins 78 | plugins, err := NewPluginServer( 79 | viper.GetString("agent.grpc.address"), 80 | viper.GetInt("agent.grpc.port"), 81 | keys(viper.GetStringMap("plugins")), 82 | ) 83 | if err != nil { 84 | log.Fatalf("InService Agent Plugin Server Error: %s\n", err) 85 | } 86 | 87 | plugins.Serve() 88 | } 89 | -------------------------------------------------------------------------------- /cmd/cli/cmd/discover.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 NAME HERE 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | 21 | "github.com/king-jam/gossdp" 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | type ssdpHandler struct{} 26 | 27 | // Response is the callback to process inbound SSDP messages. 28 | func (h *ssdpHandler) Response(message gossdp.ResponseMessage) { 29 | fmt.Printf("%+v\n", message) 30 | } 31 | 32 | // discoverCmd represents the discover command 33 | var discoverCmd = &cobra.Command{ 34 | Use: "discover", 35 | Short: "A brief description of your command", 36 | Long: `A longer description that spans multiple lines and likely contains examples 37 | and usage of using your command. For example: 38 | 39 | Cobra is a CLI library for Go that empowers applications. 40 | This application is a tool to generate the needed files 41 | to quickly create a Cobra application.`, 42 | Run: func(cmd *cobra.Command, args []string) { 43 | handler := ssdpHandler{} 44 | 45 | client, err := gossdp.NewSsdpClient(&handler) 46 | if err != nil { 47 | log.Println("Failed to start client: ", err) 48 | return 49 | } 50 | // call stop when we are done 51 | defer client.Stop() 52 | // run! this will block until stop is called. so open it in a goroutine here 53 | go client.Start() 54 | // send a request for the server type we are listening for. 55 | err = client.ListenFor("urn:skunkworxs:inservice:agent:0") 56 | if err != nil { 57 | log.Println("Error ", err) 58 | } 59 | 60 | fmt.Println("Press a key to exit discovery...") 61 | 62 | var input string 63 | 64 | fmt.Scanln(&input) 65 | }, 66 | } 67 | 68 | func init() { 69 | RootCmd.AddCommand(discoverCmd) 70 | 71 | // Here you will define your flags and configuration settings. 72 | 73 | // Cobra supports Persistent Flags which will work for this command 74 | // and all subcommands, e.g.: 75 | // discoverCmd.PersistentFlags().String("foo", "", "A help for foo") 76 | 77 | // Cobra supports local flags which will only run when this command 78 | // is called directly, e.g.: 79 | // discoverCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 80 | 81 | } 82 | -------------------------------------------------------------------------------- /agent/plugin.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/RackHD/inservice/agent/grpc/plugin" 11 | 12 | "golang.org/x/net/context" 13 | 14 | "google.golang.org/grpc" 15 | ) 16 | 17 | // This is a helper to normalize the plugin process name for use in generating 18 | // a unix socket file name. 19 | func executableName() string { 20 | _, file := filepath.Split(os.Args[0]) 21 | return file 22 | } 23 | 24 | // pluginSockFormat provides a format string for the Plugin unix socket path. 25 | const pluginSockFormat string = "/tmp/%s.sock" 26 | 27 | // Service is an interface plugins need to implement in order 28 | // to interact with the PluginHost. 29 | type Service interface { 30 | Start(context.Context) error 31 | Stop(context.Context) error 32 | Status(context.Context) error 33 | } 34 | 35 | // Plugin is the main structure for a Plugin hosted in an executable. 36 | type Plugin struct { 37 | Service Service 38 | } 39 | 40 | // NewPlugin returns a new Plugin object. 41 | func NewPlugin(service Service) (*Plugin, error) { 42 | if service == nil { 43 | return nil, fmt.Errorf("Service Interface Required.") 44 | } 45 | 46 | return &Plugin{ 47 | Service: service, 48 | }, nil 49 | } 50 | 51 | // Serve should be called by the Plugin executable and will block allowing the plugin 52 | // to execute via the Service interface. 53 | func (p *Plugin) Serve() { 54 | sock := fmt.Sprintf(pluginSockFormat, executableName()) 55 | 56 | if err := os.Remove(sock); err != nil { 57 | log.Printf("Unable to remove sock %s: %s", sock, err) 58 | } 59 | 60 | listen, err := net.Listen("unix", sock) 61 | if err != nil { 62 | log.Fatalf("Failed to Listen: %v\n", err) 63 | } 64 | 65 | server := grpc.NewServer() 66 | 67 | plugin.RegisterPluginServer(server, p) 68 | 69 | server.Serve(listen) 70 | } 71 | 72 | // Start implements the gRPC Start call and proxies it to the Service interface. 73 | func (p *Plugin) Start(ctx context.Context, in *plugin.StartRequest) (*plugin.StartResponse, error) { 74 | err := p.Service.Start(ctx) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | return &plugin.StartResponse{}, nil 80 | } 81 | 82 | // Stop implements the gRPC Stop call and proxies it to the Service interface. 83 | func (p *Plugin) Stop(ctx context.Context, in *plugin.StopRequest) (*plugin.StopResponse, error) { 84 | err := p.Service.Stop(ctx) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | return &plugin.StopResponse{}, nil 90 | } 91 | 92 | // Status implements the gRCP Status call and proxies it to the Service interface. 93 | func (p *Plugin) Status(ctx context.Context, in *plugin.StatusRequest) (*plugin.StatusResponse, error) { 94 | err := p.Service.Status(ctx) 95 | if err != nil { 96 | return nil, err 97 | } 98 | 99 | return &plugin.StatusResponse{}, nil 100 | } 101 | -------------------------------------------------------------------------------- /plugins/lldp/neighbors/decode.go: -------------------------------------------------------------------------------- 1 | package neighbors 2 | 3 | import ( 4 | "errors" 5 | "github.com/google/gopacket/layers" 6 | "net" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func (s *Neighbors) translatePacket(input *Packet) (*layers.LinkLayerDiscovery, *layers.LinkLayerDiscoveryInfo, error) { 12 | d := input.Packet.Layer(layers.LayerTypeLinkLayerDiscovery) 13 | if d == nil { 14 | return &layers.LinkLayerDiscovery{}, &layers.LinkLayerDiscoveryInfo{}, errors.New("LLDP: Failed to translate packet") 15 | } 16 | i := input.Packet.Layer(layers.LayerTypeLinkLayerDiscoveryInfo) 17 | if i == nil { 18 | return &layers.LinkLayerDiscovery{}, &layers.LinkLayerDiscoveryInfo{}, errors.New("LLDP: Failed to translate packet") 19 | } 20 | return d.(*layers.LinkLayerDiscovery), i.(*layers.LinkLayerDiscoveryInfo), nil 21 | } 22 | 23 | func (s *Neighbors) decodeChassis(discovery *layers.LinkLayerDiscovery) string { 24 | subType := discovery.ChassisID.Subtype 25 | switch subType { 26 | case 4: 27 | return net.HardwareAddr(discovery.ChassisID.ID).String() 28 | default: 29 | return "" 30 | } 31 | } 32 | 33 | func (s *Neighbors) decodeMgmtAddress(info *layers.LinkLayerDiscoveryInfo) string { 34 | subType := info.MgmtAddress.Subtype 35 | switch subType { 36 | case 1: 37 | return net.IP(info.MgmtAddress.Address).String() 38 | default: 39 | return "" 40 | } 41 | } 42 | 43 | func (s *Neighbors) decodePortID(discovery *layers.LinkLayerDiscovery) string { 44 | subType := discovery.PortID.Subtype 45 | switch subType { 46 | case 1, 2, 5, 7: 47 | return string(discovery.PortID.ID) 48 | case 3: 49 | return net.HardwareAddr(discovery.PortID.ID).String() 50 | case 4: 51 | return net.IP(discovery.PortID.ID).String() 52 | default: 53 | return "" 54 | } 55 | } 56 | 57 | func (s *Neighbors) decodePortDescription(info *layers.LinkLayerDiscoveryInfo) string { 58 | return info.PortDescription 59 | } 60 | 61 | func (s *Neighbors) decodeVlan(info *layers.LinkLayerDiscoveryInfo) string { 62 | info8021, err := info.Decode8021() 63 | if err != nil { 64 | return "" 65 | } 66 | return strconv.Itoa(int(info8021.PVID)) 67 | } 68 | 69 | func (s *Neighbors) determineSysType(discovery *layers.LinkLayerDiscovery, info *layers.LinkLayerDiscoveryInfo) string { 70 | if s.isVMwareOui(s.decodeChassis(discovery)) { 71 | return "vm" 72 | } 73 | if discovery.PortID.Subtype == 7 { 74 | return "nic" 75 | } 76 | if info.SysCapabilities.EnabledCap.Router { 77 | return "router" 78 | } 79 | if info.SysCapabilities.EnabledCap.Bridge { 80 | return "switch" 81 | } 82 | return "unknown" 83 | } 84 | 85 | var vmwareoui = [...]string{"00:1C:14", "00:0C:29", "00:50:56", "00:05:69"} 86 | 87 | func (s *Neighbors) isVMwareOui(mac string) bool { 88 | var oui string 89 | if len(mac) > 0 { 90 | oui = strings.ToUpper(mac[:8]) 91 | } else { 92 | return false 93 | } 94 | for _, addr := range vmwareoui { 95 | if oui == addr { 96 | return true 97 | } 98 | } 99 | return false 100 | } 101 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ORGANIZATION = RackHD 2 | PROJECT = inservice 3 | 4 | GOOUT = bin 5 | 6 | SHELL = /bin/bash 7 | 8 | TTY = $(shell if [ -t 0 ]; then echo "-ti"; fi) 9 | 10 | DOCKER_DIR = /go/src/github.com/${ORGANIZATION}/${PROJECT} 11 | DOCKER_IMAGE = rackhd/golang:1.7.1-wheezy 12 | DOCKER_CMD = docker run --rm -v ${PWD}:${DOCKER_DIR} ${TTY} -w ${DOCKER_DIR} ${DOCKER_IMAGE} 13 | 14 | 15 | # variable definitions 16 | COMMITHASH = $(shell git describe --tags --always --dirty) 17 | BUILDDATE = $(shell date -u) 18 | BUILDER = $(shell echo "`git config user.name` <`git config user.email`>") 19 | GOVERSION = $(shell go version) 20 | OSARCH = $(shell uname -sm) 21 | RELEASEVERSION = 0.1 22 | 23 | LINT_ARGS = --vendor --fast --disable=dupl --disable=gotype --disable=gas --disable=gotype --skip=grpc ./... 24 | 25 | 26 | #Flags to pass to main.go 27 | LDFLAGS = -ldflags "-X 'main.binaryName=${APPLICATION}' \ 28 | -X 'main.buildDate=${BUILDDATE}' \ 29 | -X 'main.buildUser=${BUILDER}' \ 30 | -X 'main.commitHash=${COMMITHASH}' \ 31 | -X 'main.goVersion=${GOVERSION}' \ 32 | -X 'main.osArch=${OSARCH}' \ 33 | -X 'main.releaseVersion=${RELEASEVERSION}' " 34 | 35 | 36 | #Some tests need to run for 5+ seconds, which trips Ginkgo Slow Test warning 37 | SLOWTEST = 10 38 | 39 | .PHONY: shell deps deps-local grpc grpc-local build build-local lint lint-local test test-local release 40 | 41 | default: deps grpc test build 42 | 43 | coveralls: 44 | @go get github.com/mattn/goveralls 45 | @go get github.com/modocache/gover 46 | @go get golang.org/x/tools/cmd/cover 47 | @gover 48 | @goveralls -coverprofile=gover.coverprofile -service=travis-ci 49 | 50 | shell: 51 | @${DOCKER_CMD} /bin/bash 52 | 53 | clean: 54 | @${DOCKER_CMD} make clean-local 55 | 56 | clean-local: 57 | @rm -rf bin vendor 58 | 59 | deps: 60 | @${DOCKER_CMD} make deps-local 61 | 62 | deps-local: 63 | @if ! [ -f glide.lock ]; then glide init --non-interactive; fi 64 | @glide install 65 | 66 | build: 67 | @${DOCKER_CMD} make build-local 68 | 69 | build-local: lint-local 70 | @go build -o $(GOOUT)/inservice-agent $(LDFLAGS) *.go 71 | @go build -o $(GOOUT)/inservice-lldp $(LDFLAGS) plugins/lldp/*.go 72 | @go build -o $(GOOUT)/inservice-catalog-compute $(LDFLAGS) plugins/catalog-compute/*.go 73 | @go build -o $(GOOUT)/inservice-cli $(LDFLAGS) cmd/cli/*.go 74 | @cp ./inservice.json $(GOOUT)/inservice.json 75 | 76 | 77 | lint: 78 | @${DOCKER_CMD} make lint-local 79 | 80 | lint-local: 81 | @gometalinter ${LINT_ARGS} 82 | 83 | test: 84 | @${DOCKER_CMD} make test-local 85 | 86 | test-local: lint-local 87 | @ginkgo -r -race -trace -cover -randomizeAllSpecs --slowSpecThreshold=${SLOWTEST} 88 | 89 | grpc: 90 | @${DOCKER_CMD} make grpc-local 91 | 92 | grpc-local: 93 | #Agent's GRPC 94 | rm -f ./agent/grpc/plugin/plugin.pb.go 95 | protoc -I ./agent/grpc/plugin ./agent/grpc/plugin/plugin.proto --go_out=plugins=grpc:agent/grpc/plugin 96 | rm -f ./agent/grpc/host/host.pb.go 97 | protoc -I ./agent/grpc/host ./agent/grpc/host/host.proto --go_out=plugins=grpc:agent/grpc/host 98 | 99 | #LLDP's GRPC 100 | rm -f ./plugins/lldp/grpc/lldp/lldp.pb.go 101 | protoc -I ./plugins/lldp/grpc/lldp ./plugins/lldp/grpc/lldp/lldp.proto --go_out=plugins=grpc:plugins/lldp/grpc/lldp 102 | 103 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 3c6851290e497feed1ea8aa6ec752d26c9c722378dcb3e49f273e0be2682b5f8 2 | updated: 2016-09-13T08:48:31.607584111-04:00 3 | imports: 4 | - name: github.com/fsnotify/fsnotify 5 | version: f12c6236fe7b5cf6bcf30e5935d08cb079d78334 6 | - name: github.com/golang/protobuf 7 | version: 1f49d83d9aa00e6ce4fc8258c71cc7786aec968a 8 | subpackages: 9 | - proto 10 | - name: github.com/google/gopacket 11 | version: 632566d585d0456397d2058bd9cb800c48e3230b 12 | subpackages: 13 | - layers 14 | - pcap 15 | - name: github.com/hashicorp/hcl 16 | version: 99df0eb941dd8ddbc83d3f3605a34f6a686ac85e 17 | subpackages: 18 | - hcl/ast 19 | - hcl/parser 20 | - hcl/token 21 | - json/parser 22 | - hcl/scanner 23 | - hcl/strconv 24 | - json/scanner 25 | - json/token 26 | - name: github.com/inconshreveable/mousetrap 27 | version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 28 | - name: github.com/king-jam/gossdp 29 | version: 83dbf4057d732d2a11fc27754f969eea50f7b132 30 | - name: github.com/kr/fs 31 | version: 2788f0dbd16903de03cb8186e5c7d97b69ad387b 32 | - name: github.com/magiconair/properties 33 | version: 0723e352fa358f9322c938cc2dadda874e9151a9 34 | - name: github.com/mitchellh/mapstructure 35 | version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1 36 | - name: github.com/pelletier/go-buffruneio 37 | version: df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d 38 | - name: github.com/pelletier/go-toml 39 | version: 31055c2ff0bb0c7f9095aec0d220aed21108121e 40 | - name: github.com/pkg/errors 41 | version: 17b591df37844cde689f4d5813e5cea0927d8dd2 42 | - name: github.com/pkg/sftp 43 | version: 8197a2e580736b78d704be0fc47b2324c0591a32 44 | - name: github.com/spf13/afero 45 | version: 20500e2abd0d1f4564a499e83d11d6c73cd58c27 46 | subpackages: 47 | - mem 48 | - sftp 49 | - name: github.com/spf13/cast 50 | version: e31f36ffc91a2ba9ddb72a4b6a607ff9b3d3cb63 51 | - name: github.com/spf13/cobra 52 | version: 9c28e4bbd74e5c3ed7aacbc552b2cab7cfdfe744 53 | - name: github.com/spf13/jwalterweatherman 54 | version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 55 | - name: github.com/spf13/pflag 56 | version: 7b17cc4658ef5ca157b986ea5c0b43af7938532b 57 | - name: github.com/spf13/viper 58 | version: 16990631d4aa7e38f73dbbbf37fa13e67c648531 59 | - name: github.com/twinj/uuid 60 | version: 7bbe408d339787c56ec15568c947c0959db1b275 61 | - name: golang.org/x/crypto 62 | version: e311231e83195f401421a286060d65643f9c9d40 63 | subpackages: 64 | - ssh 65 | - curve25519 66 | - ed25519 67 | - ed25519/internal/edwards25519 68 | - name: golang.org/x/net 69 | version: 749a502dd1eaf3e5bfd4f8956748c502357c0bbe 70 | subpackages: 71 | - context 72 | - ipv4 73 | - http2 74 | - trace 75 | - bpf 76 | - internal/iana 77 | - internal/netreflect 78 | - http2/hpack 79 | - idna 80 | - lex/httplex 81 | - internal/timeseries 82 | - name: golang.org/x/sys 83 | version: 30de6d19a3bd89a5f38ae4028e23aaa5582648af 84 | subpackages: 85 | - unix 86 | - name: golang.org/x/text 87 | version: 1e65e9bf72c307081cea196f47ef37aed17eb316 88 | subpackages: 89 | - transform 90 | - unicode/norm 91 | - name: google.golang.org/grpc 92 | version: a0ff1e78a98cd140c2d18a0210ab0a2a183a2b4c 93 | subpackages: 94 | - codes 95 | - credentials 96 | - grpclog 97 | - internal 98 | - metadata 99 | - naming 100 | - transport 101 | - peer 102 | - name: gopkg.in/yaml.v2 103 | version: 31c299268d302dd0aa9a0dcf765a3d58971ac83f 104 | testImports: [] 105 | -------------------------------------------------------------------------------- /plugins/lldp/neighbors/neighbors_test.go: -------------------------------------------------------------------------------- 1 | package neighbors_test 2 | 3 | import ( 4 | . "github.com/RackHD/inservice/plugins/lldp/neighbors" 5 | "github.com/google/gopacket" 6 | "github.com/google/gopacket/layers" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "net" 10 | "sync" 11 | ) 12 | 13 | var _ = Describe("Neighbors", func() { 14 | 15 | var ( 16 | neighbors *Neighbors 17 | data []byte 18 | iface net.Interface 19 | packet Packet 20 | wg *sync.WaitGroup 21 | ) 22 | 23 | BeforeEach(func() { 24 | data = []byte{1, 128, 194, 0, 0, 14, 0, 28, 115, 104, 23, 47, 136, 204, 2, 7, 25 | 4, 0, 28, 115, 104, 23, 35, 4, 11, 3, 69, 116, 104, 101, 114, 110, 101, 116, 26 | 49, 50, 6, 2, 0, 2, 10, 11, 68, 67, 79, 79, 66, 45, 55, 48, 53, 48, 83, 12, 27 | 78, 65, 114, 105, 115, 116, 97, 32, 78, 101, 116, 119, 111, 114, 107, 115, 32, 28 | 69, 79, 83, 32, 118, 101, 114, 115, 105, 111, 110, 32, 52, 46, 49, 52, 46, 54, 29 | 77, 32, 114, 117, 110, 110, 105, 110, 103, 32, 111, 110, 32, 97, 110, 32, 65, 30 | 114, 105, 115, 116, 97, 32, 78, 101, 116, 119, 111, 114, 107, 115, 32, 68, 67, 31 | 83, 45, 55, 48, 53, 48, 83, 45, 53, 50, 14, 4, 0, 20, 0, 20, 16, 12, 5, 1, 10, 32 | 240, 16, 9, 2, 0, 30, 132, 129, 0, 254, 6, 0, 128, 194, 1, 0, 1, 254, 9, 0, 18, 33 | 15, 3, 1, 0, 0, 0, 0, 254, 6, 0, 18, 15, 4, 36, 20, 0, 0} 34 | gopacket := gopacket.NewPacket( 35 | data, 36 | layers.LinkTypeEthernet, 37 | gopacket.DecodeOptions{ 38 | Lazy: true, 39 | NoCopy: true, 40 | SkipDecodeRecovery: false}, 41 | ) 42 | iface = net.Interface{Index: 1, MTU: 1500, Name: "eth0", HardwareAddr: []byte{00, 50, 56, 99, 49, 190}, Flags: 19} 43 | packet = Packet{Iface: iface, Packet: gopacket} 44 | wg = &sync.WaitGroup{} 45 | }) 46 | 47 | JustBeforeEach(func() { 48 | neighbors, _ = NewNeighbors() 49 | }) 50 | 51 | Describe("NewNeighbors", func() { 52 | It("should return a Neighbors struct", func() { 53 | neighbors, err := NewNeighbors() 54 | Expect(err).To(Succeed()) 55 | Expect(neighbors).To(BeAssignableToTypeOf(&Neighbors{})) 56 | }) 57 | }) 58 | 59 | Describe("ProcessPacket", func() { 60 | Context("when a good packet is received", func() { 61 | It("should decode a fake packet", func() { 62 | wg.Add(1) 63 | neighbors.ProcessPacket(wg, packet) 64 | neighbors.Rw.Lock() 65 | defer neighbors.Rw.Unlock() 66 | Expect(len(neighbors.Interfaces["eth0"])).Should(Equal(1)) 67 | }) 68 | }) 69 | 70 | Context("when a bad packet is received", func() { 71 | BeforeEach(func() { 72 | data := []byte{} 73 | gopacket := gopacket.NewPacket( 74 | data, 75 | layers.LinkTypeEthernet, 76 | gopacket.DecodeOptions{ 77 | Lazy: true, 78 | NoCopy: true, 79 | SkipDecodeRecovery: false}, 80 | ) 81 | packet = Packet{Iface: iface, Packet: gopacket} 82 | }) 83 | It("should handle empty packet data", func() { 84 | wg.Add(1) 85 | neighbors.ProcessPacket(wg, packet) 86 | neighbors.Rw.Lock() 87 | defer neighbors.Rw.Unlock() 88 | Expect(len(neighbors.Interfaces["eth0"])).Should(Equal(0)) 89 | }) 90 | }) 91 | }) 92 | 93 | Describe("expireNeighbors", func() { 94 | It("show expire timed out neighbors", func() { 95 | wg.Add(1) 96 | neighbors.ProcessPacket(wg, packet) 97 | Eventually(func() int { 98 | neighbors.Rw.Lock() 99 | defer neighbors.Rw.Unlock() 100 | return len(neighbors.Interfaces["eth0"]) 101 | }, "10s", "1s").Should(Equal(0)) 102 | }) 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /agent/plugin_test.go: -------------------------------------------------------------------------------- 1 | package plugins_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/RackHD/inservice/agent" 7 | "github.com/RackHD/inservice/agent/grpc/plugin" 8 | "golang.org/x/net/context" 9 | 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | type ServiceMock struct { 15 | start error 16 | stop error 17 | status error 18 | } 19 | 20 | func (s *ServiceMock) Start(_ context.Context) error { 21 | return s.start 22 | } 23 | 24 | func (s *ServiceMock) Stop(_ context.Context) error { 25 | return s.stop 26 | } 27 | 28 | func (s *ServiceMock) Status(_ context.Context) error { 29 | return s.status 30 | } 31 | 32 | var _ = Describe("Plugin", func() { 33 | Describe("NewPlugin", func() { 34 | It("should require a struct with a Service interface", func() { 35 | _, err := NewPlugin(nil) 36 | Expect(err).To(HaveOccurred()) 37 | }) 38 | 39 | It("should return a Plugin struct", func() { 40 | plugin, err := NewPlugin(&ServiceMock{}) 41 | Expect(err).To(Succeed()) 42 | Expect(plugin).To(BeAssignableToTypeOf(&Plugin{})) 43 | }) 44 | }) 45 | 46 | Describe("Start", func() { 47 | It("should return a plugin.StartResponse on success", func() { 48 | p, err := NewPlugin(&ServiceMock{ 49 | start: nil, 50 | stop: fmt.Errorf("Stop Called."), 51 | status: fmt.Errorf("Status Called."), 52 | }) 53 | Expect(err).To(Succeed()) 54 | 55 | response, err := p.Start(nil, &plugin.StartRequest{}) 56 | Expect(err).To(Succeed()) 57 | Expect(response).To(BeAssignableToTypeOf(&plugin.StartResponse{})) 58 | }) 59 | 60 | It("should return an error on failure", func() { 61 | p, err := NewPlugin(&ServiceMock{ 62 | start: fmt.Errorf("Start Called."), 63 | stop: nil, 64 | status: nil, 65 | }) 66 | Expect(err).To(Succeed()) 67 | 68 | _, err = p.Start(nil, &plugin.StartRequest{}) 69 | Expect(err).To(HaveOccurred()) 70 | }) 71 | }) 72 | 73 | Describe("Stop", func() { 74 | It("should return a plugin.StartResponse on success", func() { 75 | p, err := NewPlugin(&ServiceMock{ 76 | start: fmt.Errorf("Start Called."), 77 | stop: nil, 78 | status: fmt.Errorf("Status Called."), 79 | }) 80 | Expect(err).To(Succeed()) 81 | 82 | response, err := p.Stop(nil, &plugin.StopRequest{}) 83 | Expect(err).To(Succeed()) 84 | Expect(response).To(BeAssignableToTypeOf(&plugin.StopResponse{})) 85 | }) 86 | 87 | It("should return an error on failure", func() { 88 | p, err := NewPlugin(&ServiceMock{ 89 | start: nil, 90 | stop: fmt.Errorf("Stop Called."), 91 | status: nil, 92 | }) 93 | Expect(err).To(Succeed()) 94 | 95 | _, err = p.Stop(nil, &plugin.StopRequest{}) 96 | Expect(err).To(HaveOccurred()) 97 | }) 98 | }) 99 | 100 | Describe("Status", func() { 101 | It("should return a plugin.StatusResponse on success", func() { 102 | p, err := NewPlugin(&ServiceMock{ 103 | start: fmt.Errorf("Start Called."), 104 | stop: fmt.Errorf("Stop Called."), 105 | status: nil, 106 | }) 107 | Expect(err).To(Succeed()) 108 | 109 | response, err := p.Status(nil, &plugin.StatusRequest{}) 110 | Expect(err).To(Succeed()) 111 | Expect(response).To(BeAssignableToTypeOf(&plugin.StatusResponse{})) 112 | }) 113 | 114 | It("should return an error on failure", func() { 115 | p, err := NewPlugin(&ServiceMock{ 116 | start: nil, 117 | stop: nil, 118 | status: fmt.Errorf("Status Called."), 119 | }) 120 | Expect(err).To(Succeed()) 121 | 122 | _, err = p.Status(nil, &plugin.StatusRequest{}) 123 | Expect(err).To(HaveOccurred()) 124 | }) 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /agent/plugin_host.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "time" 8 | 9 | "google.golang.org/grpc" 10 | 11 | "golang.org/x/net/context" 12 | 13 | "github.com/RackHD/inservice/agent/grpc/host" 14 | ) 15 | 16 | // PluginResponseTimeout sets the maximum duration allowed to respond to a request 17 | // for a Plugin Service action (start/stop/status). 18 | const PluginResponseTimeout time.Duration = 10 * time.Second 19 | 20 | // PluginHost encapsulates a collection of PluginProcesses and manages their 21 | // lifecycles. 22 | type PluginHost struct { 23 | address string 24 | port int 25 | 26 | processes []*PluginProcess 27 | notifications chan *PluginProcess 28 | plugins []string 29 | } 30 | 31 | // NewPluginHost returns a new PluginHost. 32 | func NewPluginHost(address string, port int, plugins []string) (*PluginHost, error) { 33 | return &PluginHost{ 34 | address: address, 35 | port: port, 36 | plugins: plugins, 37 | }, nil 38 | } 39 | 40 | // Serve loads and manages all Plugins. 41 | func (h *PluginHost) Serve() { 42 | go h.serveGRPC() 43 | h.servePlugins() 44 | } 45 | 46 | func (h *PluginHost) servePlugins() { 47 | for _, plugin := range h.plugins { 48 | h.processes = append( 49 | h.processes, 50 | NewPluginProcess( 51 | plugin, 52 | ), 53 | ) 54 | } 55 | 56 | h.notifications = make(chan *PluginProcess, len(h.plugins)) 57 | 58 | for _, plugin := range h.processes { 59 | err := plugin.Start(h.notifications) 60 | if err != nil { 61 | log.Printf("Error Starting Plugin: %s, %+v\n", err, plugin) 62 | } 63 | } 64 | 65 | // Monitor 66 | for plugin := range h.notifications { 67 | if plugin.Restartable() { 68 | err := plugin.Start(h.notifications) 69 | if err != nil { 70 | log.Printf("Plugin Unable to Restart: %s (%s)\n", plugin.Name, err) 71 | } 72 | 73 | log.Printf("Plugin Restarted: %s\n", plugin.Name) 74 | } else { 75 | log.Printf("Plugin Not Set for Restart: %s\n", plugin.Name) 76 | } 77 | } 78 | } 79 | 80 | func (h *PluginHost) serveGRPC() { 81 | listen, err := net.Listen( 82 | "tcp", 83 | fmt.Sprintf("%s:%d", h.address, h.port), 84 | ) 85 | if err != nil { 86 | log.Fatalf("Failed to Listen: %v", err) 87 | } 88 | 89 | server := grpc.NewServer() 90 | 91 | host.RegisterHostServer(server, h) 92 | 93 | server.Serve(listen) 94 | } 95 | 96 | // Start implements the gRPC plugin host API and starts the requested plugin. 97 | func (h *PluginHost) Start(ctx context.Context, in *host.StartRequest) (*host.StartResponse, error) { 98 | for _, plugin := range h.processes { 99 | if plugin.Name == in.Name { 100 | err := plugin.Start(h.notifications) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | return &host.StartResponse{}, nil 106 | } 107 | } 108 | 109 | return nil, fmt.Errorf("Unable to locate plugin %s.", in.Name) 110 | } 111 | 112 | // Stop implements the gRPC plugin host API and stops the requested plugin. 113 | func (h *PluginHost) Stop(ctx context.Context, in *host.StopRequest) (*host.StopResponse, error) { 114 | for _, plugin := range h.processes { 115 | if plugin.Name == in.Name { 116 | err := plugin.Stop() 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | return &host.StopResponse{}, nil 122 | } 123 | } 124 | 125 | return nil, fmt.Errorf("Unable to locate plugin %s.", in.Name) 126 | } 127 | 128 | // Status implements the gRPC plugin host API and gets status on all plugins. 129 | func (h *PluginHost) Status(in *host.StatusRequest, stream host.Host_StatusServer) error { 130 | for _, plugin := range h.processes { 131 | var state = "Not Running" 132 | 133 | if plugin.Status() { 134 | state = "Running" 135 | } 136 | 137 | stream.Send(&host.StatusResponse{ 138 | Name: plugin.Name, 139 | Status: state, 140 | }) 141 | } 142 | 143 | stream.Send(nil) 144 | 145 | return nil 146 | } 147 | -------------------------------------------------------------------------------- /agent/plugin_process.go: -------------------------------------------------------------------------------- 1 | package plugins 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "time" 11 | 12 | "github.com/RackHD/inservice/agent/grpc/plugin" 13 | 14 | "golang.org/x/net/context" 15 | "google.golang.org/grpc" 16 | ) 17 | 18 | // This is a helper to generate a suitable unix socket dialer for gRPC. 19 | func unixDialer(path string, timeout time.Duration) (net.Conn, error) { 20 | laddr, err := net.ResolveUnixAddr("unix", "") 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | raddr, err := net.ResolveUnixAddr("unix", path) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | return net.DialUnix("unix", laddr, raddr) 31 | } 32 | 33 | // PluginProcess encapsulates plugin process lifecycle as 34 | // managed by the Plugin Host. 35 | type PluginProcess struct { 36 | Name string 37 | restart bool 38 | running bool 39 | cmd *exec.Cmd 40 | } 41 | 42 | // NewPluginProcess returns a new PluginProcess. 43 | func NewPluginProcess(name string) *PluginProcess { 44 | return &PluginProcess{name, true, false, nil} 45 | } 46 | 47 | // Restartable returns a bool indicating whether the PluginProcess should be 48 | // restarted by the host if the process ends. It's assumed to restart unless 49 | // an API call was made to the Plugin Host to set the PluginProcess to stop. 50 | func (p *PluginProcess) Restartable() bool { 51 | return p.restart 52 | } 53 | 54 | // Start brings up the PluginProcess providing a channel for the plugin to notify 55 | // when the process ends. It redirects stdout/stderr to the host console and 56 | // also contacts the PluginProcess over gRPC to le it know it can start. 57 | func (p *PluginProcess) Start(notify chan<- *PluginProcess) error { 58 | if p.running { 59 | return fmt.Errorf("Pluging Already Running: %s\n", p.Name) 60 | } 61 | 62 | path, err := exec.LookPath(p.Name) 63 | if err != nil { 64 | dir, errDir := filepath.Abs(filepath.Dir(os.Args[0])) 65 | if errDir != nil { 66 | return errDir 67 | } 68 | path = fmt.Sprintf("%s/%s", dir, p.Name) 69 | 70 | } 71 | 72 | p.cmd = exec.Command(path) 73 | 74 | p.cmd.Stdout = os.Stdout 75 | p.cmd.Stderr = os.Stderr 76 | 77 | err = p.cmd.Start() 78 | if err != nil { 79 | return err 80 | } 81 | 82 | p.restart = true 83 | p.running = true 84 | 85 | go func() { 86 | e := p.cmd.Wait() 87 | if e != nil { 88 | log.Printf("Plugin Exited with Error: %s (%s)\n", p.Name, e) 89 | } else { 90 | log.Printf("Plugin Exited: %s\n", p.Name) 91 | } 92 | 93 | p.running = false 94 | p.cmd = nil 95 | 96 | notify <- p 97 | }() 98 | 99 | conn, err := grpc.Dial( 100 | fmt.Sprintf(pluginSockFormat, p.Name), 101 | grpc.WithDialer(unixDialer), 102 | grpc.WithInsecure(), 103 | grpc.WithTimeout(PluginResponseTimeout), 104 | ) 105 | if err != nil { 106 | log.Printf("Error Dialing Plugin: %s\n", err) 107 | return p.Stop() 108 | } 109 | defer conn.Close() 110 | 111 | client := plugin.NewPluginClient(conn) 112 | 113 | _, err = client.Start(context.Background(), &plugin.StartRequest{}) 114 | if err != nil { 115 | log.Printf("Error Starting Plugin: %s\n", err) 116 | return p.Stop() 117 | } 118 | 119 | return nil 120 | } 121 | 122 | // Stop contacts a running PluginProcess over gRPC and asks it to shut down 123 | // then it kills the PluginProcess if it hasn't shutdown in a suitable amount 124 | // of time. 125 | func (p *PluginProcess) Stop() error { 126 | p.restart = false 127 | 128 | conn, err := grpc.Dial( 129 | fmt.Sprintf(pluginSockFormat, p.Name), 130 | grpc.WithInsecure(), 131 | grpc.WithDialer(unixDialer), 132 | grpc.WithTimeout(PluginResponseTimeout), 133 | ) 134 | 135 | if err != nil { 136 | return err 137 | } 138 | 139 | defer conn.Close() 140 | 141 | client := plugin.NewPluginClient(conn) 142 | 143 | _, err = client.Stop(context.Background(), &plugin.StopRequest{}) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | // TODO: Implement a wait mechanism so the PluginProcess can gracefully exit. 149 | 150 | return p.cmd.Process.Kill() 151 | } 152 | 153 | // Status returns a bool indicating whether the PluginProcess is running. 154 | func (p *PluginProcess) Status() bool { 155 | return p.running 156 | } 157 | -------------------------------------------------------------------------------- /plugins/lldp/plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "sync" 8 | "time" 9 | 10 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 11 | "github.com/RackHD/inservice/plugins/lldp/neighbors" 12 | "github.com/google/gopacket" 13 | "github.com/google/gopacket/pcap" 14 | "golang.org/x/net/context" 15 | "google.golang.org/grpc" 16 | ) 17 | 18 | // LLDPPlugin implements the Plugin interface. 19 | type LLDPPlugin struct { 20 | address string 21 | port int 22 | interfaces []string 23 | promiscuous bool 24 | timeout time.Duration 25 | start chan bool 26 | stop chan bool 27 | packets chan neighbors.Packet 28 | wg *sync.WaitGroup 29 | Neighbors neighbors.Neighbors 30 | } 31 | 32 | // NewLLDPPlugin initializes a new LLDPPlugin struct. 33 | func NewLLDPPlugin(address string, port int, interfaces []string) (*LLDPPlugin, error) { 34 | if ip := net.ParseIP(address); ip == nil { 35 | return nil, fmt.Errorf("IP Address Not Valid") 36 | } 37 | if 0 >= port || port >= 65535 { 38 | return nil, fmt.Errorf("Invalid Port Selection") 39 | } 40 | ifaces := []net.Interface{} 41 | for _, iface := range interfaces { 42 | netDev, err := net.InterfaceByName(iface) 43 | if err != nil { 44 | log.Println("No interface named: ", iface) 45 | continue 46 | } 47 | ifaces = append(ifaces, *netDev) 48 | } 49 | if len(ifaces) < 1 { 50 | return nil, fmt.Errorf("No valid interfaces selected") 51 | } 52 | start := make(chan bool) 53 | wg := &sync.WaitGroup{} 54 | n, err := neighbors.NewNeighbors() 55 | if err != nil { 56 | log.Println("Error initializing neighbors") 57 | } 58 | n.NetDevList = ifaces //hitting some linter error where this cant be an argument 59 | return &LLDPPlugin{ 60 | address: address, 61 | port: port, 62 | interfaces: interfaces, 63 | promiscuous: true, 64 | timeout: 30 * time.Second, 65 | start: start, 66 | stop: nil, 67 | wg: wg, 68 | Neighbors: *n, 69 | }, nil 70 | } 71 | 72 | // Start handles plugin startup. This creates goroutines to handle the packet capture 73 | // and serve the gRPC API or REST API 74 | func (p *LLDPPlugin) Start(ctx context.Context) error { 75 | log.Println("LLDP Plugin Started.") 76 | p.stop = make(chan bool) 77 | p.wg.Add(1) 78 | go p.Capture() 79 | p.wg.Add(1) 80 | go p.Serve() 81 | time.Sleep(10 * time.Second) 82 | p.start <- true 83 | return nil 84 | } 85 | 86 | // Stop closes a channel that should stop all capture 87 | func (p *LLDPPlugin) Stop(ctx context.Context) error { 88 | log.Println("LLDP Plugin Stopped.") 89 | close(p.stop) 90 | p.wg.Wait() 91 | return nil 92 | } 93 | 94 | // Status is a dummy function for now until a better status mechanism is identified 95 | func (p *LLDPPlugin) Status(ctx context.Context) error { 96 | log.Println("LLDP Plugin Stopped.") 97 | return nil 98 | } 99 | 100 | // Serve creates the gRPC and REST endpoints for external model access 101 | func (p *LLDPPlugin) Serve() { 102 | listenAddr := fmt.Sprintf("%s:%d", p.address, p.port) 103 | listen, err := net.Listen("tcp", listenAddr) 104 | if err != nil { 105 | log.Fatalf("Failed to Listen: %v", err) 106 | } 107 | 108 | server := grpc.NewServer() 109 | lldp.RegisterLldpServer(server, &p.Neighbors) 110 | 111 | server.Serve(listen) 112 | } 113 | 114 | // Capture opens an LLDP hook for each interface and processes packets. 115 | func (p *LLDPPlugin) Capture() { 116 | log.Println("Started Capture") 117 | p.packets = make(chan neighbors.Packet, 1000) 118 | defer close(p.packets) 119 | for { 120 | select { 121 | case <-p.start: 122 | // create all interfaces 123 | for _, iface := range p.Neighbors.NetDevList { 124 | p.wg.Add(1) 125 | go p.openInterface(iface) 126 | } 127 | // process captured packets 128 | p.wg.Add(1) 129 | go func(p *LLDPPlugin) { 130 | for { 131 | select { 132 | case <-p.stop: 133 | log.Println("Stopping aggregator") 134 | p.wg.Done() 135 | return 136 | case recPacket := <-p.packets: 137 | p.wg.Add(1) 138 | go p.Neighbors.ProcessPacket(p.wg, recPacket) 139 | } 140 | } 141 | }(p) 142 | case <-p.stop: 143 | p.wg.Done() 144 | log.Println("Waited for all processes to stop") 145 | return 146 | } 147 | } 148 | } 149 | 150 | // openInterface creates a handle for each interface in the configuration and pushes 151 | // packet to the global processing channel 152 | func (p *LLDPPlugin) openInterface(iface net.Interface) error { 153 | log.Println("Opened interface: ", iface.Name) 154 | handle, err := pcap.OpenLive(iface.Name, 65536, p.promiscuous, p.timeout) 155 | if err != nil { 156 | return err 157 | } 158 | defer handle.Close() 159 | err = handle.SetBPFFilter("ether proto 0x88cc") 160 | if err != nil { 161 | return err 162 | } 163 | src := gopacket.NewPacketSource(handle, handle.LinkType()) 164 | in := src.Packets() 165 | for { 166 | var packet gopacket.Packet 167 | select { 168 | case <-p.stop: 169 | log.Println("Exiting: ", iface.Name) 170 | handle.Close() 171 | p.wg.Done() 172 | return nil 173 | case packet = <-in: 174 | p.packets <- neighbors.Packet{Iface: iface, Packet: packet} 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/RackHD/inservice.svg?branch=master)](https://travis-ci.org/RackHD/inservice) 2 | [![Coverage Status](https://coveralls.io/repos/github/RackHD/inservice/badge.svg?branch=master)](https://coveralls.io/github/RackHD/inservice?branch=master) 3 | [![GoDoc](https://godoc.org/github.com/RackHD/inservice?status.svg)](https://godoc.org/github.com/RackHD/inservice) 4 | 5 | # InService 6 | 7 | Copyright © 2017 Dell Inc. or its subsidiaries. All Rights Reserved. 8 | 9 | ## Agent 10 | 11 | Compute, network, and storage devices all have different functionality which require different control surfaces and monitoring capabilities. Many of the required features are not accessible via external interfaces and require services hosted on the host to make functionality accessible. Given the diverse sets of functionality across network, compute and storage systems, a plugin based architecture will allow for the reuse of common functionality amongst devices as well as provide a platform for new plugins to be included in the future. 12 | 13 | The Agent is responsible for identifying what Plugins should be hosted on a device and will manage their lifecycle as child processes. It will provide functionality for the registration and advertisement of not only itself but the plugins it is hosting. As such it will be responsible for implementing the advertisement portion of the SSDP protocol from https://tools.ietf.org/html/draft-cai-ssdp-v1-03. The advertisement will provide a URI for obtaining more information about the services (Plugins) available via the Agent. 14 | 15 | Plugins should offer discrete functionality to the ecosystem which can be built upon by higher order services. They should generally be stateless themselves though they may manage the state of the device they are executing on. Any state provided should be recoverable by simply restarting the plugin or the hosting Agent. Dependencies on external state management should be avoided where possible. 16 | 17 | ###Overview 18 | 19 | On startup the Agent will identify what Plugins should be hosted based on configuration and execute them as child processes. Each child process is assumed to be serving an RPC end point which implements start, status, and stop service methods. Once the Plugin process has been started the Agent will contact the Plugin on the RPC end point to notify it to start. In the event no errors occur the Agent will monitor the Plugin process and restart if it exits. 20 | 21 | The Agent itself will host an RPC end point for managing Plugins which consists of a list of running plugins & start/status/stop service functionality for each service by it's name. When stopping a Plugin the Agent will call the Plugin RPC stop end point and allow the Plugin time to gracefully exit. A status listing will also be made available. 22 | 23 | In addition the Agent will advertise via SSDP and provide a URI for advertisement recipients to contact for more information about supported plugins. The location URI will be hosted by the Agent and serve content from a local cache of data provided the plugins which register for advertisement with the Agent. As information changes the content provided by the location URI will change. 24 | 25 | ## Plugins 26 | ### Catalog Compute 27 | 28 | RackHD (https://github.com/RackHD) is able to generate low level inventories of a resource's hardware components as part of its discovery process. Customers desire to have this cataloging process be made available while a server is running its primary operating system. InService Agent will execute this plugin periodically to generate a local equivalent of the hosts hardware catalog that can be consumed by infrastructure managers. 29 | 30 | ### LLDP 31 | 32 | LLDP (IEEE standard 802.1AB-2009) is a network protocol for identifying devices physically connected to a hosts network port. It can be used to discover and describe network topology. The LLDP plugin is responsible for listening to LLDP advertisements on all of host's interfaces where the plugin is running and caching the data. The initial usage of this plugin will be targeted to InService running on a network switch to enable switch topology mappings. The plugin may later be targeted to run on all compute nodes to provide mapping information from both sides of the network connection. The collected data will be made available via an API and higher order services can utilize the data to discovery devices and create a topology by combining LLDP data from multiple LLDP plugins. 33 | 34 | [Agent]: https://github.com/RackHD/inservice/tree/master/agent 35 | [Catalog Compute]: https://github.com/RackHD/inservice/tree/master/plugins/catalog-compute 36 | [LLDP]: https://github.com/RackHD/inservice/tree/master/plugins/lldp 37 | 38 | Contribute 39 | ---------- 40 | 41 | InService is a collection of libraries and applications housed at https://github.com/RackHD/inservice. The code for Inservice is written in Golang and makes use of Makefiles. It is available under the Apache 2.0 license (or compatible sublicences for library dependencies). 42 | 43 | Code and bug submissions are handled on GitHub using the Issues tab for this repository above. 44 | 45 | Community 46 | --------- 47 | 48 | We also have a #InfraEnablers Slack channel: You can get an invite by requesting one at http://community.emccode.com. 49 | 50 | Documentation 51 | ------------- 52 | 53 | You can find documentation for InService here: [![GoDoc](https://godoc.org/github.com/RackHD/inservice?status.svg)](https://godoc.org/github.com/RackHD/inservice) 54 | 55 | Licensing 56 | --------- 57 | 58 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 59 | 60 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 61 | 62 | RackHD is a Trademark of EMC Corporation 63 | 64 | Support 65 | ------- 66 | 67 | Please file bugs and issues at the GitHub issues page. The code and documentation are released with no warranties or SLAs and are intended to be supported through a community driven process. 68 | -------------------------------------------------------------------------------- /plugins/lldp/neighbors/neighbors.go: -------------------------------------------------------------------------------- 1 | package neighbors 2 | 3 | import ( 4 | "fmt" 5 | "github.com/RackHD/inservice/plugins/lldp/grpc/lldp" 6 | "github.com/google/gopacket" 7 | "golang.org/x/net/context" 8 | "log" 9 | "net" 10 | "strings" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | // Neighbors stores the local neighbors topology 16 | type Neighbors struct { 17 | Rw sync.RWMutex `json:"-"` 18 | NetDevList []net.Interface `json:"-"` 19 | Interfaces map[string]map[string]NeighborDetails `json:"connections,omitempty"` 20 | ChassisID string `json:"host,omitempty"` 21 | SysName string `json:"hostname,omitempty"` 22 | SysDesc string `json:"description,omitempty"` 23 | Address string `json:"address,omitempty"` 24 | Type string `json:"type,omitempty"` 25 | } 26 | 27 | // NeighborDetails stores relevent information to identify a neighboring node 28 | type NeighborDetails struct { 29 | PortID string `json:"portid,omitempty"` 30 | PortDescription string `json:"portdesc,omitempty"` 31 | SysName string `json:"hostname,omitempty"` 32 | SysDesc string `json:"description,omitempty"` 33 | Address string `json:"address,omitempty"` 34 | Vlan string `json:"vlan,omitempty"` 35 | Type string `json:"type,omitempty"` 36 | TTL *time.Timer `json:"-"` 37 | } 38 | 39 | // Packet encodes the ingress interface with the receives LLDP packet 40 | type Packet struct { 41 | Iface net.Interface 42 | Packet gopacket.Packet 43 | } 44 | 45 | // NewNeighbors creates the Neighbors data storage object and returns it to the LLDP Plugin 46 | func NewNeighbors() (*Neighbors, error) { 47 | t := make(map[string]map[string]NeighborDetails) 48 | return &Neighbors{ 49 | Interfaces: t, 50 | }, nil 51 | 52 | } 53 | 54 | // ListInterfaces provides a list of all active interfaces 55 | func (n *Neighbors) ListInterfaces(in *lldp.EmptyMessage, stream lldp.Lldp_ListInterfacesServer) error { 56 | for _, iface := range n.NetDevList { 57 | err := stream.Send(&lldp.Interface{ 58 | Name: iface.Name, 59 | Hardwareaddr: iface.HardwareAddr.String(), 60 | }) 61 | if err != nil { 62 | return err 63 | } 64 | } 65 | return nil 66 | } 67 | 68 | // GetInterfaceDetails provides details indofmration about each interface 69 | func (n *Neighbors) GetInterfaceDetails(ctx context.Context, in *lldp.Interface) (*lldp.Interface, error) { 70 | for _, iface := range n.NetDevList { 71 | if iface.Name == in.Name { 72 | return &lldp.Interface{ 73 | Index: int32(iface.Index), 74 | Mtu: int32(iface.MTU), 75 | Name: iface.Name, 76 | Hardwareaddr: iface.HardwareAddr.String(), 77 | Flags: uint32(iface.Flags), 78 | }, nil 79 | } 80 | } 81 | return &lldp.Interface{}, nil 82 | } 83 | 84 | // ListInterfaceNeighbors shows the neighbors attached to a specific interface 85 | func (n *Neighbors) ListInterfaceNeighbors(in *lldp.Interface, stream lldp.Lldp_ListInterfaceNeighborsServer) error { 86 | var targetInterface *net.Interface 87 | for _, iface := range n.NetDevList { 88 | if in.Name == iface.Name { 89 | targetInterface = &iface 90 | break 91 | } 92 | } 93 | if targetInterface == nil { 94 | return fmt.Errorf("Interface does not exist or is not active") 95 | } 96 | 97 | n.Rw.RLock() 98 | defer n.Rw.RUnlock() 99 | val, ok := n.Interfaces[targetInterface.Name] 100 | if !ok { 101 | return fmt.Errorf("Interface does not have any neighbors") 102 | } 103 | for neighbors := range val { 104 | data := val[neighbors] 105 | err := stream.Send(&lldp.Neighbor{ 106 | Portid: data.PortID, 107 | Portdescription: data.PortDescription, 108 | Sysname: data.SysName, 109 | Sysdesc: data.SysDesc, 110 | Address: data.Address, 111 | Vlan: data.Vlan, 112 | Type: data.Type, 113 | }) 114 | if err != nil { 115 | return err 116 | } 117 | } 118 | return nil 119 | } 120 | 121 | // ListNeighbors shows all neighbors attached to all active interfaces on the host 122 | func (n *Neighbors) ListNeighbors(in *lldp.EmptyMessage, stream lldp.Lldp_ListNeighborsServer) error { 123 | n.Rw.RLock() 124 | defer n.Rw.RUnlock() 125 | for _, val := range n.Interfaces { 126 | for neighbors := range val { 127 | data := val[neighbors] 128 | err := stream.Send(&lldp.Neighbor{ 129 | Portid: data.PortID, 130 | Portdescription: data.PortDescription, 131 | Sysname: data.SysName, 132 | Sysdesc: data.SysDesc, 133 | Address: data.Address, 134 | Vlan: data.Vlan, 135 | Type: data.Type, 136 | }) 137 | if err != nil { 138 | return err 139 | } 140 | } 141 | } 142 | return nil 143 | } 144 | 145 | // ProcessPacket takes a cpatured packet 146 | func (n *Neighbors) ProcessPacket(wg *sync.WaitGroup, in Packet) { 147 | // log.Printf("%v\n", in.Packet.Data()) 148 | lldpDiscovery, lldpInfo, err := n.translatePacket(&in) 149 | if err != nil { 150 | wg.Done() 151 | return 152 | } 153 | chassis := n.decodeChassis(lldpDiscovery) 154 | address := n.decodeMgmtAddress(lldpInfo) 155 | neighborPort := n.decodePortID(lldpDiscovery) 156 | neighborPortDescription := n.decodePortDescription(lldpInfo) 157 | vlan := n.decodeVlan(lldpInfo) 158 | sysType := n.determineSysType(lldpDiscovery, lldpInfo) 159 | if n.isLocalInterface(chassis) { 160 | n.ChassisID = chassis 161 | n.SysName = lldpInfo.SysName 162 | n.SysDesc = lldpInfo.SysDescription 163 | n.Address = address 164 | n.Type = sysType 165 | } else { 166 | if chassis == "" { 167 | wg.Done() 168 | return 169 | } 170 | n.Rw.Lock() 171 | defer n.Rw.Unlock() 172 | c, ok := n.Interfaces[in.Iface.Name] 173 | if !ok { 174 | c = make(map[string]NeighborDetails) 175 | n.Interfaces[in.Iface.Name] = c 176 | } 177 | t, ok := c[chassis] 178 | if ok { 179 | t.TTL.Stop() 180 | } 181 | f := func() { 182 | n.expireNeighbor(in.Iface.Name, chassis) 183 | } 184 | timeout := time.AfterFunc(time.Duration(lldpDiscovery.TTL)*time.Second, f) 185 | node := NeighborDetails{ 186 | PortID: neighborPort, 187 | PortDescription: neighborPortDescription, 188 | SysName: lldpInfo.SysName, 189 | SysDesc: lldpInfo.SysDescription, 190 | Address: address, 191 | Vlan: vlan, 192 | Type: sysType, 193 | TTL: timeout, 194 | } 195 | c[chassis] = node 196 | } 197 | wg.Done() 198 | return 199 | } 200 | 201 | func (n *Neighbors) isLocalInterface(input string) bool { 202 | for _, iface := range n.NetDevList { 203 | if strings.Compare(iface.HardwareAddr.String(), input) == 0 { 204 | return true 205 | } 206 | } 207 | return false 208 | } 209 | 210 | func (n *Neighbors) expireNeighbor(iface string, chassis string) { 211 | n.Rw.Lock() 212 | defer n.Rw.Unlock() 213 | neighbor, ok := n.Interfaces[iface] 214 | if !ok { 215 | return 216 | } 217 | delete(neighbor, chassis) 218 | log.Printf("LLDP: Removed expired neighbor %s\n", chassis) 219 | return 220 | } 221 | -------------------------------------------------------------------------------- /agent/grpc/plugin/plugin.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: plugin.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package plugin is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | plugin.proto 10 | 11 | It has these top-level messages: 12 | StartRequest 13 | StartResponse 14 | StopRequest 15 | StopResponse 16 | StatusRequest 17 | StatusResponse 18 | */ 19 | package plugin 20 | 21 | import proto "github.com/golang/protobuf/proto" 22 | import fmt "fmt" 23 | import math "math" 24 | 25 | import ( 26 | context "golang.org/x/net/context" 27 | grpc "google.golang.org/grpc" 28 | ) 29 | 30 | // Reference imports to suppress errors if they are not otherwise used. 31 | var _ = proto.Marshal 32 | var _ = fmt.Errorf 33 | var _ = math.Inf 34 | 35 | // This is a compile-time assertion to ensure that this generated file 36 | // is compatible with the proto package it is being compiled against. 37 | // A compilation error at this line likely means your copy of the 38 | // proto package needs to be updated. 39 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 40 | 41 | type StartRequest struct { 42 | } 43 | 44 | func (m *StartRequest) Reset() { *m = StartRequest{} } 45 | func (m *StartRequest) String() string { return proto.CompactTextString(m) } 46 | func (*StartRequest) ProtoMessage() {} 47 | func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 48 | 49 | type StartResponse struct { 50 | } 51 | 52 | func (m *StartResponse) Reset() { *m = StartResponse{} } 53 | func (m *StartResponse) String() string { return proto.CompactTextString(m) } 54 | func (*StartResponse) ProtoMessage() {} 55 | func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 56 | 57 | type StopRequest struct { 58 | } 59 | 60 | func (m *StopRequest) Reset() { *m = StopRequest{} } 61 | func (m *StopRequest) String() string { return proto.CompactTextString(m) } 62 | func (*StopRequest) ProtoMessage() {} 63 | func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 64 | 65 | type StopResponse struct { 66 | } 67 | 68 | func (m *StopResponse) Reset() { *m = StopResponse{} } 69 | func (m *StopResponse) String() string { return proto.CompactTextString(m) } 70 | func (*StopResponse) ProtoMessage() {} 71 | func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 72 | 73 | type StatusRequest struct { 74 | } 75 | 76 | func (m *StatusRequest) Reset() { *m = StatusRequest{} } 77 | func (m *StatusRequest) String() string { return proto.CompactTextString(m) } 78 | func (*StatusRequest) ProtoMessage() {} 79 | func (*StatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 80 | 81 | type StatusResponse struct { 82 | } 83 | 84 | func (m *StatusResponse) Reset() { *m = StatusResponse{} } 85 | func (m *StatusResponse) String() string { return proto.CompactTextString(m) } 86 | func (*StatusResponse) ProtoMessage() {} 87 | func (*StatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 88 | 89 | func init() { 90 | proto.RegisterType((*StartRequest)(nil), "plugin.StartRequest") 91 | proto.RegisterType((*StartResponse)(nil), "plugin.StartResponse") 92 | proto.RegisterType((*StopRequest)(nil), "plugin.StopRequest") 93 | proto.RegisterType((*StopResponse)(nil), "plugin.StopResponse") 94 | proto.RegisterType((*StatusRequest)(nil), "plugin.StatusRequest") 95 | proto.RegisterType((*StatusResponse)(nil), "plugin.StatusResponse") 96 | } 97 | 98 | // Reference imports to suppress errors if they are not otherwise used. 99 | var _ context.Context 100 | var _ grpc.ClientConn 101 | 102 | // This is a compile-time assertion to ensure that this generated file 103 | // is compatible with the grpc package it is being compiled against. 104 | const _ = grpc.SupportPackageIsVersion3 105 | 106 | // Client API for Plugin service 107 | 108 | type PluginClient interface { 109 | Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) 110 | Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) 111 | Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) 112 | } 113 | 114 | type pluginClient struct { 115 | cc *grpc.ClientConn 116 | } 117 | 118 | func NewPluginClient(cc *grpc.ClientConn) PluginClient { 119 | return &pluginClient{cc} 120 | } 121 | 122 | func (c *pluginClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { 123 | out := new(StartResponse) 124 | err := grpc.Invoke(ctx, "/plugin.Plugin/Start", in, out, c.cc, opts...) 125 | if err != nil { 126 | return nil, err 127 | } 128 | return out, nil 129 | } 130 | 131 | func (c *pluginClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) { 132 | out := new(StopResponse) 133 | err := grpc.Invoke(ctx, "/plugin.Plugin/Stop", in, out, c.cc, opts...) 134 | if err != nil { 135 | return nil, err 136 | } 137 | return out, nil 138 | } 139 | 140 | func (c *pluginClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { 141 | out := new(StatusResponse) 142 | err := grpc.Invoke(ctx, "/plugin.Plugin/Status", in, out, c.cc, opts...) 143 | if err != nil { 144 | return nil, err 145 | } 146 | return out, nil 147 | } 148 | 149 | // Server API for Plugin service 150 | 151 | type PluginServer interface { 152 | Start(context.Context, *StartRequest) (*StartResponse, error) 153 | Stop(context.Context, *StopRequest) (*StopResponse, error) 154 | Status(context.Context, *StatusRequest) (*StatusResponse, error) 155 | } 156 | 157 | func RegisterPluginServer(s *grpc.Server, srv PluginServer) { 158 | s.RegisterService(&_Plugin_serviceDesc, srv) 159 | } 160 | 161 | func _Plugin_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 162 | in := new(StartRequest) 163 | if err := dec(in); err != nil { 164 | return nil, err 165 | } 166 | if interceptor == nil { 167 | return srv.(PluginServer).Start(ctx, in) 168 | } 169 | info := &grpc.UnaryServerInfo{ 170 | Server: srv, 171 | FullMethod: "/plugin.Plugin/Start", 172 | } 173 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 174 | return srv.(PluginServer).Start(ctx, req.(*StartRequest)) 175 | } 176 | return interceptor(ctx, in, info, handler) 177 | } 178 | 179 | func _Plugin_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 180 | in := new(StopRequest) 181 | if err := dec(in); err != nil { 182 | return nil, err 183 | } 184 | if interceptor == nil { 185 | return srv.(PluginServer).Stop(ctx, in) 186 | } 187 | info := &grpc.UnaryServerInfo{ 188 | Server: srv, 189 | FullMethod: "/plugin.Plugin/Stop", 190 | } 191 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 192 | return srv.(PluginServer).Stop(ctx, req.(*StopRequest)) 193 | } 194 | return interceptor(ctx, in, info, handler) 195 | } 196 | 197 | func _Plugin_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 198 | in := new(StatusRequest) 199 | if err := dec(in); err != nil { 200 | return nil, err 201 | } 202 | if interceptor == nil { 203 | return srv.(PluginServer).Status(ctx, in) 204 | } 205 | info := &grpc.UnaryServerInfo{ 206 | Server: srv, 207 | FullMethod: "/plugin.Plugin/Status", 208 | } 209 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 210 | return srv.(PluginServer).Status(ctx, req.(*StatusRequest)) 211 | } 212 | return interceptor(ctx, in, info, handler) 213 | } 214 | 215 | var _Plugin_serviceDesc = grpc.ServiceDesc{ 216 | ServiceName: "plugin.Plugin", 217 | HandlerType: (*PluginServer)(nil), 218 | Methods: []grpc.MethodDesc{ 219 | { 220 | MethodName: "Start", 221 | Handler: _Plugin_Start_Handler, 222 | }, 223 | { 224 | MethodName: "Stop", 225 | Handler: _Plugin_Stop_Handler, 226 | }, 227 | { 228 | MethodName: "Status", 229 | Handler: _Plugin_Status_Handler, 230 | }, 231 | }, 232 | Streams: []grpc.StreamDesc{}, 233 | Metadata: fileDescriptor0, 234 | } 235 | 236 | func init() { proto.RegisterFile("plugin.proto", fileDescriptor0) } 237 | 238 | var fileDescriptor0 = []byte{ 239 | // 168 bytes of a gzipped FileDescriptorProto 240 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0xc8, 0x29, 0x4d, 241 | 0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0xf8, 0xb8, 0x78, 242 | 0x82, 0x4b, 0x12, 0x8b, 0x4a, 0x82, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x94, 0xf8, 0xb9, 0x78, 243 | 0xa1, 0xfc, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0x25, 0x5e, 0x2e, 0xee, 0xe0, 0x92, 0xfc, 0x02, 244 | 0x98, 0x3c, 0x58, 0x3d, 0x88, 0x0b, 0x95, 0x86, 0xa8, 0x2f, 0x29, 0x2d, 0x86, 0x29, 0x10, 0xe0, 245 | 0xe2, 0x83, 0x09, 0x40, 0x94, 0x18, 0x6d, 0x60, 0xe4, 0x62, 0x0b, 0x00, 0xdb, 0x26, 0x64, 0xc6, 246 | 0xc5, 0x0a, 0x36, 0x5d, 0x48, 0x44, 0x0f, 0xea, 0x1a, 0x64, 0xcb, 0xa5, 0x44, 0xd1, 0x44, 0xa1, 247 | 0x76, 0x30, 0x08, 0x19, 0x73, 0xb1, 0x80, 0x6c, 0x15, 0x12, 0x46, 0x28, 0x80, 0x3b, 0x49, 0x4a, 248 | 0x04, 0x55, 0x10, 0xae, 0xc9, 0x92, 0x8b, 0x0d, 0xe2, 0x12, 0x21, 0x64, 0x73, 0x11, 0x4e, 0x95, 249 | 0x12, 0x43, 0x17, 0x86, 0x69, 0x4d, 0x62, 0x03, 0x07, 0x92, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 250 | 0x86, 0x87, 0x83, 0x35, 0x34, 0x01, 0x00, 0x00, 251 | } 252 | -------------------------------------------------------------------------------- /agent/grpc/host/host.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: host.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package host is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | host.proto 10 | 11 | It has these top-level messages: 12 | StartRequest 13 | StartResponse 14 | StopRequest 15 | StopResponse 16 | StatusRequest 17 | StatusResponse 18 | */ 19 | package host 20 | 21 | import proto "github.com/golang/protobuf/proto" 22 | import fmt "fmt" 23 | import math "math" 24 | 25 | import ( 26 | context "golang.org/x/net/context" 27 | grpc "google.golang.org/grpc" 28 | ) 29 | 30 | // Reference imports to suppress errors if they are not otherwise used. 31 | var _ = proto.Marshal 32 | var _ = fmt.Errorf 33 | var _ = math.Inf 34 | 35 | // This is a compile-time assertion to ensure that this generated file 36 | // is compatible with the proto package it is being compiled against. 37 | // A compilation error at this line likely means your copy of the 38 | // proto package needs to be updated. 39 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 40 | 41 | type StartRequest struct { 42 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 43 | } 44 | 45 | func (m *StartRequest) Reset() { *m = StartRequest{} } 46 | func (m *StartRequest) String() string { return proto.CompactTextString(m) } 47 | func (*StartRequest) ProtoMessage() {} 48 | func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 49 | 50 | type StartResponse struct { 51 | } 52 | 53 | func (m *StartResponse) Reset() { *m = StartResponse{} } 54 | func (m *StartResponse) String() string { return proto.CompactTextString(m) } 55 | func (*StartResponse) ProtoMessage() {} 56 | func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 57 | 58 | type StopRequest struct { 59 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 60 | } 61 | 62 | func (m *StopRequest) Reset() { *m = StopRequest{} } 63 | func (m *StopRequest) String() string { return proto.CompactTextString(m) } 64 | func (*StopRequest) ProtoMessage() {} 65 | func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 66 | 67 | type StopResponse struct { 68 | } 69 | 70 | func (m *StopResponse) Reset() { *m = StopResponse{} } 71 | func (m *StopResponse) String() string { return proto.CompactTextString(m) } 72 | func (*StopResponse) ProtoMessage() {} 73 | func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 74 | 75 | type StatusRequest struct { 76 | } 77 | 78 | func (m *StatusRequest) Reset() { *m = StatusRequest{} } 79 | func (m *StatusRequest) String() string { return proto.CompactTextString(m) } 80 | func (*StatusRequest) ProtoMessage() {} 81 | func (*StatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 82 | 83 | type StatusResponse struct { 84 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 85 | Status string `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"` 86 | } 87 | 88 | func (m *StatusResponse) Reset() { *m = StatusResponse{} } 89 | func (m *StatusResponse) String() string { return proto.CompactTextString(m) } 90 | func (*StatusResponse) ProtoMessage() {} 91 | func (*StatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 92 | 93 | func init() { 94 | proto.RegisterType((*StartRequest)(nil), "host.StartRequest") 95 | proto.RegisterType((*StartResponse)(nil), "host.StartResponse") 96 | proto.RegisterType((*StopRequest)(nil), "host.StopRequest") 97 | proto.RegisterType((*StopResponse)(nil), "host.StopResponse") 98 | proto.RegisterType((*StatusRequest)(nil), "host.StatusRequest") 99 | proto.RegisterType((*StatusResponse)(nil), "host.StatusResponse") 100 | } 101 | 102 | // Reference imports to suppress errors if they are not otherwise used. 103 | var _ context.Context 104 | var _ grpc.ClientConn 105 | 106 | // This is a compile-time assertion to ensure that this generated file 107 | // is compatible with the grpc package it is being compiled against. 108 | const _ = grpc.SupportPackageIsVersion3 109 | 110 | // Client API for Host service 111 | 112 | type HostClient interface { 113 | Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) 114 | Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) 115 | Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Host_StatusClient, error) 116 | } 117 | 118 | type hostClient struct { 119 | cc *grpc.ClientConn 120 | } 121 | 122 | func NewHostClient(cc *grpc.ClientConn) HostClient { 123 | return &hostClient{cc} 124 | } 125 | 126 | func (c *hostClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { 127 | out := new(StartResponse) 128 | err := grpc.Invoke(ctx, "/host.Host/Start", in, out, c.cc, opts...) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return out, nil 133 | } 134 | 135 | func (c *hostClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) { 136 | out := new(StopResponse) 137 | err := grpc.Invoke(ctx, "/host.Host/Stop", in, out, c.cc, opts...) 138 | if err != nil { 139 | return nil, err 140 | } 141 | return out, nil 142 | } 143 | 144 | func (c *hostClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Host_StatusClient, error) { 145 | stream, err := grpc.NewClientStream(ctx, &_Host_serviceDesc.Streams[0], c.cc, "/host.Host/Status", opts...) 146 | if err != nil { 147 | return nil, err 148 | } 149 | x := &hostStatusClient{stream} 150 | if err := x.ClientStream.SendMsg(in); err != nil { 151 | return nil, err 152 | } 153 | if err := x.ClientStream.CloseSend(); err != nil { 154 | return nil, err 155 | } 156 | return x, nil 157 | } 158 | 159 | type Host_StatusClient interface { 160 | Recv() (*StatusResponse, error) 161 | grpc.ClientStream 162 | } 163 | 164 | type hostStatusClient struct { 165 | grpc.ClientStream 166 | } 167 | 168 | func (x *hostStatusClient) Recv() (*StatusResponse, error) { 169 | m := new(StatusResponse) 170 | if err := x.ClientStream.RecvMsg(m); err != nil { 171 | return nil, err 172 | } 173 | return m, nil 174 | } 175 | 176 | // Server API for Host service 177 | 178 | type HostServer interface { 179 | Start(context.Context, *StartRequest) (*StartResponse, error) 180 | Stop(context.Context, *StopRequest) (*StopResponse, error) 181 | Status(*StatusRequest, Host_StatusServer) error 182 | } 183 | 184 | func RegisterHostServer(s *grpc.Server, srv HostServer) { 185 | s.RegisterService(&_Host_serviceDesc, srv) 186 | } 187 | 188 | func _Host_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 189 | in := new(StartRequest) 190 | if err := dec(in); err != nil { 191 | return nil, err 192 | } 193 | if interceptor == nil { 194 | return srv.(HostServer).Start(ctx, in) 195 | } 196 | info := &grpc.UnaryServerInfo{ 197 | Server: srv, 198 | FullMethod: "/host.Host/Start", 199 | } 200 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 201 | return srv.(HostServer).Start(ctx, req.(*StartRequest)) 202 | } 203 | return interceptor(ctx, in, info, handler) 204 | } 205 | 206 | func _Host_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 207 | in := new(StopRequest) 208 | if err := dec(in); err != nil { 209 | return nil, err 210 | } 211 | if interceptor == nil { 212 | return srv.(HostServer).Stop(ctx, in) 213 | } 214 | info := &grpc.UnaryServerInfo{ 215 | Server: srv, 216 | FullMethod: "/host.Host/Stop", 217 | } 218 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 219 | return srv.(HostServer).Stop(ctx, req.(*StopRequest)) 220 | } 221 | return interceptor(ctx, in, info, handler) 222 | } 223 | 224 | func _Host_Status_Handler(srv interface{}, stream grpc.ServerStream) error { 225 | m := new(StatusRequest) 226 | if err := stream.RecvMsg(m); err != nil { 227 | return err 228 | } 229 | return srv.(HostServer).Status(m, &hostStatusServer{stream}) 230 | } 231 | 232 | type Host_StatusServer interface { 233 | Send(*StatusResponse) error 234 | grpc.ServerStream 235 | } 236 | 237 | type hostStatusServer struct { 238 | grpc.ServerStream 239 | } 240 | 241 | func (x *hostStatusServer) Send(m *StatusResponse) error { 242 | return x.ServerStream.SendMsg(m) 243 | } 244 | 245 | var _Host_serviceDesc = grpc.ServiceDesc{ 246 | ServiceName: "host.Host", 247 | HandlerType: (*HostServer)(nil), 248 | Methods: []grpc.MethodDesc{ 249 | { 250 | MethodName: "Start", 251 | Handler: _Host_Start_Handler, 252 | }, 253 | { 254 | MethodName: "Stop", 255 | Handler: _Host_Stop_Handler, 256 | }, 257 | }, 258 | Streams: []grpc.StreamDesc{ 259 | { 260 | StreamName: "Status", 261 | Handler: _Host_Status_Handler, 262 | ServerStreams: true, 263 | }, 264 | }, 265 | Metadata: fileDescriptor0, 266 | } 267 | 268 | func init() { proto.RegisterFile("host.proto", fileDescriptor0) } 269 | 270 | var fileDescriptor0 = []byte{ 271 | // 205 bytes of a gzipped FileDescriptorProto 272 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xca, 0xc8, 0x2f, 0x2e, 273 | 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0x94, 0xb8, 0x78, 0x82, 0x4b, 274 | 0x12, 0x8b, 0x4a, 0x82, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 275 | 0x73, 0x53, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x7e, 0x2e, 0x5e, 0xa8, 276 | 0x9a, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0x25, 0x45, 0x2e, 0xee, 0xe0, 0x92, 0xfc, 0x02, 0x7c, 277 | 0x7a, 0xf8, 0x40, 0xe6, 0x82, 0x94, 0x40, 0xb5, 0x40, 0xcc, 0x28, 0x29, 0x2d, 0x86, 0x6a, 0x52, 278 | 0xb2, 0xe1, 0xe2, 0x83, 0x09, 0x40, 0x94, 0x60, 0x33, 0x46, 0x48, 0x8c, 0x8b, 0xad, 0x18, 0xac, 279 | 0x4a, 0x82, 0x09, 0x2c, 0x0a, 0xe5, 0x19, 0x2d, 0x61, 0xe4, 0x62, 0xf1, 0xc8, 0x2f, 0x2e, 0x11, 280 | 0x32, 0xe2, 0x62, 0x05, 0xbb, 0x4d, 0x48, 0x48, 0x0f, 0xec, 0x37, 0x64, 0xcf, 0x48, 0x09, 0xa3, 281 | 0x88, 0x41, 0x5d, 0xc2, 0x20, 0xa4, 0xcf, 0xc5, 0x02, 0x72, 0x9b, 0x90, 0x20, 0x4c, 0x1a, 0xee, 282 | 0x15, 0x29, 0x21, 0x64, 0x21, 0xb8, 0x06, 0x73, 0x2e, 0x36, 0x88, 0x5b, 0x85, 0x10, 0x26, 0x22, 283 | 0xbc, 0x22, 0x25, 0x82, 0x2a, 0x08, 0xd3, 0x66, 0xc0, 0x98, 0xc4, 0x06, 0x0e, 0x6a, 0x63, 0x40, 284 | 0x00, 0x00, 0x00, 0xff, 0xff, 0x1c, 0xd9, 0xa4, 0xc6, 0x78, 0x01, 0x00, 0x00, 285 | } 286 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /plugins/lldp/grpc/lldp/lldp.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: lldp.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package lldp is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | lldp.proto 10 | 11 | It has these top-level messages: 12 | EmptyMessage 13 | Neighbor 14 | Interface 15 | */ 16 | package lldp 17 | 18 | import proto "github.com/golang/protobuf/proto" 19 | import fmt "fmt" 20 | import math "math" 21 | 22 | import ( 23 | context "golang.org/x/net/context" 24 | grpc "google.golang.org/grpc" 25 | ) 26 | 27 | // Reference imports to suppress errors if they are not otherwise used. 28 | var _ = proto.Marshal 29 | var _ = fmt.Errorf 30 | var _ = math.Inf 31 | 32 | // This is a compile-time assertion to ensure that this generated file 33 | // is compatible with the proto package it is being compiled against. 34 | // A compilation error at this line likely means your copy of the 35 | // proto package needs to be updated. 36 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 37 | 38 | type EmptyMessage struct { 39 | } 40 | 41 | func (m *EmptyMessage) Reset() { *m = EmptyMessage{} } 42 | func (m *EmptyMessage) String() string { return proto.CompactTextString(m) } 43 | func (*EmptyMessage) ProtoMessage() {} 44 | func (*EmptyMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 45 | 46 | type Neighbor struct { 47 | Portid string `protobuf:"bytes,1,opt,name=portid" json:"portid,omitempty"` 48 | Portdescription string `protobuf:"bytes,2,opt,name=portdescription" json:"portdescription,omitempty"` 49 | Sysname string `protobuf:"bytes,3,opt,name=sysname" json:"sysname,omitempty"` 50 | Sysdesc string `protobuf:"bytes,4,opt,name=sysdesc" json:"sysdesc,omitempty"` 51 | Address string `protobuf:"bytes,5,opt,name=address" json:"address,omitempty"` 52 | Vlan string `protobuf:"bytes,6,opt,name=vlan" json:"vlan,omitempty"` 53 | Type string `protobuf:"bytes,7,opt,name=type" json:"type,omitempty"` 54 | } 55 | 56 | func (m *Neighbor) Reset() { *m = Neighbor{} } 57 | func (m *Neighbor) String() string { return proto.CompactTextString(m) } 58 | func (*Neighbor) ProtoMessage() {} 59 | func (*Neighbor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 60 | 61 | type Interface struct { 62 | Index int32 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"` 63 | Mtu int32 `protobuf:"varint,2,opt,name=mtu" json:"mtu,omitempty"` 64 | Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` 65 | Hardwareaddr string `protobuf:"bytes,4,opt,name=hardwareaddr" json:"hardwareaddr,omitempty"` 66 | Flags uint32 `protobuf:"varint,5,opt,name=flags" json:"flags,omitempty"` 67 | } 68 | 69 | func (m *Interface) Reset() { *m = Interface{} } 70 | func (m *Interface) String() string { return proto.CompactTextString(m) } 71 | func (*Interface) ProtoMessage() {} 72 | func (*Interface) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 73 | 74 | func init() { 75 | proto.RegisterType((*EmptyMessage)(nil), "lldp.EmptyMessage") 76 | proto.RegisterType((*Neighbor)(nil), "lldp.Neighbor") 77 | proto.RegisterType((*Interface)(nil), "lldp.Interface") 78 | } 79 | 80 | // Reference imports to suppress errors if they are not otherwise used. 81 | var _ context.Context 82 | var _ grpc.ClientConn 83 | 84 | // This is a compile-time assertion to ensure that this generated file 85 | // is compatible with the grpc package it is being compiled against. 86 | const _ = grpc.SupportPackageIsVersion3 87 | 88 | // Client API for Lldp service 89 | 90 | type LldpClient interface { 91 | ListInterfaces(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (Lldp_ListInterfacesClient, error) 92 | GetInterfaceDetails(ctx context.Context, in *Interface, opts ...grpc.CallOption) (*Interface, error) 93 | ListInterfaceNeighbors(ctx context.Context, in *Interface, opts ...grpc.CallOption) (Lldp_ListInterfaceNeighborsClient, error) 94 | ListNeighbors(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (Lldp_ListNeighborsClient, error) 95 | } 96 | 97 | type lldpClient struct { 98 | cc *grpc.ClientConn 99 | } 100 | 101 | func NewLldpClient(cc *grpc.ClientConn) LldpClient { 102 | return &lldpClient{cc} 103 | } 104 | 105 | func (c *lldpClient) ListInterfaces(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (Lldp_ListInterfacesClient, error) { 106 | stream, err := grpc.NewClientStream(ctx, &_Lldp_serviceDesc.Streams[0], c.cc, "/lldp.Lldp/ListInterfaces", opts...) 107 | if err != nil { 108 | return nil, err 109 | } 110 | x := &lldpListInterfacesClient{stream} 111 | if err := x.ClientStream.SendMsg(in); err != nil { 112 | return nil, err 113 | } 114 | if err := x.ClientStream.CloseSend(); err != nil { 115 | return nil, err 116 | } 117 | return x, nil 118 | } 119 | 120 | type Lldp_ListInterfacesClient interface { 121 | Recv() (*Interface, error) 122 | grpc.ClientStream 123 | } 124 | 125 | type lldpListInterfacesClient struct { 126 | grpc.ClientStream 127 | } 128 | 129 | func (x *lldpListInterfacesClient) Recv() (*Interface, error) { 130 | m := new(Interface) 131 | if err := x.ClientStream.RecvMsg(m); err != nil { 132 | return nil, err 133 | } 134 | return m, nil 135 | } 136 | 137 | func (c *lldpClient) GetInterfaceDetails(ctx context.Context, in *Interface, opts ...grpc.CallOption) (*Interface, error) { 138 | out := new(Interface) 139 | err := grpc.Invoke(ctx, "/lldp.Lldp/GetInterfaceDetails", in, out, c.cc, opts...) 140 | if err != nil { 141 | return nil, err 142 | } 143 | return out, nil 144 | } 145 | 146 | func (c *lldpClient) ListInterfaceNeighbors(ctx context.Context, in *Interface, opts ...grpc.CallOption) (Lldp_ListInterfaceNeighborsClient, error) { 147 | stream, err := grpc.NewClientStream(ctx, &_Lldp_serviceDesc.Streams[1], c.cc, "/lldp.Lldp/ListInterfaceNeighbors", opts...) 148 | if err != nil { 149 | return nil, err 150 | } 151 | x := &lldpListInterfaceNeighborsClient{stream} 152 | if err := x.ClientStream.SendMsg(in); err != nil { 153 | return nil, err 154 | } 155 | if err := x.ClientStream.CloseSend(); err != nil { 156 | return nil, err 157 | } 158 | return x, nil 159 | } 160 | 161 | type Lldp_ListInterfaceNeighborsClient interface { 162 | Recv() (*Neighbor, error) 163 | grpc.ClientStream 164 | } 165 | 166 | type lldpListInterfaceNeighborsClient struct { 167 | grpc.ClientStream 168 | } 169 | 170 | func (x *lldpListInterfaceNeighborsClient) Recv() (*Neighbor, error) { 171 | m := new(Neighbor) 172 | if err := x.ClientStream.RecvMsg(m); err != nil { 173 | return nil, err 174 | } 175 | return m, nil 176 | } 177 | 178 | func (c *lldpClient) ListNeighbors(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (Lldp_ListNeighborsClient, error) { 179 | stream, err := grpc.NewClientStream(ctx, &_Lldp_serviceDesc.Streams[2], c.cc, "/lldp.Lldp/ListNeighbors", opts...) 180 | if err != nil { 181 | return nil, err 182 | } 183 | x := &lldpListNeighborsClient{stream} 184 | if err := x.ClientStream.SendMsg(in); err != nil { 185 | return nil, err 186 | } 187 | if err := x.ClientStream.CloseSend(); err != nil { 188 | return nil, err 189 | } 190 | return x, nil 191 | } 192 | 193 | type Lldp_ListNeighborsClient interface { 194 | Recv() (*Neighbor, error) 195 | grpc.ClientStream 196 | } 197 | 198 | type lldpListNeighborsClient struct { 199 | grpc.ClientStream 200 | } 201 | 202 | func (x *lldpListNeighborsClient) Recv() (*Neighbor, error) { 203 | m := new(Neighbor) 204 | if err := x.ClientStream.RecvMsg(m); err != nil { 205 | return nil, err 206 | } 207 | return m, nil 208 | } 209 | 210 | // Server API for Lldp service 211 | 212 | type LldpServer interface { 213 | ListInterfaces(*EmptyMessage, Lldp_ListInterfacesServer) error 214 | GetInterfaceDetails(context.Context, *Interface) (*Interface, error) 215 | ListInterfaceNeighbors(*Interface, Lldp_ListInterfaceNeighborsServer) error 216 | ListNeighbors(*EmptyMessage, Lldp_ListNeighborsServer) error 217 | } 218 | 219 | func RegisterLldpServer(s *grpc.Server, srv LldpServer) { 220 | s.RegisterService(&_Lldp_serviceDesc, srv) 221 | } 222 | 223 | func _Lldp_ListInterfaces_Handler(srv interface{}, stream grpc.ServerStream) error { 224 | m := new(EmptyMessage) 225 | if err := stream.RecvMsg(m); err != nil { 226 | return err 227 | } 228 | return srv.(LldpServer).ListInterfaces(m, &lldpListInterfacesServer{stream}) 229 | } 230 | 231 | type Lldp_ListInterfacesServer interface { 232 | Send(*Interface) error 233 | grpc.ServerStream 234 | } 235 | 236 | type lldpListInterfacesServer struct { 237 | grpc.ServerStream 238 | } 239 | 240 | func (x *lldpListInterfacesServer) Send(m *Interface) error { 241 | return x.ServerStream.SendMsg(m) 242 | } 243 | 244 | func _Lldp_GetInterfaceDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 245 | in := new(Interface) 246 | if err := dec(in); err != nil { 247 | return nil, err 248 | } 249 | if interceptor == nil { 250 | return srv.(LldpServer).GetInterfaceDetails(ctx, in) 251 | } 252 | info := &grpc.UnaryServerInfo{ 253 | Server: srv, 254 | FullMethod: "/lldp.Lldp/GetInterfaceDetails", 255 | } 256 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 257 | return srv.(LldpServer).GetInterfaceDetails(ctx, req.(*Interface)) 258 | } 259 | return interceptor(ctx, in, info, handler) 260 | } 261 | 262 | func _Lldp_ListInterfaceNeighbors_Handler(srv interface{}, stream grpc.ServerStream) error { 263 | m := new(Interface) 264 | if err := stream.RecvMsg(m); err != nil { 265 | return err 266 | } 267 | return srv.(LldpServer).ListInterfaceNeighbors(m, &lldpListInterfaceNeighborsServer{stream}) 268 | } 269 | 270 | type Lldp_ListInterfaceNeighborsServer interface { 271 | Send(*Neighbor) error 272 | grpc.ServerStream 273 | } 274 | 275 | type lldpListInterfaceNeighborsServer struct { 276 | grpc.ServerStream 277 | } 278 | 279 | func (x *lldpListInterfaceNeighborsServer) Send(m *Neighbor) error { 280 | return x.ServerStream.SendMsg(m) 281 | } 282 | 283 | func _Lldp_ListNeighbors_Handler(srv interface{}, stream grpc.ServerStream) error { 284 | m := new(EmptyMessage) 285 | if err := stream.RecvMsg(m); err != nil { 286 | return err 287 | } 288 | return srv.(LldpServer).ListNeighbors(m, &lldpListNeighborsServer{stream}) 289 | } 290 | 291 | type Lldp_ListNeighborsServer interface { 292 | Send(*Neighbor) error 293 | grpc.ServerStream 294 | } 295 | 296 | type lldpListNeighborsServer struct { 297 | grpc.ServerStream 298 | } 299 | 300 | func (x *lldpListNeighborsServer) Send(m *Neighbor) error { 301 | return x.ServerStream.SendMsg(m) 302 | } 303 | 304 | var _Lldp_serviceDesc = grpc.ServiceDesc{ 305 | ServiceName: "lldp.Lldp", 306 | HandlerType: (*LldpServer)(nil), 307 | Methods: []grpc.MethodDesc{ 308 | { 309 | MethodName: "GetInterfaceDetails", 310 | Handler: _Lldp_GetInterfaceDetails_Handler, 311 | }, 312 | }, 313 | Streams: []grpc.StreamDesc{ 314 | { 315 | StreamName: "ListInterfaces", 316 | Handler: _Lldp_ListInterfaces_Handler, 317 | ServerStreams: true, 318 | }, 319 | { 320 | StreamName: "ListInterfaceNeighbors", 321 | Handler: _Lldp_ListInterfaceNeighbors_Handler, 322 | ServerStreams: true, 323 | }, 324 | { 325 | StreamName: "ListNeighbors", 326 | Handler: _Lldp_ListNeighbors_Handler, 327 | ServerStreams: true, 328 | }, 329 | }, 330 | Metadata: fileDescriptor0, 331 | } 332 | 333 | func init() { proto.RegisterFile("lldp.proto", fileDescriptor0) } 334 | 335 | var fileDescriptor0 = []byte{ 336 | // 328 bytes of a gzipped FileDescriptorProto 337 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x52, 0xcd, 0x4e, 0xf3, 0x30, 338 | 0x10, 0xfc, 0xf2, 0x35, 0x69, 0xe9, 0xaa, 0x3f, 0x68, 0x41, 0x95, 0xc5, 0x09, 0xe5, 0xd4, 0x53, 339 | 0x85, 0xe0, 0x80, 0x7a, 0xe0, 0x06, 0x42, 0x48, 0x85, 0x43, 0xde, 0xc0, 0xad, 0xb7, 0xad, 0xa5, 340 | 0x34, 0xb1, 0x6c, 0xf3, 0xd3, 0x23, 0xaf, 0xc6, 0x33, 0xf1, 0x00, 0xc8, 0x76, 0x9a, 0xfe, 0x28, 341 | 0xb7, 0x99, 0xd9, 0x19, 0x7b, 0x92, 0x35, 0x40, 0x9e, 0x0b, 0x35, 0x51, 0xba, 0xb4, 0x25, 0xc6, 342 | 0x0e, 0xa7, 0x03, 0xe8, 0x3d, 0x6d, 0x94, 0xdd, 0xbe, 0x92, 0x31, 0x7c, 0x45, 0xe9, 0x4f, 0x04, 343 | 0x67, 0x6f, 0x24, 0x57, 0xeb, 0x79, 0xa9, 0x71, 0x04, 0x6d, 0x55, 0x6a, 0x2b, 0x05, 0x8b, 0xae, 344 | 0xa3, 0x71, 0x37, 0xab, 0x18, 0x8e, 0x61, 0xe8, 0x90, 0x20, 0xb3, 0xd0, 0x52, 0x59, 0x59, 0x16, 345 | 0xec, 0xbf, 0x37, 0x9c, 0xca, 0xc8, 0xa0, 0x63, 0xb6, 0xa6, 0xe0, 0x1b, 0x62, 0x2d, 0xef, 0xd8, 346 | 0xd1, 0x6a, 0xe2, 0xbc, 0x2c, 0xae, 0x27, 0x8e, 0xba, 0x09, 0x17, 0x42, 0x93, 0x31, 0x2c, 0x09, 347 | 0x93, 0x8a, 0x22, 0x42, 0xfc, 0x91, 0xf3, 0x82, 0xb5, 0xbd, 0xec, 0xb1, 0xd3, 0xec, 0x56, 0x11, 348 | 0xeb, 0x04, 0xcd, 0xe1, 0xf4, 0x3b, 0x82, 0xee, 0x4b, 0x61, 0x49, 0x2f, 0xf9, 0x82, 0xf0, 0x12, 349 | 0x12, 0x59, 0x08, 0xfa, 0xf2, 0x1f, 0x91, 0x64, 0x81, 0xe0, 0x39, 0xb4, 0x36, 0xf6, 0xdd, 0xf7, 350 | 0x4e, 0x32, 0x07, 0xdd, 0x49, 0x07, 0x45, 0x3d, 0xc6, 0x14, 0x7a, 0x6b, 0xae, 0xc5, 0x27, 0xd7, 351 | 0xe4, 0x4a, 0x54, 0x55, 0x8f, 0x34, 0x77, 0xfe, 0x32, 0xe7, 0xab, 0xd0, 0xb6, 0x9f, 0x05, 0x72, 352 | 0xfb, 0x1b, 0x41, 0x3c, 0xcb, 0x85, 0xc2, 0x29, 0x0c, 0x66, 0xd2, 0xd8, 0xba, 0x8f, 0x41, 0x9c, 353 | 0xf8, 0x35, 0x1c, 0xfe, 0xf7, 0xab, 0x61, 0xd0, 0x6a, 0x57, 0xfa, 0xef, 0x26, 0xc2, 0x29, 0x5c, 354 | 0x3c, 0xd3, 0x3e, 0xf9, 0x48, 0x96, 0xcb, 0xdc, 0xe0, 0xa9, 0xb7, 0x21, 0x8c, 0x0f, 0x30, 0x3a, 355 | 0xba, 0x75, 0xb7, 0xd3, 0x86, 0xf4, 0x20, 0x08, 0x3b, 0x87, 0xbf, 0xf9, 0x1e, 0xfa, 0x2e, 0xbe, 356 | 0x4f, 0x35, 0x75, 0x6e, 0x08, 0xce, 0xdb, 0xfe, 0x71, 0xdd, 0xfd, 0x05, 0x00, 0x00, 0xff, 0xff, 357 | 0x15, 0x5e, 0x19, 0x0d, 0x6a, 0x02, 0x00, 0x00, 358 | } 359 | --------------------------------------------------------------------------------