pinger.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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 misc
  15. import (
  16. "context"
  17. "fmt"
  18. "strconv"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. "yunion.io/x/pkg/util/netutils"
  24. "yunion.io/x/pkg/util/rbacscope"
  25. api "yunion.io/x/onecloud/pkg/apis/compute"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/tsdb"
  27. "yunion.io/x/onecloud/pkg/cloudmon/options"
  28. "yunion.io/x/onecloud/pkg/mcclient"
  29. "yunion.io/x/onecloud/pkg/mcclient/auth"
  30. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  31. "yunion.io/x/onecloud/pkg/util/influxdb"
  32. "yunion.io/x/onecloud/pkg/util/sysutils"
  33. )
  34. func PingProbe(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
  35. err := func() error {
  36. if options.Options.DisablePingProbe {
  37. return nil
  38. }
  39. isRoot := sysutils.IsRootPermission()
  40. if !isRoot {
  41. return errors.Error("require root permissions")
  42. }
  43. s := auth.GetAdminSession(ctx, options.Options.Region)
  44. networks := []api.NetworkDetails{}
  45. for {
  46. params := map[string]interface{}{
  47. "offset": len(networks),
  48. "limit": "10",
  49. "provider": []string{
  50. api.CLOUD_PROVIDER_ONECLOUD,
  51. },
  52. "scope": rbacscope.ScopeSystem,
  53. "is_classic": true,
  54. "server_type": []string{
  55. string(api.NETWORK_TYPE_GUEST),
  56. string(api.NETWORK_TYPE_BAREMETAL),
  57. string(api.NETWORK_TYPE_CONTAINER),
  58. string(api.NETWORK_TYPE_PXE),
  59. string(api.NETWORK_TYPE_IPMI),
  60. string(api.NETWORK_TYPE_EIP),
  61. },
  62. }
  63. resp, err := compute.Networks.List(s, jsonutils.Marshal(params))
  64. if err != nil {
  65. return errors.Wrapf(err, "Networks.List")
  66. }
  67. part := []api.NetworkDetails{}
  68. err = jsonutils.Update(&part, resp.Data)
  69. if err != nil {
  70. return errors.Wrapf(err, "jsonutils.Update")
  71. }
  72. networks = append(networks, part...)
  73. if len(networks) >= resp.Total {
  74. break
  75. }
  76. }
  77. metrics := make([]influxdb.SMetricData, 0)
  78. for i := range networks {
  79. network := sNetwork{networks[i]}
  80. m, err := pingProbeNetwork(s, network)
  81. if err != nil {
  82. log.Errorf("pingProbeNetwork network %s(%s-%s) fail %s", network.Name, network.GuestIpStart, network.GuestIpEnd, err)
  83. continue
  84. }
  85. metrics = append(metrics, m...)
  86. }
  87. urls, err := tsdb.GetDefaultServiceSourceURLs(s, options.Options.SessionEndpointType)
  88. if err != nil {
  89. return errors.Wrap(err, "GetServiceURLs")
  90. }
  91. return influxdb.SendMetrics(urls, options.Options.InfluxDatabase, metrics, false)
  92. }()
  93. if err != nil {
  94. log.Errorf("PingProb error: %v", err)
  95. }
  96. }
  97. type sNetwork struct {
  98. api.NetworkDetails
  99. }
  100. func getNetworkAddrMap(s *mcclient.ClientSession, netId string) (map[string]api.SNetworkUsedAddress, error) {
  101. addrListJson, err := compute.Networks.GetSpecific(s, netId, "addresses", nil)
  102. if err != nil {
  103. return nil, errors.Wrap(err, "GetSpecific addresses")
  104. }
  105. addrList := make([]api.SNetworkUsedAddress, 0)
  106. if addrListJson.Contains("addresses") {
  107. err = addrListJson.Unmarshal(&addrList, "addresses")
  108. if err != nil {
  109. return nil, errors.Wrap(err, "Unmarshal addreses")
  110. }
  111. }
  112. addrMap := make(map[string]api.SNetworkUsedAddress)
  113. for i := range addrList {
  114. addrMap[addrList[i].IpAddr] = addrList[i]
  115. }
  116. return addrMap, nil
  117. }
  118. func pingProbeNetwork(s *mcclient.ClientSession, net sNetwork) ([]influxdb.SMetricData, error) {
  119. metrics := make([]influxdb.SMetricData, 0)
  120. if net.CloudEnv != api.CLOUD_ENV_ON_PREMISE {
  121. return nil, errors.Wrap(errors.ErrInvalidStatus, "not onpremise network")
  122. }
  123. if net.GuestGateway == "" {
  124. return nil, errors.Wrap(errors.ErrInvalidStatus, "unreachable network, empty gateway")
  125. }
  126. addrStart, err := netutils.NewIPV4Addr(net.GuestIpStart)
  127. if err != nil {
  128. return nil, errors.Wrapf(err, "NewIPV4Addr %s", net.GuestIpStart)
  129. }
  130. addrEnd, err := netutils.NewIPV4Addr(net.GuestIpEnd)
  131. if err != nil {
  132. return nil, errors.Wrapf(err, "NewIPV4Addr %s", net.GuestIpEnd)
  133. }
  134. log.Infof("ping address %s - %s", addrStart, addrEnd)
  135. pingAddrs := make([]string, 0)
  136. for addr := addrStart; addr <= addrEnd; addr = addr.StepUp() {
  137. addrStr := addr.String()
  138. pingAddrs = append(pingAddrs, addrStr)
  139. }
  140. pingResults, err := Ping(pingAddrs,
  141. options.Options.PingProbeOptions.ProbeCount,
  142. options.Options.PingProbeOptions.TimeoutSecond,
  143. options.Options.PingProbeOptions.Debug,
  144. )
  145. if err != nil {
  146. return nil, errors.Wrap(err, "Ping")
  147. }
  148. addrMap, err := getNetworkAddrMap(s, net.Id)
  149. if err != nil {
  150. return nil, errors.Wrap(err, "getNetworkAddrMap")
  151. }
  152. reserveIps := make([]string, 0)
  153. now := time.Now().UTC()
  154. for addr := addrStart; addr <= addrEnd; addr = addr.StepUp() {
  155. addrStr := addr.String()
  156. pingResult := pingResults[addrStr]
  157. netAddr, allocated := addrMap[addrStr]
  158. if allocated {
  159. if netAddr.OwnerType == api.RESERVEDIP_RESOURCE_TYPES {
  160. loss := pingResult.Loss()
  161. if loss < 100 {
  162. log.Debugf("Reserved address %s continues responding ping, extend reserving the address", addrStr)
  163. reserveIps = append(reserveIps, addrStr)
  164. } else {
  165. params := jsonutils.NewDict()
  166. params.Add(jsonutils.NewString(api.RESERVEDIP_STATUS_OFFLINE), "status")
  167. _, err := compute.ReservedIPs.Update(s, netAddr.OwnerId, params)
  168. if err != nil {
  169. log.Errorf("update reserved ip %s status fail: %s", addrStr, err)
  170. }
  171. }
  172. } else {
  173. // send metrics
  174. metric := influxdb.SMetricData{}
  175. metric.Name = "ping"
  176. metric.Timestamp = now
  177. metric.Tags = []influxdb.SKeyValue{
  178. {
  179. Key: "ip_addr",
  180. Value: addrStr,
  181. },
  182. {
  183. Key: "owner_type",
  184. Value: netAddr.OwnerType,
  185. },
  186. {
  187. Key: "owner_id",
  188. Value: netAddr.OwnerId,
  189. },
  190. {
  191. Key: "owner",
  192. Value: netAddr.Owner,
  193. },
  194. }
  195. loss := pingResult.Loss()
  196. max, avg, min := pingResult.Rtt()
  197. metric.Metrics = []influxdb.SKeyValue{
  198. {
  199. Key: "loss",
  200. Value: strconv.FormatInt(int64(loss), 10),
  201. },
  202. {
  203. Key: "rtt_ms_avg",
  204. Value: strconv.FormatInt(int64(avg/time.Millisecond), 10),
  205. },
  206. {
  207. Key: "rtt_ms_max",
  208. Value: strconv.FormatInt(int64(max/time.Millisecond), 10),
  209. },
  210. {
  211. Key: "rtt_ms_min",
  212. Value: strconv.FormatInt(int64(min/time.Millisecond), 10),
  213. },
  214. }
  215. metrics = append(metrics, metric)
  216. }
  217. } else {
  218. loss := pingResult.Loss()
  219. if loss < 100 {
  220. // reserve ip
  221. log.Debugf("Free address %s is responding ping, reserve the address", addrStr)
  222. reserveIps = append(reserveIps, addrStr)
  223. }
  224. }
  225. log.Debugf("%s %s allocated %v", addrStr, netAddr, allocated)
  226. }
  227. if len(reserveIps) > 0 {
  228. params := jsonutils.NewDict()
  229. params.Add(jsonutils.NewStringArray(reserveIps), "ips")
  230. params.Add(jsonutils.NewString("ping detected online free IP"), "notes")
  231. params.Add(jsonutils.NewString(api.RESERVEDIP_STATUS_ONLINE), "status")
  232. if options.Options.PingReserveIPTimeoutHours > 0 {
  233. params.Add(jsonutils.NewString(fmt.Sprintf("%dH", options.Options.PingReserveIPTimeoutHours)), "duration")
  234. }
  235. _, err = compute.Networks.PerformAction(s, net.Id, "reserve-ip", params)
  236. if err != nil {
  237. log.Errorf("failed to reserve ip %#v: %s", reserveIps, err)
  238. }
  239. }
  240. return metrics, nil
  241. }