From 12e8ef51c169370f31bef28abe85009f7d765bf3 Mon Sep 17 00:00:00 2001 From: fg-admin Date: Wed, 25 Jan 2023 14:25:58 +0300 Subject: [PATCH] Initial commit --- .gitignore | 2 + LICENSE | 202 +++++++++++++++++++++++++ README.md | 12 ++ cmd/inventory/agent.go | 12 ++ cmd/inventory/agent/agent.go | 36 +++++ cmd/inventory/bootstrap.go | 12 ++ cmd/inventory/bootstrap/bootstrap.go | 17 +++ cmd/inventory/main.go | 7 + cmd/inventory/main_test.go | 13 ++ cmd/inventory/root.go | 90 +++++++++++ go.mod | 58 +++++++ go.sum | 152 +++++++++++++++++++ pkg/agent/agent.go | 157 +++++++++++++++++++ pkg/agent/baseboard.go | 38 +++++ pkg/agent/bios.go | 37 +++++ pkg/agent/cpu.go | 37 +++++ pkg/agent/dimm.go | 53 +++++++ pkg/agent/entrypoint.go | 82 ++++++++++ pkg/agent/netlink.go | 91 +++++++++++ pkg/agent/node.go | 118 +++++++++++++++ pkg/agent/os.go | 37 +++++ pkg/agent/query.go | 38 +++++ pkg/agent/signal.go | 35 +++++ pkg/agent/types/baseboard/baseboard.go | 34 +++++ pkg/agent/types/bios/bios.go | 28 ++++ pkg/agent/types/cpu/cpu.go | 23 +++ pkg/agent/types/mem/inventory.go | 54 +++++++ pkg/agent/types/mem/mem.go | 25 +++ pkg/agent/types/netlink/inventory.go | 155 +++++++++++++++++++ pkg/agent/types/netlink/link.go | 68 +++++++++ pkg/agent/types/node/node.go | 50 ++++++ pkg/agent/types/os/os.go | 31 ++++ pkg/agent/types/types.go | 37 +++++ pkg/agent/utils.go | 70 +++++++++ pkg/agent/worker.go | 126 +++++++++++++++ pkg/bootstrap/bootstrap.go | 56 +++++++ pkg/bootstrap/functions.go | 33 ++++ pkg/bootstrap/links.go | 91 +++++++++++ pkg/bootstrap/nodes.go | 77 ++++++++++ pkg/bootstrap/types.go | 108 +++++++++++++ pkg/utils/driver/driver.go | 48 ++++++ pkg/utils/ipmi/tool.go | 163 ++++++++++++++++++++ pkg/utils/ipmitool.go | 20 +++ pkg/utils/pid/file.go | 54 +++++++ pkg/utils/sys.go | 106 +++++++++++++ 45 files changed, 2793 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/inventory/agent.go create mode 100644 cmd/inventory/agent/agent.go create mode 100644 cmd/inventory/bootstrap.go create mode 100644 cmd/inventory/bootstrap/bootstrap.go create mode 100644 cmd/inventory/main.go create mode 100644 cmd/inventory/main_test.go create mode 100644 cmd/inventory/root.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/agent/agent.go create mode 100644 pkg/agent/baseboard.go create mode 100644 pkg/agent/bios.go create mode 100644 pkg/agent/cpu.go create mode 100644 pkg/agent/dimm.go create mode 100644 pkg/agent/entrypoint.go create mode 100644 pkg/agent/netlink.go create mode 100644 pkg/agent/node.go create mode 100644 pkg/agent/os.go create mode 100644 pkg/agent/query.go create mode 100644 pkg/agent/signal.go create mode 100644 pkg/agent/types/baseboard/baseboard.go create mode 100644 pkg/agent/types/bios/bios.go create mode 100644 pkg/agent/types/cpu/cpu.go create mode 100644 pkg/agent/types/mem/inventory.go create mode 100644 pkg/agent/types/mem/mem.go create mode 100644 pkg/agent/types/netlink/inventory.go create mode 100644 pkg/agent/types/netlink/link.go create mode 100644 pkg/agent/types/node/node.go create mode 100644 pkg/agent/types/os/os.go create mode 100644 pkg/agent/types/types.go create mode 100644 pkg/agent/utils.go create mode 100644 pkg/agent/worker.go create mode 100644 pkg/bootstrap/bootstrap.go create mode 100644 pkg/bootstrap/functions.go create mode 100644 pkg/bootstrap/links.go create mode 100644 pkg/bootstrap/nodes.go create mode 100644 pkg/bootstrap/types.go create mode 100644 pkg/utils/driver/driver.go create mode 100644 pkg/utils/ipmi/tool.go create mode 100644 pkg/utils/ipmitool.go create mode 100644 pkg/utils/pid/file.go create mode 100644 pkg/utils/sys.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35c6455 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/inventory +/cmd/inventory/inventory diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + 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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ecfe729 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Inventory Application + +Environmental objects inventory application + +## run + +### boottstrap +```inventory bootstrap``` + +### agent +```inventory agent run``` + diff --git a/cmd/inventory/agent.go b/cmd/inventory/agent.go new file mode 100644 index 0000000..4b441a8 --- /dev/null +++ b/cmd/inventory/agent.go @@ -0,0 +1,12 @@ +// Copyright 2022 Listware + +package main + +import ( + "git.fg-tech.ru/listware/inventory-app/cmd/inventory/agent" +) + +// runCmd represents the run command +func init() { + rootCmd.AddCommand(agent.RootCmd) +} diff --git a/cmd/inventory/agent/agent.go b/cmd/inventory/agent/agent.go new file mode 100644 index 0000000..09b2b85 --- /dev/null +++ b/cmd/inventory/agent/agent.go @@ -0,0 +1,36 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/agent" + "git.fg-tech.ru/listware/inventory-app/pkg/utils/pid" + "github.com/spf13/cobra" +) + +var pidfile = pid.File("/var/run/exmt.pid") + +var RootCmd = &cobra.Command{ + Use: "agent", + Short: "Extended Management Agent", +} + +// agentCmd represents the agent command +var agentRunCmd = &cobra.Command{ + Use: "run", + Short: "Run Extended Management Agent", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if err := pidfile.Write(); err != nil { + return err + } + defer pidfile.Remove() + + return agent.Run() + }, +} + +// runCmd represents the run command +func init() { + RootCmd.AddCommand(agentRunCmd) +} diff --git a/cmd/inventory/bootstrap.go b/cmd/inventory/bootstrap.go new file mode 100644 index 0000000..ad545ec --- /dev/null +++ b/cmd/inventory/bootstrap.go @@ -0,0 +1,12 @@ +// Copyright 2022 Listware + +package main + +import ( + "git.fg-tech.ru/listware/inventory-app/cmd/inventory/bootstrap" +) + +// runCmd represents the run command +func init() { + rootCmd.AddCommand(bootstrap.RootCmd) +} diff --git a/cmd/inventory/bootstrap/bootstrap.go b/cmd/inventory/bootstrap/bootstrap.go new file mode 100644 index 0000000..c88d8b7 --- /dev/null +++ b/cmd/inventory/bootstrap/bootstrap.go @@ -0,0 +1,17 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/bootstrap" + "github.com/spf13/cobra" +) + +var RootCmd = &cobra.Command{ + Use: "bootstrap", + Short: "Extended Management Agent Boostrap", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) (err error) { + return bootstrap.Run() + }, +} diff --git a/cmd/inventory/main.go b/cmd/inventory/main.go new file mode 100644 index 0000000..ee79c13 --- /dev/null +++ b/cmd/inventory/main.go @@ -0,0 +1,7 @@ +// Copyright 2022 Listware + +package main + +func main() { + execute() +} diff --git a/cmd/inventory/main_test.go b/cmd/inventory/main_test.go new file mode 100644 index 0000000..a030797 --- /dev/null +++ b/cmd/inventory/main_test.go @@ -0,0 +1,13 @@ +// Copyright 2022 Listware + +package main + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(t *testing.T) { + defer goleak.VerifyNone(t) +} diff --git a/cmd/inventory/root.go b/cmd/inventory/root.go new file mode 100644 index 0000000..11d7f2c --- /dev/null +++ b/cmd/inventory/root.go @@ -0,0 +1,90 @@ +// Copyright 2022 Listware + +package main + +import ( + "encoding/json" + "fmt" + "os" + + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/spf13/cobra" +) + +var ( + version = "v0.1.0" + release = "dev" + versionTemplate = `{{printf "%s Agent" .Short}} +{{printf "Version: %s" .Version}} +Release: ` + release + ` +` +) + +var configFile string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "exmt", + Short: "Extended Management", + Long: `Coming soon...`, + Version: version, + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + // cobra.OnInitialize(initConfig) + rootCmd.SetVersionTemplate(versionTemplate) + rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "config file (default is ~/.exmt.yaml)") + rootCmd.AddCommand(autoShellCmd, testCmd) +} + +/* +// initConfig reads in config file and ENV variables if set. + + func initConfig() { + if configFile == "" { + if home, err := homedir.Dir(); err == nil { + configFile = path.Join(home, ".exmt.yaml") + } + } + profile.SetConfigFile(configFile) + dir, err := filepath.Abs(filepath.Dir(configFile)) + if err != nil { + fmt.Println(err) + } + os.MkdirAll(dir, 0600) + profile.ReadInConfig() + } +*/ +var autoShellCmd = &cobra.Command{ + Use: "autoshell", + Short: "Generate bash completion script", + Long: "Generate bash completion script", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + err := cmd.Root().GenBashCompletionFile("/etc/bash_completion.d/exmt.sh") + if err != nil { + return err + } + return nil + }, +} + +var testCmd = &cobra.Command{ + Use: "test", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + utils.Init() + err = json.NewEncoder(os.Stdout).Encode(utils.Sys) + return + }, +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2245fe2 --- /dev/null +++ b/go.mod @@ -0,0 +1,58 @@ +module git.fg-tech.ru/listware/inventory-app + +go 1.19 + +require ( + git.fg-tech.ru/listware/cmdb v0.1.0 + git.fg-tech.ru/listware/go-core v0.1.0 + git.fg-tech.ru/listware/proto v0.1.1 + git.fg-tech.ru/listware/proxy v0.1.0 + github.com/shirou/gopsutil v3.21.11+incompatible + github.com/sirupsen/logrus v1.9.0 + github.com/spf13/cobra v1.6.1 + github.com/vishvananda/netlink v1.1.0 + github.com/yumaojun03/dmidecode v0.1.4 + github.com/zcalusic/sysinfo v0.9.5 + go.uber.org/goleak v1.2.0 +) + +require ( + github.com/Shopify/sarama v1.38.1 // indirect + github.com/apache/flink-statefun/statefun-sdk-go/v3 v3.2.0 // indirect + github.com/arangodb/go-driver v1.4.1 // indirect + github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect + github.com/eapache/go-resiliency v1.3.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/klauspost/compress v1.15.14 // indirect + github.com/pierrec/lz4/v4 v4.1.17 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/net v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect + google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect + google.golang.org/grpc v1.52.1 // indirect + google.golang.org/protobuf v1.28.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..03ff157 --- /dev/null +++ b/go.sum @@ -0,0 +1,152 @@ +git.fg-tech.ru/listware/cmdb v0.1.0 h1:K4A8XI8X5nh975+m+LVea/kIa2hSQSXCnH3qVjDjOwk= +git.fg-tech.ru/listware/cmdb v0.1.0/go.mod h1:kke3p3xl6lPlqwSfmA+grlKhGX6zKXctjcbE1Pcs9Ik= +git.fg-tech.ru/listware/go-core v0.1.0 h1:ftwr4VjmFSHfCqPQFTi2rcpELVznYBUPmwRbKMm/OGQ= +git.fg-tech.ru/listware/go-core v0.1.0/go.mod h1:jKjqdeZUtlNoSFSSHSmiw0fAy4FWeYhLqqRvLU5K4t4= +git.fg-tech.ru/listware/proto v0.1.1 h1:CSqteAtgysiJe7+KtLOEXSIvxypmlJCKwQtla1d2v+A= +git.fg-tech.ru/listware/proto v0.1.1/go.mod h1:t5lyMTuX/if05HI/na9tJAlHCHHMdhdPLBTkhvscedQ= +git.fg-tech.ru/listware/proxy v0.1.0 h1:rQyaa894Pt0G1psNA/Li3znKZ2ol+4nX0yfi3EGPxwY= +git.fg-tech.ru/listware/proxy v0.1.0/go.mod h1:bD0eR5gv4Dy4DV9Pp2dmHEIwrY476dQ+53/MB5xsnWw= +github.com/Shopify/sarama v1.38.1 h1:lqqPUPQZ7zPqYlWpTh+LQ9bhYNu2xJL6k1SJN4WVe2A= +github.com/Shopify/sarama v1.38.1/go.mod h1:iwv9a67Ha8VNa+TifujYoWGxWnu2kNVAQdSdZ4X2o5g= +github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= +github.com/apache/flink-statefun/statefun-sdk-go/v3 v3.2.0 h1:OfLhhWnnOfBUvzbQuhE7hCKJdlBW41nV3CfCF/q7UJs= +github.com/apache/flink-statefun/statefun-sdk-go/v3 v3.2.0/go.mod h1:uHiPJsi71a161NMH/ISkkSPIXenkcG9A2m+uhT8UlJ4= +github.com/arangodb/go-driver v1.4.1 h1:Jg0N7XKxiKwjswmAcMCnefWmt81KJEqybqRAGJDRWlo= +github.com/arangodb/go-driver v1.4.1/go.mod h1:UTtaxTUMmyPWzKc2dsWWOZzZ3yM6aHWxn/eubGa3YmQ= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2LcQBbxd0ZFdbGSyRKTYMZCfBbw/pMJFOk1g= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q= +github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A= +github.com/eapache/go-resiliency v1.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0= +github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6 h1:8yY/I9ndfrgrXUbOGObLHKBR4Fl3nZXwM2c7OYTT8hM= +github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE/Tq8= +github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc= +github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/yumaojun03/dmidecode v0.1.4 h1:ZmHBHs5sE5z1clgpry9tmuWeo2FBFi018MiSAWr+5n8= +github.com/yumaojun03/dmidecode v0.1.4/go.mod h1:34bbsMNMNjDbijDpRuqd+2ZapDKxvhO+FlgGgOgS6G8= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zcalusic/sysinfo v0.9.5 h1:ivoHyj9aIAYkwzo1+8QgJ5s4oeE6Etx9FmZtqa4wJjQ= +github.com/zcalusic/sysinfo v0.9.5/go.mod h1:Z/gPVufBrFc8X5sef3m6kkw3r3nlNFp+I6bvASfvBZQ= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/grpc v1.52.1 h1:2NpOPk5g5Xtb0qebIEs7hNIa++PdtZLo2AQUpc1YnSU= +google.golang.org/grpc v1.52.1/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go new file mode 100644 index 0000000..5822e9b --- /dev/null +++ b/pkg/agent/agent.go @@ -0,0 +1,157 @@ +// Copyright 2022 Listware + +package agent + +import ( + "context" + "strings" + + "git.fg-tech.ru/listware/go-core/pkg/executor" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/baseboard" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/bios" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/cpu" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/mem" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/netlink" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/node" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/os" + proxy "git.fg-tech.ru/listware/proxy/pkg/module" + "github.com/sirupsen/logrus" +) + +var ( + log = logrus.New() +) + +type Agent struct { + ctx context.Context + cancel context.CancelFunc + + // init values + baseboard baseboard.Baseboard + bios bios.BIOS + cpu cpu.CPU + node node.Node + os os.OS + + dimmDevs map[string]mem.MemoryDevice + links map[string]netlink.Netlink + + executor executor.Executor + + m module.Module +} + +// Run agent +func Run() (err error) { + a := &Agent{} + a.ctx, a.cancel = context.WithCancel(context.Background()) + + if a.executor, err = executor.New(); err != nil { + return + } + + if a.baseboard, err = baseboard.New(); err != nil { + return + } + + if a.bios, err = bios.New(); err != nil { + return + } + + if a.cpu, err = cpu.New(); err != nil { + return + } + + if a.node, err = node.New(); err != nil { + return + } + + if a.os, err = os.New(); err != nil { + return + } + + if a.dimmDevs, err = mem.Inventory(); err != nil { + return + } + + if a.links, err = netlink.New(); err != nil { + return + } + + return a.run() +} + +func appendPath(paths ...string) string { + return strings.Join(paths, ".") +} + +func (a *Agent) hostname() string { + return a.node.Hostname +} + +func (a *Agent) nodepath() string { + return appendPath(a.hostname(), types.NodeContainerPath) +} + +func (a *Agent) baseboardpath() string { + return appendPath(types.BaseboardLink, a.nodepath()) +} + +func (a *Agent) biospath() string { + return appendPath(types.BiosLink, a.nodepath()) +} + +func (a *Agent) cpupath() string { + return appendPath(types.CpuLink, a.nodepath()) +} + +func (a *Agent) ospath() string { + return appendPath(types.OsLink, a.nodepath()) +} + +func (a *Agent) dimmpath(dev string) string { + return appendPath(dev, a.nodepath()) +} + +func (a *Agent) dimmspath() string { + return appendPath(memMask, a.nodepath()) +} + +func (a *Agent) netlinkpath(os string) string { + return appendPath(os, a.nodepath()) +} + +func (a *Agent) netlinkspath() string { + return appendPath(netlinkMask, a.nodepath()) +} + +func (a *Agent) run() (err error) { + defer a.executor.Close() + + log.Infof("run system agent") + + a.osSignalCtrl() + + a.m = proxy.New(namespace, module.WithPort(8181)) + + if err = a.m.Bind(types.FunctionPath, a.workerFunction); err != nil { + return + } + + // // TODO move to another app + // if err = a.m.Bind("monit", monit.Monit); err != nil { + // return + // } + + go a.m.RegisterAndListen(a.ctx) + + if err = a.entrypoint(); err != nil { + return + } + + <-a.ctx.Done() + + return nil +} diff --git a/pkg/agent/baseboard.go b/pkg/agent/baseboard.go new file mode 100644 index 0000000..4b39a16 --- /dev/null +++ b/pkg/agent/baseboard.go @@ -0,0 +1,38 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) createBaseboard(ctx module.Context) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.BaseboardID, types.BaseboardLink, a.baseboard) + if err != nil { + return + } + + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateBaseboard(ctx module.Context, uuid string) (err error) { + update, err := system.UpdateObject(uuid, a.baseboard) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/bios.go b/pkg/agent/bios.go new file mode 100644 index 0000000..7ef71e7 --- /dev/null +++ b/pkg/agent/bios.go @@ -0,0 +1,37 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) createBios(ctx module.Context) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.BiosID, types.BiosLink, a.bios) + if err != nil { + return + } + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateBios(ctx module.Context, uuid string) (err error) { + update, err := system.UpdateObject(uuid, a.bios) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/cpu.go b/pkg/agent/cpu.go new file mode 100644 index 0000000..eebd043 --- /dev/null +++ b/pkg/agent/cpu.go @@ -0,0 +1,37 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) createCpu(ctx module.Context) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.CpuID, types.CpuLink, a.cpu) + if err != nil { + return + } + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateCpu(ctx module.Context, uuid string) (err error) { + update, err := system.UpdateObject(uuid, a.cpu) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/dimm.go b/pkg/agent/dimm.go new file mode 100644 index 0000000..f61bcfe --- /dev/null +++ b/pkg/agent/dimm.go @@ -0,0 +1,53 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) createDimms(ctx module.Context) (err error) { + for _, dev := range a.dimmDevs { + dimmFunc, err := genDimmFunction(ctx.Self().Id, a.dimmpath(dev.Name()), dev.Name()) + if err != nil { + return err + } + msg, err := module.ToMessage(dimmFunc) + if err != nil { + return err + } + + ctx.Send(msg) + } + return +} + +func (a *Agent) createDimm(ctx module.Context, name string) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.MemoryID, name, a.dimmDevs[name]) + if err != nil { + return + } + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateDimm(ctx module.Context, id, name string) (err error) { + update, err := system.UpdateObject(id, a.dimmDevs[name]) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/entrypoint.go b/pkg/agent/entrypoint.go new file mode 100644 index 0000000..04d7c7c --- /dev/null +++ b/pkg/agent/entrypoint.go @@ -0,0 +1,82 @@ +// Copyright 2022 Listware + +package agent + +import ( + "fmt" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/proto/sdk/pbtypes" +) + +func (a *Agent) createLink(route *pbtypes.FunctionRoute) (err error) { + function, err := a.getFunction() + if err != nil { + return + } + + node, err := a.getNode() + if err != nil { + return + } + + // create init link, will trigger 'init' + createLink, err := system.CreateLink(function.Id.String(), node.Id.String(), node.Key, function.Type, route) + if err != nil { + return err + } + + return a.executor.ExecSync(a.ctx, createLink) +} + +func (a *Agent) entrypoint() (err error) { + route := &pbtypes.FunctionRoute{ + Url: a.m.Addr(), + } + + // exists node or create + node, err := a.getNode() + if err == nil { + // search function + documents, err := qdsl.Qdsl(a.ctx, fmt.Sprintf("%s.%s", node.Key, types.FunctionPath), qdsl.WithLinkId()) + if err != nil { + return err + } + + // func on node: exists + for _, document := range documents { + updateLink, err := system.UpdateAdvancedLink(document.LinkId.String(), route) + if err != nil { + return err + } + + return a.executor.ExecSync(a.ctx, updateLink) + } + + return a.createLink(route) + } + + // TODO move to register + nodes, err := a.getNodes() + if err != nil { + return + } + + message := &pbtypes.FunctionMessage{ + Type: types.FunctionPath, + Route: route, + } + + createNode, err := system.CreateChild(nodes.Id.String(), types.NodeID, a.hostname(), a.node, message) + if err != nil { + return + } + + if err = a.executor.ExecSync(a.ctx, createNode); err != nil { + return err + } + + return a.createLink(route) +} diff --git a/pkg/agent/netlink.go b/pkg/agent/netlink.go new file mode 100644 index 0000000..38dc791 --- /dev/null +++ b/pkg/agent/netlink.go @@ -0,0 +1,91 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/netlink" +) + +func (a *Agent) netlink(uuid string) { + updateChan, deleteChan := netlink.Subscribe(a.ctx) + + for { + select { + case link := <-updateChan: + netlinkFunc, err := genNetlinkFunction(uuid, a.netlinkpath(link.LinkName()), link, updateEvent) + if err != nil { + log.Error(err) + continue + } + + if err = a.executor.ExecAsync(a.ctx, netlinkFunc); err != nil { + log.Error(err) + continue + } + + case link := <-deleteChan: + netlinkFunc, err := genNetlinkFunction(uuid, a.netlinkpath(link.LinkName()), link, deleteEvent) + if err != nil { + log.Error(err) + continue + } + + if err = a.executor.ExecAsync(a.ctx, netlinkFunc); err != nil { + log.Error(err) + continue + } + + case <-a.ctx.Done(): + return + } + } +} + +func (a *Agent) createNetlinks(ctx module.Context) (err error) { + for _, link := range a.links { + netlinkFunc, err := genNetlinkFunction(ctx.Self().Id, a.netlinkpath(link.LinkName()), link, updateEvent) + if err != nil { + return err + } + msg, err := module.ToMessage(netlinkFunc) + if err != nil { + return err + } + + ctx.Send(msg) + } + + go a.netlink(ctx.Self().Id) + return +} + +func (a *Agent) createNetlink(ctx module.Context, link *netlink.Netlink) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.NetlinkID, link.LinkName(), link) + if err != nil { + return + } + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateNetlink(ctx module.Context, uuid string, link netlink.Netlink) (err error) { + update, err := system.UpdateObject(uuid, link) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/node.go b/pkg/agent/node.go new file mode 100644 index 0000000..65e6963 --- /dev/null +++ b/pkg/agent/node.go @@ -0,0 +1,118 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" +) + +// readNode generate and exec worker("nodepath") func +func (a *Agent) readNode(ctx module.Context, id string) (err error) { + nodeFunc, err := genFunction(id, a.nodepath()) + if err != nil { + return + } + msg, err := module.ToMessage(nodeFunc) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +// readNode generate and exec worker("baseboard", "cpu", etc...) funcs +func (a *Agent) readChilds(ctx module.Context, id string) (err error) { + update, err := system.UpdateObject(id, a.node) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + + baseboardFunc, err := genFunction(id, a.baseboardpath()) + if err != nil { + return + } + msg, err = module.ToMessage(baseboardFunc) + if err != nil { + return + } + + ctx.Send(msg) + + osFunc, err := genFunction(id, a.ospath()) + if err != nil { + return + } + msg, err = module.ToMessage(osFunc) + if err != nil { + return + } + + ctx.Send(msg) + + biosFunc, err := genFunction(id, a.biospath()) + if err != nil { + return + } + msg, err = module.ToMessage(biosFunc) + if err != nil { + return + } + + ctx.Send(msg) + + cpuFunc, err := genFunction(id, a.cpupath()) + if err != nil { + return + } + msg, err = module.ToMessage(cpuFunc) + if err != nil { + return + } + + ctx.Send(msg) + + dimmFunc, err := genFunction(id, a.dimmspath()) + if err != nil { + return + } + msg, err = module.ToMessage(dimmFunc) + if err != nil { + return + } + + ctx.Send(msg) + + netlinkFunc, err := genFunction(id, a.netlinkspath()) + if err != nil { + return + } + msg, err = module.ToMessage(netlinkFunc) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) deleteObject(ctx module.Context, uuid string) (err error) { + del, err := system.DeleteObject(uuid) + if err != nil { + return + } + msg, err := module.ToMessage(del) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/os.go b/pkg/agent/os.go new file mode 100644 index 0000000..7bbceaf --- /dev/null +++ b/pkg/agent/os.go @@ -0,0 +1,37 @@ +// Copyright 2022 Listware + +package agent + +import ( + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) createOs(ctx module.Context) (err error) { + create, err := system.CreateChild(ctx.Self().Id, types.OsID, types.OsLink, a.os) + if err != nil { + return + } + msg, err := module.ToMessage(create) + if err != nil { + return + } + + ctx.Send(msg) + return +} + +func (a *Agent) updateOs(ctx module.Context, uuid string) (err error) { + update, err := system.UpdateObject(uuid, a.os) + if err != nil { + return + } + msg, err := module.ToMessage(update) + if err != nil { + return + } + + ctx.Send(msg) + return +} diff --git a/pkg/agent/query.go b/pkg/agent/query.go new file mode 100644 index 0000000..459cfe9 --- /dev/null +++ b/pkg/agent/query.go @@ -0,0 +1,38 @@ +// Copyright 2022 Listware + +package agent + +import ( + "fmt" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/documents" + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) getDocument(query string) (document *documents.Node, err error) { + documents, err := qdsl.Qdsl(a.ctx, query, qdsl.WithKey(), qdsl.WithId(), qdsl.WithType()) + if err != nil { + return + } + for _, document = range documents { + return + } + err = fmt.Errorf("document '%s' not found", query) + return +} + +func (a *Agent) getFunction() (document *documents.Node, err error) { + // search function_type init 'init.exmt.functions.root' + return a.getDocument(types.FunctionPath) +} + +func (a *Agent) getNode() (document *documents.Node, err error) { + // search function_type init 'dev0.nodes.root' + return a.getDocument(a.nodepath()) +} + +func (a *Agent) getNodes() (document *documents.Node, err error) { + // search 'nodes.root' + return a.getDocument(types.NodeContainerPath) +} diff --git a/pkg/agent/signal.go b/pkg/agent/signal.go new file mode 100644 index 0000000..36d262f --- /dev/null +++ b/pkg/agent/signal.go @@ -0,0 +1,35 @@ +// Copyright 2022 Listware + +package agent + +import ( + "os" + "os/signal" + "syscall" +) + +func (a *Agent) osSignalCtrl() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT, + syscall.SIGHUP, + syscall.SIGUSR1, + syscall.SIGUSR2, + ) + go func() { + for { + select { + case sig := <-sigChan: + switch sig { + case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT: + log.Infof("Get Stop signal") + a.cancel() + } + case <-a.ctx.Done(): + return + } + } + }() +} diff --git a/pkg/agent/types/baseboard/baseboard.go b/pkg/agent/types/baseboard/baseboard.go new file mode 100644 index 0000000..599d23f --- /dev/null +++ b/pkg/agent/types/baseboard/baseboard.go @@ -0,0 +1,34 @@ +// Copyright 2022 Listware + +package baseboard + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/sirupsen/logrus" +) + +// Baseboard information. +type Baseboard struct { + UUID string `json:"uuid,omitemtpy"` + Name string `json:"name,omitempty"` + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Serial string `json:"serial,omitempty"` +} + +// Inventory interface +func New() (b Baseboard, err error) { + utils.Init() + + logrus.Infof("baseboard: %s", utils.Sys.Board.Name) + + b = Baseboard{ + UUID: utils.Sys.HostID, + Name: utils.Sys.Board.Name, + Vendor: utils.Sys.Board.Vendor, + Version: utils.Sys.Board.Version, + Serial: utils.Sys.Board.Serial, + } + + return +} diff --git a/pkg/agent/types/bios/bios.go b/pkg/agent/types/bios/bios.go new file mode 100644 index 0000000..fc16cf5 --- /dev/null +++ b/pkg/agent/types/bios/bios.go @@ -0,0 +1,28 @@ +// Copyright 2022 Listware + +package bios + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/sirupsen/logrus" +) + +// BIOS information. +type BIOS struct { + Vendor string `json:"vendor,omitempty"` + Version string `json:"version,omitempty"` + Date string `json:"date,omitempty"` +} + +// Inventory interface +func New() (b BIOS, err error) { + logrus.Infof("bios: %s", utils.Sys.BIOS.Vendor) + + b = BIOS{ + Vendor: utils.Sys.BIOS.Vendor, + Version: utils.Sys.BIOS.Version, + Date: utils.Sys.BIOS.Date, + } + + return +} diff --git a/pkg/agent/types/cpu/cpu.go b/pkg/agent/types/cpu/cpu.go new file mode 100644 index 0000000..56305fa --- /dev/null +++ b/pkg/agent/types/cpu/cpu.go @@ -0,0 +1,23 @@ +// Copyright 2022 Listware + +package cpu + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/sirupsen/logrus" + "github.com/zcalusic/sysinfo" +) + +// CPU profile +type CPU struct { + sysinfo.CPU +} + +// Inventory \\ +func New() (c CPU, err error) { + logrus.Infof("cpu: %s", utils.Sys.CPU.Model) + + c = CPU{CPU: utils.Sys.CPU} + + return +} diff --git a/pkg/agent/types/mem/inventory.go b/pkg/agent/types/mem/inventory.go new file mode 100644 index 0000000..ab39a48 --- /dev/null +++ b/pkg/agent/types/mem/inventory.go @@ -0,0 +1,54 @@ +// Copyright 2022 Listware + +package mem + +import ( + "github.com/sirupsen/logrus" + "github.com/yumaojun03/dmidecode" + "github.com/yumaojun03/dmidecode/parser/memory" +) + +// Channels for get updates +var ( + log = logrus.New() +) + +func deviceToProfile(device *memory.MemoryDevice) (dev MemoryDevice) { + dev = MemoryDevice{ + Size: device.Size, + FormFactor: device.FormFactor.String(), + Device: device.DeviceLocator, + Bank: device.BankLocator, + Type: device.Type.String(), + Speed: device.Speed, + Manufacturer: device.Manufacturer, + Serial: device.SerialNumber, + Part: device.PartNumber, + } + + return +} + +// Inventory implement inventory interface +func Inventory() (devs map[string]MemoryDevice, err error) { + log.Info("inventory mem") + + devs = make(map[string]MemoryDevice) + + dmi, err := dmidecode.New() + if err != nil { + log.Warn(err) + return + } + devices, err := dmi.MemoryDevice() + if err != nil { + log.Warn(err) + return + } + + for _, device := range devices { + dev := deviceToProfile(device) + devs[dev.Name()] = dev + } + return +} diff --git a/pkg/agent/types/mem/mem.go b/pkg/agent/types/mem/mem.go new file mode 100644 index 0000000..6b07b97 --- /dev/null +++ b/pkg/agent/types/mem/mem.go @@ -0,0 +1,25 @@ +// Copyright 2022 Listware + +package mem + +import ( + "strings" +) + +// Ram module profile +type MemoryDevice struct { + Device string `json:"device,omitempty"` + Bank string `json:"bank,omitempty"` + FormFactor string `json:"form-factor,omitempty"` + Type string `json:"type,omitempty"` + + Size uint16 `json:"size,omitempty"` + Speed uint16 `json:"speed,omitempty"` + Manufacturer string `json:"manufacturer,omitempty"` + Serial string `json:"serial,omitempty"` + Part string `json:"part,omitempty"` +} + +func (m *MemoryDevice) Name() string { + return strings.ToLower(strings.ReplaceAll(m.Device, "_", "-")) +} diff --git a/pkg/agent/types/netlink/inventory.go b/pkg/agent/types/netlink/inventory.go new file mode 100644 index 0000000..f8c2f50 --- /dev/null +++ b/pkg/agent/types/netlink/inventory.go @@ -0,0 +1,155 @@ +// Copyright 2022 Listware + +package netlink + +import ( + "context" + "fmt" + "net" + "os" + "strconv" + "strings" + "syscall" + + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +var ( + log = logrus.New() +) + +func Subscribe(ctx context.Context) (updateChan, deleteChan chan Netlink) { + updateChan = make(chan Netlink, 1000) + deleteChan = make(chan Netlink, 100) + + go func() { + linkUpdateChan := make(chan netlink.LinkUpdate, 100) + if err := netlink.LinkSubscribe(linkUpdateChan, ctx.Done()); err != nil { + log.Warn(err.Error()) + return + } + for { + select { + case m := <-linkUpdateChan: + + switch m.Header.Type { + case syscall.RTM_NEWLINK: + if filterLinks(m.Link) { + log.Debugf("Handle link type: %s", m.Link.Type()) + updateChan <- netlinkToLink(m.Link) + } + case syscall.RTM_DELLINK: + if filterLinks(m.Link) { + log.Debugf("Handle link type: %s", m.Link.Type()) + deleteChan <- netlinkToLink(m.Link) + } + } + + case <-ctx.Done(): + return + } + } + + }() + + go func() { + addrUpdateChan := make(chan netlink.AddrUpdate, 100) + if err := netlink.AddrSubscribe(addrUpdateChan, ctx.Done()); err != nil { + log.Warn(err.Error()) + } + for { + select { + case m := <-addrUpdateChan: + if link, err := netlink.LinkByIndex(m.LinkIndex); err == nil { + if filterLinks(link) { + log.Debugf("Handle address changes: %s", m.LinkAddress.String()) + updateChan <- netlinkToLink(link) + } + } + case <-ctx.Done(): + return + } + } + }() + + return +} + +func New() (initLinks map[string]Netlink, err error) { + log.Info("inventory links") + + initLinks = make(map[string]Netlink) + + links, err := netlink.LinkList() + if err != nil { + return + } + + for _, link := range links { + if filterLinks(link) { + l := netlinkToLink(link) + initLinks[l.LinkName()] = l + } + } + + return +} + +// spaghetti filter +func filterLinks(link netlink.Link) bool { + if link.Type() == "veth" || link.Type() == "bridge" { + return false + } + attrs := link.Attrs() + if (attrs.Flags & net.FlagLoopback) != 0 { + return false + } + if attrs.MasterIndex != 0 { + return false + } + return true +} + +func netlinkToLink(link netlink.Link) Netlink { + attrs := link.Attrs() + l := Netlink{ + PortType: "os-def", + PortID: attrs.Index - 1, + Type: link.Type(), + MTU: attrs.MTU, + Name: attrs.Name, + MacAddr: attrs.HardwareAddr.String(), + Alias: attrs.Alias, + State: attrs.OperState.String(), + } + + if attrs.ParentIndex != 0 { + l.PortID = attrs.ParentIndex + } + switch attrs.EncapType { + case "infiniband": + l.PortType = "os-ib" + l.Type = attrs.EncapType + case "ether": + l.PortType = "os-eth" + } + if link.Type() == "vlan" { + if vlan, ok := link.(*netlink.Vlan); ok { + l.VlanID = vlan.VlanId + l.PortType = "os-vlan" + l.PortID = (attrs.ParentIndex - 1) + } + } + l.Addrs = addrs(link) + + data, err := os.ReadFile(fmt.Sprintf("/sys/class/net/%s/device/numa_node", l.Name)) + if err == nil { + l.Numa, err = strconv.Atoi(strings.TrimSuffix(string(data), "\n")) + if err != nil { + log.Warn(err) + } + } + + return l +} diff --git a/pkg/agent/types/netlink/link.go b/pkg/agent/types/netlink/link.go new file mode 100644 index 0000000..506b7e9 --- /dev/null +++ b/pkg/agent/types/netlink/link.go @@ -0,0 +1,68 @@ +// Copyright 2022 Listware + +package netlink + +import ( + "fmt" + "strings" + + "github.com/vishvananda/netlink" +) + +// Addr ... +type Addr struct { + IP string `json:"ip,omitempty"` + Mask string `json:"mask,omitempty"` + Network string `json:"network,omitempty"` +} + +// Link profile +type Netlink struct { + PortID int `json:"port-id"` + PortType string `json:"port-type"` + Type string `json:"type,omitempty"` + MTU int `json:"mtu,omitempty"` + Name string `json:"name,omitempty"` + MacAddr string `json:"mac,omitempty"` + State string `json:"state,omitempaty"` + Alias string `json:"alias,omitempty"` + VlanID int `json:"vlan-id,omitempty"` + + Numa int `json:"numa,omitempty"` + Addrs []Addr `json:"addrs,omitempty"` +} + +func (l *Netlink) LinkName() string { + if l.VlanID != 0 { + return fmt.Sprintf("%s%d", l.PortType, l.VlanID) + } + return fmt.Sprintf("%s%d", l.PortType, l.PortID) +} + +func addrs(link netlink.Link) (res []Addr) { + // Get all IPv4 addrs + addrs, _ := netlink.AddrList(link, 2) + for _, a := range addrs { + var network string + ipnet := strings.Split(a.IPNet.String(), "/") + if len(ipnet) == 2 { + network = fmt.Sprintf("%s/%s", a.IP.Mask(a.Mask).String(), ipnet[1]) + + } + addr := Addr{ + IP: a.IP.String(), + Mask: ipv4MaskString(a.Mask), + Network: network, + } + res = append(res, addr) + } + return res +} + +// Net mask in dot decimal notation +func ipv4MaskString(m []byte) string { + if len(m) != 4 { + return "" + } + return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) +} diff --git a/pkg/agent/types/node/node.go b/pkg/agent/types/node/node.go new file mode 100644 index 0000000..4059978 --- /dev/null +++ b/pkg/agent/types/node/node.go @@ -0,0 +1,50 @@ +// Copyright 2022 Listware + +package node + +import ( + "fmt" + "strings" + + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/sirupsen/logrus" +) + +// Node profile +type Node struct { + Hostname string `json:"hostname,omitempty"` + Domain string `json:"domain,omitempty"` + Model string `json:"model,omitempty"` +} + +func Name() (string, error) { + fqdn := strings.SplitN(utils.Sys.Hostname, ".", 2) + if len(fqdn) == 0 { + return "", fmt.Errorf("bad fqdn") + } + return fqdn[0], nil +} + +// Inventory interface +func New() (n Node, err error) { + logrus.Infof("node: %s", utils.Sys.Hostname) + + fqdn := strings.SplitN(utils.Sys.Hostname, ".", 2) + if len(fqdn) == 0 { + err = fmt.Errorf("bad fqdn") + return + } + var dn string + + if len(fqdn) > 1 { + dn = fqdn[1] + } + + n = Node{ + Hostname: fqdn[0], + Domain: dn, + Model: utils.Sys.Board.Name, + } + + return +} diff --git a/pkg/agent/types/os/os.go b/pkg/agent/types/os/os.go new file mode 100644 index 0000000..9121984 --- /dev/null +++ b/pkg/agent/types/os/os.go @@ -0,0 +1,31 @@ +// Copyright 2022 Listware + +package os + +import ( + "git.fg-tech.ru/listware/inventory-app/pkg/utils" + "github.com/sirupsen/logrus" +) + +// OS profile +type OS struct { + Type string `json:"type,omitempty"` + Platform string `json:"platform,omitempty"` + Family string `json:"family,omitempty"` + Version string `json:"version,omitempty"` + BootTime int64 `json:"boot-time,omitempty"` +} + +// Inventory interface +func New() (o OS, err error) { + logrus.Infof("os: %s %s", utils.Sys.InfoStat.OS, utils.Sys.Platform) + + o = OS{ + Type: utils.Sys.InfoStat.OS, + Platform: utils.Sys.Platform, + Family: utils.Sys.PlatformFamily, + Version: utils.Sys.PlatformVersion, + BootTime: utils.Sys.BootTime.Unix(), + } + return +} diff --git a/pkg/agent/types/types.go b/pkg/agent/types/types.go new file mode 100644 index 0000000..34d512f --- /dev/null +++ b/pkg/agent/types/types.go @@ -0,0 +1,37 @@ +package types + +const ( + NodeID = "types/node" + CpuID = "types/cpu" + OsID = "types/os" + BaseboardID = "types/baseboard" + BiosID = "types/bios" + MemoryID = "types/memory-device" + NetlinkID = "types/netlink" + TempID = "types/temp" + NodeContainerID = "types/node-container" + FunctionContainerID = "types/function-container" + FunctionID = "types/function" + + RootID = "system/root" +) + +const ( + CpuLink = "cpu" + OsLink = "os" + BaseboardLink = "baseboard" + BiosLink = "bios" + DimmLink = "dimm" + NetlinkLink = "os-" + TempLink = "temp" + NodeContainerLink = "nodes" + FunctionContainerLink = "inventory" + FunctionLink = "init" +) + +const ( + NodeContainerPath = "nodes.root" + FunctionsPath = "functions.root" + FunctionContainerPath = "inventory.functions.root" + FunctionPath = "init.inventory.functions.root" +) diff --git a/pkg/agent/utils.go b/pkg/agent/utils.go new file mode 100644 index 0000000..bbef26c --- /dev/null +++ b/pkg/agent/utils.go @@ -0,0 +1,70 @@ +// Copyright 2022 Listware + +package agent + +import ( + "encoding/json" + + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/netlink" + "git.fg-tech.ru/listware/proto/sdk/pbtypes" +) + +const ( + namespace = "proxy" + + netlinkMask = "*[?@._type == 'netlink'?]" + memMask = "*[?@._type == 'memory-device'?]" +) + +const ( + updateEvent = "update" + deleteEvent = "delete" +) + +// TODO temp +const ( + cpuDev = "cpu" +) + +type Request struct { + Query string `json:"query"` + Name string `json:"name"` + + // need for subscribe + Link netlink.Netlink `json:"link"` + // update or delete + Event string `json:"event"` +} + +func prepareFunc(id string, r Request) (fc *pbtypes.FunctionContext, err error) { + ft := &pbtypes.FunctionType{ + Namespace: namespace, + Type: types.FunctionPath, + } + + fc = &pbtypes.FunctionContext{ + Id: id, + FunctionType: ft, + } + fc.Value, err = json.Marshal(r) + return +} + +// genFunction generate function call with object uuid and qdsl +func genFunction(id, query string) (*pbtypes.FunctionContext, error) { + r := Request{Query: query} + return prepareFunc(id, r) +} + +// genDimmFunction generate function call with object uuid and dimm name +func genDimmFunction(id, query, name string) (*pbtypes.FunctionContext, error) { + r := Request{Query: query, Name: name} + return prepareFunc(id, r) +} + +// genNetlinkFunction generate function call with object uuid and link object +func genNetlinkFunction(id, query string, link netlink.Netlink, event string) (*pbtypes.FunctionContext, error) { + r := Request{Query: query, Link: link, Event: event} + return prepareFunc(id, r) +} diff --git a/pkg/agent/worker.go b/pkg/agent/worker.go new file mode 100644 index 0000000..92d9f1f --- /dev/null +++ b/pkg/agent/worker.go @@ -0,0 +1,126 @@ +// Copyright 2022 Listware + +package agent + +import ( + "encoding/json" + "fmt" + "strings" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/go-core/pkg/module" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" +) + +func (a *Agent) workerFunction(ctx module.Context) (err error) { + var req Request + + if err = json.Unmarshal(ctx.Message(), &req); err != nil { + // temp + // move def msg to trigger + req.Query = a.nodepath() + } + + elements, err := qdsl.Qdsl(ctx, req.Query, qdsl.WithName(), qdsl.WithId()) + if err != nil { + return + } + + switch { + case req.Query == types.NodeContainerPath: + // find nodes + for _, doc := range elements { + // uuid of "nodes.root" + return a.readNode(ctx, doc.Id.String()) + } + return fmt.Errorf("%s: not found", types.NodeContainerPath) + + case req.Query == a.nodepath(): + // find node + for _, doc := range elements { + // uuid of "dev0.nodes.root._" + return a.readChilds(ctx, doc.Id.String()) + } + return + + case req.Query == a.baseboardpath(): + // find baseboard + for _, doc := range elements { + // uuid of "baseboard.dev0.nodes.root._" + return a.updateBaseboard(ctx, doc.Id.String()) + } + return a.createBaseboard(ctx) + + case req.Query == a.cpupath(): + // find cpu + for _, doc := range elements { + // uuid of "cpu.dev0.nodes.root._" + return a.updateCpu(ctx, doc.Id.String()) + } + return a.createCpu(ctx) + + case req.Query == a.biospath(): + // find bios + for _, doc := range elements { + // uuid of "bios.dev0.nodes.root._" + return a.updateBios(ctx, doc.Id.String()) + } + return a.createBios(ctx) + + case req.Query == a.ospath(): + // find os + for _, doc := range elements { + // uuid of "os.dev0.nodes.root._" + return a.updateOs(ctx, doc.Id.String()) + } + return a.createOs(ctx) + + case req.Query == a.dimmspath(): + for _, doc := range elements { + if _, ok := a.dimmDevs[doc.Name]; ok { + continue + } + if err = a.deleteObject(ctx, doc.Id.String()); err != nil { + return + } + } + return a.createDimms(ctx) + + case req.Query == a.netlinkspath(): + + for _, doc := range elements { + if _, ok := a.links[doc.Name]; ok { + continue + } + + // TODO remove if not exists + if err = a.deleteObject(ctx, doc.Id.String()); err != nil { + return + } + } + return a.createNetlinks(ctx) + + case strings.Contains(req.Query, types.DimmLink): + // find dimm + for _, doc := range elements { + // uuid of "dimm*.dev0.nodes.root._" + return a.updateDimm(ctx, doc.Id.String(), req.Name) + } + return a.createDimm(ctx, req.Name) + + case strings.Contains(req.Query, types.NetlinkLink): + for _, doc := range elements { + // uuid of "os-*.dev0.nodes.root._" + switch req.Event { + case updateEvent: + return a.updateNetlink(ctx, doc.Id.String(), req.Link) + case deleteEvent: + return a.deleteObject(ctx, doc.Id.String()) + } + return + } + return a.createNetlink(ctx, &req.Link) + } + + return +} diff --git a/pkg/bootstrap/bootstrap.go b/pkg/bootstrap/bootstrap.go new file mode 100644 index 0000000..3aaf1a0 --- /dev/null +++ b/pkg/bootstrap/bootstrap.go @@ -0,0 +1,56 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "context" + "fmt" + + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/go-core/pkg/executor" +) + +const ( + appName = "inventory" +) + +func register(ctx context.Context, exec executor.Executor) (err error) { + // create types + if err = createTypes(ctx); err != nil { + return + } + + // create objects + if err = createObjects(ctx); err != nil { + return + } + + // create links + if err = createLinks(ctx); err != nil { + return + } + + message, err := system.Register(appName, registerTypes, registerObjects, registerLinks) + if err != nil { + return + } + + return exec.ExecSync(ctx, message) +} + +func Run() (err error) { + ctx := context.Background() + + exec, err := executor.New() + if err != nil { + return + } + defer exec.Close() + + if err = register(ctx, exec); err != nil { + fmt.Println("register: ", err) + return + } + + return +} diff --git a/pkg/bootstrap/functions.go b/pkg/bootstrap/functions.go new file mode 100644 index 0000000..0b03a7d --- /dev/null +++ b/pkg/bootstrap/functions.go @@ -0,0 +1,33 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "context" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/go-core/pkg/client/system" +) + +type InitFunction struct{} + +func createInitFunctionObject(ctx context.Context) (err error) { + // check if object exists + elements, err := qdsl.Qdsl(ctx, "init.inventory.functions.root") + if err != nil { + return + } + + // already exists + if len(elements) > 0 { + return + } + + message, err := system.RegisterObject("inventory.functions.root", "types/function", "init", InitFunction{}, true, true) + if err != nil { + return + } + registerObjects = append(registerObjects, message) + + return +} diff --git a/pkg/bootstrap/links.go b/pkg/bootstrap/links.go new file mode 100644 index 0000000..8ee6130 --- /dev/null +++ b/pkg/bootstrap/links.go @@ -0,0 +1,91 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "context" + "encoding/json" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/proto/sdk/pbcmdb" + "git.fg-tech.ru/listware/proto/sdk/pbtypes" +) + +var ( + registerLinks = []*pbcmdb.RegisterLinkMessage{} + + createTrigger = &pbcmdb.Trigger{ + Type: "create", + FunctionType: &pbtypes.FunctionType{ + Namespace: "proxy", + Type: types.FunctionPath, + }, + } + + updateTrigger = &pbcmdb.Trigger{ + Type: "update", + FunctionType: &pbtypes.FunctionType{ + Namespace: "proxy", + Type: types.FunctionPath, + }, + } +) + +// TODO move to go-core? +type Link struct { + Triggers map[string]map[string]*pbtypes.FunctionType +} + +func (l *Link) IsExists(trigger *pbcmdb.Trigger) bool { + if trigger, ok := l.Triggers[createTrigger.Type]; ok { + if _, ok := trigger[createTrigger.FunctionType.Namespace+"/"+createTrigger.FunctionType.Type]; ok { + return ok + } + } + return false +} + +func createLinks(ctx context.Context) (err error) { + if err = createFunctionNodeLink(ctx); err != nil { + return + } + return +} + +func createFunctionNodeLink(ctx context.Context) (err error) { + createTriggerMessage, err := system.RegisterLinkTrigger(types.FunctionID, types.NodeID, createTrigger, true) + if err != nil { + return + } + + updateTriggerMessage, err := system.RegisterLinkTrigger(types.FunctionID, types.NodeID, updateTrigger, true) + if err != nil { + return + } + + query := "node.function.types.root" + + elements, err := qdsl.Qdsl(ctx, query, qdsl.WithLink()) + if err != nil { + return + } + + for _, element := range elements { + var link Link + if err := json.Unmarshal(element.Link, &link); err == nil { + if !link.IsExists(createTrigger) { + registerLinks = append(registerLinks, createTriggerMessage) + } + if !link.IsExists(updateTrigger) { + registerLinks = append(registerLinks, updateTriggerMessage) + } + } + return + } + + registerLinks = append(registerLinks, createTriggerMessage, updateTriggerMessage) + + return +} diff --git a/pkg/bootstrap/nodes.go b/pkg/bootstrap/nodes.go new file mode 100644 index 0000000..0362110 --- /dev/null +++ b/pkg/bootstrap/nodes.go @@ -0,0 +1,77 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "context" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types" + "git.fg-tech.ru/listware/proto/sdk/pbcmdb" +) + +var ( + registerObjects = []*pbcmdb.RegisterObjectMessage{} +) + +type InventoryFunctionContainer struct{} + +type NodeContainer struct{} + +func createObjects(ctx context.Context) (err error) { + if err = createNodeContainerObject(ctx); err != nil { + return + } + + if err = createInventoryMountpointObject(ctx); err != nil { + return + } + + if err = createInitFunctionObject(ctx); err != nil { + return + } + + return +} + +func createNodeContainerObject(ctx context.Context) (err error) { + // check if object exists + elements, err := qdsl.Qdsl(ctx, types.NodeContainerPath) + if err != nil { + return + } + + // TODO already exists + if len(elements) > 0 { + return + } + + message, err := system.RegisterObject(types.RootID, types.NodeContainerID, types.NodeContainerLink, NodeContainer{}, true, false) + if err != nil { + return + } + registerObjects = append(registerObjects, message) + return +} + +func createInventoryMountpointObject(ctx context.Context) (err error) { + // check if object exists + elements, err := qdsl.Qdsl(ctx, types.FunctionContainerPath) + if err != nil { + return + } + + // TODO already exists + if len(elements) > 0 { + return + } + + message, err := system.RegisterObject(types.FunctionsPath, types.FunctionContainerID, types.FunctionContainerLink, InventoryFunctionContainer{}, false, true) + if err != nil { + return + } + registerObjects = append(registerObjects, message) + + return +} diff --git a/pkg/bootstrap/types.go b/pkg/bootstrap/types.go new file mode 100644 index 0000000..55f6bf0 --- /dev/null +++ b/pkg/bootstrap/types.go @@ -0,0 +1,108 @@ +// Copyright 2022 Listware + +package bootstrap + +import ( + "context" + "fmt" + + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/qdsl" + "git.fg-tech.ru/listware/cmdb/pkg/cmdb/vertex/types" + "git.fg-tech.ru/listware/go-core/pkg/client/system" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/baseboard" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/bios" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/cpu" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/mem" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/netlink" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/node" + "git.fg-tech.ru/listware/inventory-app/pkg/agent/types/os" + "git.fg-tech.ru/listware/proto/sdk/pbcmdb" +) + +var ( + registerTypes = []*pbcmdb.RegisterTypeMessage{} +) + +func createTypes(ctx context.Context) (err error) { + if err = createNodeType(ctx); err != nil { + return + } + if err = createBiosType(ctx); err != nil { + return + } + if err = createBaseboardType(ctx); err != nil { + return + } + if err = createCpuType(ctx); err != nil { + return + } + if err = createOsType(ctx); err != nil { + return + } + if err = createNetlinkType(ctx); err != nil { + return + } + if err = createMemType(ctx); err != nil { + return + } + if err = createNodeContainerType(ctx); err != nil { + return + } + + return +} + +func createType(ctx context.Context, pt *types.Type) (err error) { + query := fmt.Sprintf("%s.types.root", pt.Schema.Title) + elements, err := qdsl.Qdsl(ctx, query) + if err != nil { + return + } + + // TODO already exists + if len(elements) > 0 { + return + } + + message, err := system.RegisterType(pt, true) + if err != nil { + return + } + + registerTypes = append(registerTypes, message) + return +} + +func createNodeType(ctx context.Context) (err error) { + pt := types.ReflectType(&node.Node{}) + return createType(ctx, pt) +} +func createBiosType(ctx context.Context) (err error) { + pt := types.ReflectType(&bios.BIOS{}) + return createType(ctx, pt) +} +func createBaseboardType(ctx context.Context) (err error) { + pt := types.ReflectType(&baseboard.Baseboard{}) + return createType(ctx, pt) +} +func createCpuType(ctx context.Context) (err error) { + pt := types.ReflectType(&cpu.CPU{}) + return createType(ctx, pt) +} +func createOsType(ctx context.Context) (err error) { + pt := types.ReflectType(&os.OS{}) + return createType(ctx, pt) +} +func createNetlinkType(ctx context.Context) (err error) { + pt := types.ReflectType(&netlink.Netlink{}) + return createType(ctx, pt) +} +func createMemType(ctx context.Context) (err error) { + pt := types.ReflectType(&mem.MemoryDevice{}) + return createType(ctx, pt) +} + +func createNodeContainerType(ctx context.Context) (err error) { + pt := types.ReflectType(&NodeContainer{}) + return createType(ctx, pt) +} diff --git a/pkg/utils/driver/driver.go b/pkg/utils/driver/driver.go new file mode 100644 index 0000000..6a69bb3 --- /dev/null +++ b/pkg/utils/driver/driver.go @@ -0,0 +1,48 @@ +// Copyright 2022 Listware + +package driver + +import ( + "os" + "os/exec" + "path" + + "github.com/sirupsen/logrus" +) + +const ( + kmodSysPath = "/sys/module" + drvsPath = "/sys/bus/pci/drivers" +) + +// Driver - disk driver: nvme, uio, etc.. +type Driver string + +func (drv Driver) String() string { + return string(drv) +} + +// IsLoaded - check if driver is loaded +func (drv Driver) IsLoaded() bool { + _, err := os.Stat(path.Join(kmodSysPath, drv.String())) + return !os.IsNotExist(err) +} + +// Load kernel module +func (drv Driver) Load() error { + if drv.IsLoaded() { + logrus.WithField("driver", drv.String()).Debug("is already loaded") + return nil + + } + return exec.Command("modprobe", drv.String()).Run() +} + +// Unload kernel module +func (drv Driver) Unload() error { + if !drv.IsLoaded() { + logrus.WithField("driver", drv.String()).Debug("is already unloaded") + return nil + } + return exec.Command("modprobe", "--remove", drv.String()).Run() +} diff --git a/pkg/utils/ipmi/tool.go b/pkg/utils/ipmi/tool.go new file mode 100644 index 0000000..ba66529 --- /dev/null +++ b/pkg/utils/ipmi/tool.go @@ -0,0 +1,163 @@ +// Copyright 2022 Listware + +package ipmi + +import ( + "bufio" + "bytes" + "encoding/hex" + "fmt" + "os/exec" + "strings" + + "git.fg-tech.ru/listware/inventory-app/pkg/utils/driver" +) + +// https://webcache.googleusercontent.com/search?q=cache:ohTuxLGBYaEJ:https://computercheese.blogspot.com/2014/03/&cd=2&hl=ru&ct=clnk&gl=ru +var ( + // Tool \\ + Tool = &tool{} + // Driver \\ + Driver = driver.Driver("ipmi_si") +) + +type tool struct{} + +func (t *tool) MC() MC { + mc := &mgmtController{tool: t} + return mc +} + +func (t *tool) exec(method string, args ...string) (*bytes.Buffer, error) { + a := []string{method} + a = append(a, args...) + c := exec.Command("ipmitool", a...) + stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) + c.Stdout = stdout + c.Stderr = stderr + if err := c.Run(); err != nil { + return nil, fmt.Errorf("%s: %w", stderr.String(), err) + } + return stdout, nil +} + +// MC - management controller +type MC interface { + GUID() (string, error) + IP() (string, error) + Temp() ([4]int, error) + Reset() error // rtype: +} + +type mgmtController struct { + tool *tool +} + +func (mc *mgmtController) GUIDOld() (s string, err error) { + bf, err := mc.tool.exec("mc", "guid") + if err != nil { + return + } + scanner := bufio.NewScanner(bf) + for scanner.Scan() { + txt := scanner.Text() + if strings.Contains(txt, "System GUID") { + kv := strings.Split(txt, ": ") + if len(kv) != 2 { + err = fmt.Errorf("failed to parse ipmitool output: %s", txt) + return + } + s = kv[1] + return + } + } + err = fmt.Errorf("failed to parse ipmitool output: %s", bf) + return +} + +func (mc *mgmtController) GUID() (s string, err error) { + buf, err := mc.tool.exec("raw", "0x06", "0x037") + if err != nil { + return + } + rawString := normalize(buf.Bytes()) + output := make([]string, 5) + output[0] = revert(rawString, 0, 7) + output[1] = revert(rawString, 1+7, 7+4) + output[2] = revert(rawString, 1+7+4, 7+4+4) + output[3] = string(rawString[1+7+4+4 : 1+7+4+4+4]) + output[4] = string(rawString[1+7+4+4+4 : 1+7+4+4+4+12]) + return strings.Join(output, "-"), err +} + +func revert(r []byte, f, t int) (result string) { + for i := t; i >= f; i -= 2 { + result += string(r[i-1]) + string(r[i]) + } + return +} + +func normalize(src []byte) []byte { + str := strings.ReplaceAll(string(src), " ", "") + return []byte(strings.ReplaceAll(str, "\n", "")) +} + +func (mc *mgmtController) IP() (s string, err error) { + buf, err := mc.tool.exec("raw", "0x0C", "0x02", "0x1", "0x3", "0x0", "0x0") + if err != nil { + return + } + rawString := normalize(buf.Bytes()) + dst := make([]byte, hex.DecodedLen(len(rawString))) + if _, err = hex.Decode(dst, rawString); err != nil { + return + } + + if dst[0] != 0x11 { + err = fmt.Errorf("bad code") + return + } + s = fmt.Sprintf("%d.%d.%d.%d", dst[1], dst[2], dst[3], dst[4]) + return +} + +func (mc *mgmtController) Reset() (err error) { + _, err = mc.tool.exec("mc", "reset", "cold") + return +} + +// ipmitool -b 0x06 -t 0x2c raw 0x2e 0x4b 0x57 0x01 0x00 0x0f 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff +// -b channel +// -t address +// header: 57 01 00 +// cpus (2 из 4 Шт): 2c 2f ff ff +// mem (посмотри в IpmiTemperatureStats::SystemTopology::setChannels): +// +// 2d ff ff ff 2d ff ff ff 2d +// ff ff ff 2d ff ff ff 2d ff ff ff 2d ff ff ff 2e +// ff ff ff 2d ff ff ff ff ff ff ff ff ff ff ff ff +// ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff +// ff ff ff ff ff ff ff +func (mc *mgmtController) Temp() (cpuTemp [4]int, err error) { + buf, err := mc.tool.exec("-b", "0x06", "-t", "0x2c", "raw", "0x2e", "0x4b", "0x57", "0x01", "0x00", "0x0f", "0xff", "0xff", "0xff", "0xff", "0xff", "0xff", "0xff", "0xff") + if err != nil { + return + } + rawString := normalize(buf.Bytes()) + dst := make([]byte, hex.DecodedLen(len(rawString))) + if _, err = hex.Decode(dst, rawString); err != nil { + return + } + + if dst[0] != 0x57 || dst[1] != 0x01 || dst[2] != 0x00 { + err = fmt.Errorf("bad code") + return + } + + cpuTemp[0] = int(dst[3]) + cpuTemp[1] = int(dst[4]) + cpuTemp[2] = int(dst[5]) + cpuTemp[3] = int(dst[6]) + + return +} diff --git a/pkg/utils/ipmitool.go b/pkg/utils/ipmitool.go new file mode 100644 index 0000000..7eb8c75 --- /dev/null +++ b/pkg/utils/ipmitool.go @@ -0,0 +1,20 @@ +// Copyright 2022 Listware + +package utils + +import ( + "io" + "os/exec" +) + +type ipmitool struct { + cmd *exec.Cmd + stdin io.Reader + stdout io.Writer +} + +func newIpmiTool() (it *ipmitool, err error) { + it = &ipmitool{} + // it.cmd, err = exec.Command("") + return +} diff --git a/pkg/utils/pid/file.go b/pkg/utils/pid/file.go new file mode 100644 index 0000000..58b7c8b --- /dev/null +++ b/pkg/utils/pid/file.go @@ -0,0 +1,54 @@ +// Copyright 2022 Listware + +package pid + +import ( + "fmt" + "os" + "strconv" + "syscall" +) + +// File - is a pid-file +type File string + +func (f File) String() string { + return string(f) +} + +// Write a pid file, but first make sure it doesn't exist with a running pid. +func (f File) Write() error { + // Read in the pid file as a slice of bytes. + if piddata, err := os.ReadFile(f.String()); err == nil { + // Convert the file contents to an integer. + if pid, err := strconv.Atoi(string(piddata)); err == nil { + // Look for the pid in the process list. + if process, err := os.FindProcess(pid); err == nil { + // Send the process a signal zero kill. + if err := process.Signal(syscall.Signal(0)); err == nil { + // We only get an error if the pid isn't running, or it's not ours. + return fmt.Errorf("pid already running: %d", pid) + } + } + } + } + // If we get here, then the pidfile didn't exist, + // or the pid in it doesn't belong to the user running this app. + return os.WriteFile(f.String(), []byte(fmt.Sprintf("%d", os.Getpid())), 0664) +} + +// Remove pid file +func (f File) Remove() error { + return os.Remove(f.String()) +} + +// PID - process id +func (f File) PID() (pid int, err error) { + var piddata []byte + piddata, err = os.ReadFile(f.String()) + if err != nil { + return + } + pid, err = strconv.Atoi(string(piddata)) + return +} diff --git a/pkg/utils/sys.go b/pkg/utils/sys.go new file mode 100644 index 0000000..2c1dd64 --- /dev/null +++ b/pkg/utils/sys.go @@ -0,0 +1,106 @@ +// Copyright 2022 Listware + +package utils + +import ( + "math/rand" + "os" + "time" + + "git.fg-tech.ru/listware/inventory-app/pkg/utils/ipmi" + "github.com/shirou/gopsutil/host" + "github.com/sirupsen/logrus" + "github.com/zcalusic/sysinfo" +) + +var ( + forceUuidEnvName = "FORCE_BASEBOARD_UUID" + + srcUuidEnvName = "SRC_BASEBOARD_UUID" + + log = logrus.New() +) + +// Sys info +var Sys *sys + +type sys struct { + sysinfo.SysInfo + *host.InfoStat + + BootTime time.Time +} + +func Init() { + Sys = newSys() +} + +func newSys() *sys { + s := &sys{ + InfoStat: getHostInfo(), + BootTime: bootTime(), + } + + s.GetSysInfo() + + if v, ok := os.LookupEnv(forceUuidEnvName); ok { + log.Infof("overidden uuid: %s", v) + s.InfoStat.HostID = v + return s + } + + if v, ok := os.LookupEnv(srcUuidEnvName); ok { + if v == "dmi" { + log.Infof("uuid from dmi: %s", s.InfoStat.HostID) + return s + } + } + + u, err := getUUIDFromIPMI() + if u != "" { + log.Infof("uuid from ipmi: %s", s.InfoStat.HostID) + s.InfoStat.HostID = u + } else { + log.Warnf("can't read uuid from ipmi, using dmi: %v", err) + } + + return s +} + +func getUUIDFromIPMI() (string, error) { + drvWasLoaded := ipmi.Driver.IsLoaded() + if !drvWasLoaded { + if err := ipmi.Driver.Load(); err != nil { + return "", err + } + } + uuid, err := ipmi.Tool.MC().GUID() + if err != nil { + return "", err + } + if !drvWasLoaded { + err = ipmi.Driver.Unload() + } + return uuid, err +} + +func getHostInfo() *host.InfoStat { + hostInfo, err := host.Info() + if err != nil { + return nil + } + return hostInfo +} + +// Random func +func Random(min, max int) int { + rand.Seed(time.Now().Unix()) + return rand.Intn(max-min) + min +} + +func bootTime() time.Time { + if bt, err := host.BootTime(); err == nil { + return time.Unix(int64(bt), 0) + } + return time.Now() +}