Initial commit

This commit is contained in:
fg-admin 2023-01-25 14:25:58 +03:00
commit 12e8ef51c1
45 changed files with 2793 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/inventory
/cmd/inventory/inventory

202
LICENSE Normal file
View File

@ -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.

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# Inventory Application
Environmental objects inventory application
## run
### boottstrap
```inventory bootstrap```
### agent
```inventory agent run```

12
cmd/inventory/agent.go Normal file
View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()
},
}

7
cmd/inventory/main.go Normal file
View File

@ -0,0 +1,7 @@
// Copyright 2022 Listware
package main
func main() {
execute()
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Listware
package main
import (
"testing"
"go.uber.org/goleak"
)
func TestMain(t *testing.T) {
defer goleak.VerifyNone(t)
}

90
cmd/inventory/root.go Normal file
View File

@ -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
},
}

58
go.mod Normal file
View File

@ -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
)

152
go.sum Normal file
View File

@ -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=

157
pkg/agent/agent.go Normal file
View File

@ -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
}

38
pkg/agent/baseboard.go Normal file
View File

@ -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
}

37
pkg/agent/bios.go Normal file
View File

@ -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
}

37
pkg/agent/cpu.go Normal file
View File

@ -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
}

53
pkg/agent/dimm.go Normal file
View File

@ -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
}

82
pkg/agent/entrypoint.go Normal file
View File

@ -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)
}

91
pkg/agent/netlink.go Normal file
View File

@ -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
}

118
pkg/agent/node.go Normal file
View File

@ -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
}

37
pkg/agent/os.go Normal file
View File

@ -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
}

38
pkg/agent/query.go Normal file
View File

@ -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)
}

35
pkg/agent/signal.go Normal file
View File

@ -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
}
}
}()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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, "_", "-"))
}

View File

@ -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
}

View File

@ -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])
}

View File

@ -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
}

31
pkg/agent/types/os/os.go Normal file
View File

@ -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
}

37
pkg/agent/types/types.go Normal file
View File

@ -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"
)

70
pkg/agent/utils.go Normal file
View File

@ -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)
}

126
pkg/agent/worker.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

91
pkg/bootstrap/links.go Normal file
View File

@ -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
}

77
pkg/bootstrap/nodes.go Normal file
View File

@ -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
}

108
pkg/bootstrap/types.go Normal file
View File

@ -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)
}

View File

@ -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()
}

163
pkg/utils/ipmi/tool.go Normal file
View File

@ -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: <warm|cold>
}
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
}

20
pkg/utils/ipmitool.go Normal file
View File

@ -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
}

54
pkg/utils/pid/file.go Normal file
View File

@ -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
}

106
pkg/utils/sys.go Normal file
View File

@ -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()
}