report.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package report
  15. import (
  16. "context"
  17. "net"
  18. "os"
  19. "strings"
  20. "time"
  21. "k8s.io/client-go/kubernetes"
  22. "k8s.io/client-go/rest"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/gotypes"
  27. "yunion.io/x/pkg/util/httputils"
  28. "yunion.io/x/pkg/util/version"
  29. "yunion.io/x/pkg/utils"
  30. "yunion.io/x/onecloud/pkg/apigateway/options"
  31. api "yunion.io/x/onecloud/pkg/apis/compute"
  32. idapi "yunion.io/x/onecloud/pkg/apis/identity"
  33. "yunion.io/x/onecloud/pkg/mcclient"
  34. "yunion.io/x/onecloud/pkg/mcclient/auth"
  35. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  36. "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
  37. )
  38. type sReport struct {
  39. GenerateName string
  40. UUID string
  41. Version string
  42. OsDist string
  43. OsVersion string
  44. QemuVersion string
  45. CpuArchitecture string
  46. Brands string
  47. HostCnt int64
  48. KvmHostCnt int64
  49. VmwareHostCnt int64
  50. HostCpuCnt int64
  51. HostMemSizeMb int64
  52. BaremetalCnt int64
  53. BaremetalCpuCnt int64
  54. BaremetalMemSizeMb int64
  55. BaremetalStorageSizeGb int64
  56. ServerCnt int64
  57. ContainerCnt int64
  58. ContainerCpuCnt int64
  59. ContainerMemSizeMb int64
  60. ServerCpuCnt int64
  61. ServerMemSizeMb int64
  62. KvmServerCnt int64
  63. KvmServerCpuCnt int64
  64. KvmServerMemSizeMb int64
  65. DiskCnt int64
  66. DiskSizeMb int64
  67. BucketCnt int64
  68. RdsCnt int64
  69. MongoDBCnt int64
  70. KafkaCnt int64
  71. UserCnt int64
  72. ProjectCnt int64
  73. DomainCnt int64
  74. RunningEnv string
  75. }
  76. const (
  77. RUNNING_ENV_KUBERNETES = "kubernetes"
  78. RUNNING_ENV_K3S = "k3s"
  79. RUNNING_ENV_DOCKER_COMPOSE = "docker-compose"
  80. RUNNING_ENV_DOCKER_COMPOSE_BAREMETAL = "docker-compose-baremetal"
  81. RUNNING_ENV_UNKNOWN = "unknown"
  82. )
  83. func isInsideDockerCompose() bool {
  84. // hostname likes: c1577eee2aed
  85. DOCKER_HOSTNAME_LEN := 12
  86. hostname := os.Getenv("HOSTNAME")
  87. if len(hostname) != DOCKER_HOSTNAME_LEN {
  88. return false
  89. }
  90. // resolv mysql and keystone host
  91. resolvTimeout := 500 * time.Millisecond
  92. ctx, cancel := context.WithTimeout(context.Background(), resolvTimeout)
  93. defer cancel()
  94. var r net.Resolver
  95. hosts := []string{"mysql", "keystone", "region"}
  96. for _, host := range hosts {
  97. _, err := r.LookupIPAddr(ctx, host)
  98. if err != nil {
  99. return false
  100. }
  101. }
  102. return true
  103. }
  104. func isInsideKubernetes() bool {
  105. k8sHost := os.Getenv("KUBERNETES_SERVICE_HOST")
  106. if len(k8sHost) == 0 {
  107. return false
  108. }
  109. // check /var/run/secrets/kubernetes.io/serviceaccount/ directory
  110. k8sSecDir := "/var/run/secrets/kubernetes.io/serviceaccount/"
  111. if _, err := os.Stat(k8sSecDir); err != nil {
  112. return false
  113. }
  114. return true
  115. }
  116. func getK8sVersion() (string, error) {
  117. cfg, err := rest.InClusterConfig()
  118. if err != nil {
  119. return "", errors.Wrap(err, "get k8s config")
  120. }
  121. client, err := kubernetes.NewForConfig(cfg)
  122. if err != nil {
  123. return "", errors.Wrap(err, "get k8s client")
  124. }
  125. ver, err := client.ServerVersion()
  126. if err != nil {
  127. return "", errors.Wrap(err, "get k8s version")
  128. }
  129. return ver.String(), nil
  130. }
  131. func getRunningEnv() string {
  132. if isInsideKubernetes() {
  133. ver, err := getK8sVersion()
  134. if err != nil {
  135. log.Errorf("get k8s version: %v", err)
  136. }
  137. if strings.Contains(ver, "k3s") {
  138. return RUNNING_ENV_K3S
  139. }
  140. return RUNNING_ENV_KUBERNETES
  141. }
  142. if isInsideDockerCompose() {
  143. // list agent to check whether we're running baremetal agent
  144. s := auth.GetAdminSession(context.Background(), options.Options.Region)
  145. ret, err := compute.Baremetalagents.List(s, nil)
  146. if err != nil {
  147. log.Warningf("list agents error: %s", err)
  148. return RUNNING_ENV_DOCKER_COMPOSE
  149. }
  150. for _, agent := range ret.Data {
  151. aType, _ := agent.GetString("agent_type")
  152. if aType == "baremetal" {
  153. return RUNNING_ENV_DOCKER_COMPOSE_BAREMETAL
  154. }
  155. }
  156. return RUNNING_ENV_DOCKER_COMPOSE
  157. }
  158. return RUNNING_ENV_UNKNOWN
  159. }
  160. func Report(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  161. getFunc := func() (*sReport, error) {
  162. ret := &sReport{}
  163. ret.Version = version.GetShortString()
  164. s := auth.GetAdminSession(ctx, options.Options.Region)
  165. system := jsonutils.Marshal(map[string]string{"scope": "system"})
  166. user, err := identity.UsersV3.Get(s, idapi.SystemAdminUser, nil)
  167. if err != nil {
  168. return nil, errors.Wrapf(err, "Get user %s id", idapi.SystemAdminUser)
  169. }
  170. ret.UUID, err = user.GetString("id")
  171. if err != nil {
  172. return nil, errors.Wrapf(err, "get user %s id", idapi.SystemAdminUser)
  173. }
  174. ret.GenerateName = ret.UUID
  175. resp, err := compute.Hosts.List(s, jsonutils.Marshal(map[string]interface{}{
  176. "scope": "system",
  177. "hypervisor": api.HYPERVISOR_KVM,
  178. "limit": 1,
  179. }))
  180. if err != nil {
  181. return nil, errors.Wrapf(err, "Hosts.List")
  182. }
  183. ret.KvmHostCnt = int64(resp.Total)
  184. resp, err = compute.Hosts.List(s, jsonutils.Marshal(map[string]interface{}{
  185. "scope": "system",
  186. "hypervisor": api.HYPERVISOR_ESXI,
  187. "limit": 1,
  188. }))
  189. if err != nil {
  190. return nil, errors.Wrapf(err, "Hosts.List")
  191. }
  192. ret.VmwareHostCnt = int64(resp.Total)
  193. resp, err = compute.Servers.List(s, jsonutils.Marshal(map[string]interface{}{
  194. "scope": "system",
  195. "hypervisor": api.HYPERVISOR_KVM,
  196. "limit": 1,
  197. }))
  198. if err != nil {
  199. return nil, errors.Wrapf(err, "Server.List")
  200. }
  201. ret.KvmServerCnt = int64(resp.Total)
  202. if !gotypes.IsNil(resp.Totals) {
  203. ret.KvmServerCpuCnt, _ = resp.Totals.Int("cpu_count")
  204. ret.KvmServerMemSizeMb, _ = resp.Totals.Int("mem_mb")
  205. }
  206. osDists, osVersions, qemuVersions, archs := []string{}, []string{}, []string{}, []string{}
  207. hosts := []api.HostDetails{}
  208. jsonutils.Update(&hosts, resp.Data)
  209. appendInfo := func(arr []string, info string) []string {
  210. if len(info) == 0 {
  211. return arr
  212. }
  213. if !utils.IsInStringArray(info, arr) {
  214. return append(arr, info)
  215. }
  216. return arr
  217. }
  218. for _, host := range hosts {
  219. archs = appendInfo(archs, host.CpuArchitecture)
  220. sn := struct {
  221. OsDistribution string
  222. OsVersion string
  223. QemuVersion string
  224. }{}
  225. if host.SysInfo != nil {
  226. host.SysInfo.Unmarshal(&sn)
  227. osDists = appendInfo(osDists, sn.OsDistribution)
  228. osVersions = appendInfo(osVersions, sn.OsVersion)
  229. qemuVersions = appendInfo(qemuVersions, sn.QemuVersion)
  230. }
  231. }
  232. ret.OsDist = strings.Join(osDists, ",")
  233. ret.OsVersion = strings.Join(osVersions, ",")
  234. ret.QemuVersion = strings.Join(qemuVersions, ",")
  235. ret.CpuArchitecture = strings.Join(archs, ",")
  236. resp, err = compute.Capabilities.List(s, system)
  237. if err != nil {
  238. return nil, errors.Wrapf(err, "Capabilities.List")
  239. }
  240. if len(resp.Data) > 0 {
  241. brands := struct {
  242. Brands []string
  243. DisabledBrands []string
  244. }{}
  245. resp.Data[0].Unmarshal(&brands)
  246. ret.Brands = strings.Join(append(brands.Brands, brands.DisabledBrands...), ",")
  247. }
  248. usage, err := compute.Usages.GetGeneralUsage(s, system)
  249. if err != nil {
  250. return nil, errors.Wrapf(err, "GetGeneralUsage")
  251. }
  252. ret.HostCnt, _ = usage.Int("hosts")
  253. ret.HostCpuCnt, _ = usage.Int("hosts.cpu.total")
  254. ret.HostMemSizeMb, _ = usage.Int("hosts.memory.total")
  255. ret.ServerCnt, _ = usage.Int("all.servers")
  256. ret.ServerCpuCnt, _ = usage.Int("all.servers.cpu")
  257. ret.ServerMemSizeMb, _ = usage.Int("all.servers.memory")
  258. ret.ContainerCnt, _ = usage.Int("all.containers")
  259. ret.ContainerCpuCnt, _ = usage.Int("all.containers.cpu")
  260. ret.ContainerMemSizeMb, _ = usage.Int("all.containers.memory")
  261. ret.DiskCnt, _ = usage.Int("all.disks.count")
  262. ret.DiskSizeMb, _ = usage.Int("all.disks")
  263. ret.BucketCnt, _ = usage.Int("all.buckets")
  264. ret.RdsCnt, _ = usage.Int("all.rds")
  265. ret.MongoDBCnt, _ = usage.Int("all.mongodb")
  266. ret.KafkaCnt, _ = usage.Int("all.kafka")
  267. ret.BaremetalCnt, _ = usage.Int("baremetals")
  268. ret.BaremetalCpuCnt, _ = usage.Int("baremetals.cpu")
  269. ret.BaremetalMemSizeMb, _ = usage.Int("baremetals.memory")
  270. ret.BaremetalStorageSizeGb, _ = usage.Int("baremetals.storage_gb")
  271. usage, err = identity.IdentityUsages.GetUsage(s, system)
  272. if err != nil {
  273. return nil, errors.Wrapf(err, "IdentityUsages.GetUsage")
  274. }
  275. ret.UserCnt, _ = usage.Int("users")
  276. ret.DomainCnt, _ = usage.Int("domains")
  277. ret.ProjectCnt, _ = usage.Int("projects")
  278. ret.RunningEnv = getRunningEnv()
  279. return ret, nil
  280. }
  281. rp, err := func() (*sReport, error) {
  282. var err error
  283. var report *sReport
  284. for i := 0; i < 3; i++ {
  285. report, err = getFunc()
  286. if err == nil {
  287. return report, nil
  288. }
  289. time.Sleep(time.Minute * time.Duration(i+1) * 2)
  290. continue
  291. }
  292. return report, err
  293. }()
  294. if err != nil {
  295. log.Errorf("get report error: %v", err)
  296. return
  297. }
  298. url := "https://cloud.yunion.cn/api/v2/opensource-reporting"
  299. client := httputils.GetDefaultClient()
  300. _, _, err = httputils.JSONRequest(client, context.Background(), httputils.POST, url, nil, jsonutils.Marshal(rp), false)
  301. if err != nil {
  302. log.Errorf("report data error: %v", err)
  303. }
  304. }