agent/checknet/routine_support.go (115 lines of code) (raw):

//go:build linux || windows package checknet import ( "sync/atomic" "time" "github.com/aliyun/aliyun_assist_client/thirdparty/sirupsen/logrus" heavylock "github.com/viney-shih/go-lock" "github.com/aliyun/aliyun_assist_client/agent/log" "github.com/aliyun/aliyun_assist_client/agent/util/atomicutil" "github.com/aliyun/aliyun_assist_client/agent/util/wrapgo" "github.com/aliyun/aliyun_assist_client/common/networkcategory" ) var ( // Atomic value for boolean indicator of whether it is need to report network // diagnostic result _needToReport atomicutil.AtomicBoolean // Atomic counter of how many times the network diagnostic result must be // reported due to force-to-report request _forceToReportResponses atomicutil.AtomicInt32 // Atomic value for pointer of last network diagnostic report _neverDirectRW_atomic_lastReportPtr atomic.Value // _refreshingReportLock indicates whether one goroutine is running netcheck _refreshingReportLock heavylock.CASMutex ) func init() { // _needToReport and _forceToReportOnce are automatically statically // initialized with zero-value of atomicutil.AtomicBoolean type. var nilCheckReportPtr *CheckReport = nil _neverDirectRW_atomic_lastReportPtr.Store(nilCheckReportPtr) _refreshingReportLock = heavylock.NewCASMutex() } // RequestNetcheck would asynchronously invoke netcheck program for network // diagnostic, when no other network diagnostic is running or the last // diagnostic report has outdated. func RequestNetcheck(requestType NetcheckRequestType) { logger := log.GetLogger().WithFields(logrus.Fields{ "module": "checknet", }) switch requestType { case NetcheckRequestNormal: _needToReport.Set() wrapgo.GoWithDefaultPanicHandler(func() { _doNetcheck(NetcheckRequestNormal) }) case NetcheckRequestForceOnce: wrapgo.GoWithDefaultPanicHandler(func() { _doNetcheck(NetcheckRequestForceOnce) }) default: logger.WithFields(logrus.Fields{ "requestType": requestType, }).Errorln("Invalid netcheck request type") return } } func _doNetcheck(requestType NetcheckRequestType) { if !_refreshingReportLock.TryLock() { return } defer _refreshingReportLock.Unlock() logger := log.GetLogger().WithFields(logrus.Fields{ "module": "checknet", }) logger.Infof("requestType: %s", requestType) // Only check cache validity when processing normal netcheck request if requestType == NetcheckRequestNormal { reportPtr, ok := _neverDirectRW_atomic_lastReportPtr.Load().(*CheckReport) if !ok { return } if reportPtr != nil { if !isReportOutdated(reportPtr.FinishedTime) { return } } } logger.WithFields(logrus.Fields{ "requestType": requestType, }).Infoln("Invoke netcheck program in response to checknet request") resultCode, err := invokeNetcheck() if err != nil { logger.WithError(err).Errorln("Failed to invoke netcheck program") return } finishedTime := time.Now() newReportPtr := &CheckReport{ Result: resultCode, FinishedTime: finishedTime, } _neverDirectRW_atomic_lastReportPtr.Store(newReportPtr) // Only increase force-to-report response counter when processing // force-to-report netcheck request if requestType == NetcheckRequestForceOnce { _forceToReportResponses.Add(1) } logger.WithFields(logrus.Fields{ "result": resultCode, "finishedTime": finishedTime.Format(time.RFC3339), }).Infoln("Finished network diagnostic") } // RecentReport would return the most recent available network diagnostic report, // or nil pointer if the report has not been generated. When the report has been // outdated, it would call RequestNetcheck to refresh netcheck report. func RecentReport() *CheckReport { isForceToReportOnce := _forceToReportResponses.Load() > 0 isNeedToReport := _needToReport.IsSet() if !isForceToReportOnce && !isNeedToReport { return nil } reportPtr, ok := _neverDirectRW_atomic_lastReportPtr.Load().(*CheckReport) if !ok { return nil } if reportPtr == nil { return nil } // NOTE: Thanks to to serial feature of gshell channel, RecentReport() would // never be called concurrently and _forceToReportResponses counter should // never become less than zero due to parallel decreasing actions more than // available response count. if isForceToReportOnce { _forceToReportResponses.Add(-1) } // Only when it is needed to report by automatic detection via heart-beat, // it is needed to check whether current report is out-of-date. if isNeedToReport && isReportOutdated(reportPtr.FinishedTime) { RequestNetcheck(NetcheckRequestNormal) } return reportPtr } // DeclareNetworkCategory sets the network category in cache of this module, // which is used to specify the network environment when running netcheck // program. func DeclareNetworkCategory(category networkcategory.NetworkCategory) { networkCategoryCache.Set(category) } // clearNeedToReport simply set that it is not needed to report network // diagnostic result. func clearNeedToReport() { _needToReport.Clear() } // CollectNetworkConfiguration collects the network configurations of system, // which be used to diagnosing no network issues. func CollectNetworkConfiguration(logger logrus.FieldLogger, taskId string) (int, error) { return invokeCollection(logger, taskId) }