options.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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 options
  15. import (
  16. "crypto/tls"
  17. "crypto/x509"
  18. "encoding/pem"
  19. "fmt"
  20. "io"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "path"
  25. "path/filepath"
  26. "strings"
  27. "golang.org/x/net/http/httpproxy"
  28. "golang.org/x/text/language"
  29. "yunion.io/x/log"
  30. "yunion.io/x/log/hooks"
  31. "yunion.io/x/pkg/errors"
  32. "yunion.io/x/pkg/util/httputils"
  33. "yunion.io/x/pkg/util/reflectutils"
  34. "yunion.io/x/pkg/util/version"
  35. "yunion.io/x/pkg/utils"
  36. "yunion.io/x/structarg"
  37. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  38. "yunion.io/x/onecloud/pkg/httperrors"
  39. "yunion.io/x/onecloud/pkg/util/atexit"
  40. "yunion.io/x/onecloud/pkg/util/fileutils2"
  41. "yunion.io/x/onecloud/pkg/util/netutils2"
  42. )
  43. const (
  44. DefaultQuotaUnlimit = "unlimit"
  45. DefaultQuotaZero = "zero"
  46. DefaultQuotaDefault = "default"
  47. )
  48. type BaseOptions struct {
  49. Region string `help:"Region name or ID" alias:"auth-region"`
  50. Port int `help:"The port that the service runs on" alias:"bind-port"`
  51. Address string `help:"The IP address to serve on (set to 0.0.0.0 for all interfaces)" default:"0.0.0.0" alias:"bind-host"`
  52. DebugClient bool `help:"Switch on/off mcclient debugs" default:"false"`
  53. LogLevel string `help:"log level" default:"info" choices:"debug|info|warn|error"`
  54. LogWithTimeZone string `help:"log time zone" default:"UTC"`
  55. LogTimestampFormat string `help:"log time format" default:"2006-01-02 15:04:05"`
  56. LogVerboseLevel int `help:"log verbosity level" default:"0"`
  57. LogFilePrefix string `help:"prefix of log files"`
  58. CorsHosts []string `help:"List of hostname that allow CORS"`
  59. TempPath string `help:"Path for store temp file, at least 40G space" default:"/opt/yunion/tmp"`
  60. ApplicationID string `help:"Application ID"`
  61. RequestWorkerCount int `default:"8" help:"Request worker thread count, default is 8"`
  62. RequestWorkerQueueSize int `default:"10" help:"Request worker queue size, default is 10"`
  63. TaskWorkerCount int `default:"4" help:"Task manager worker thread count, default is 4"`
  64. LocalTaskWorkerCount int `default:"4" help:"Worker thread count that runs local tasks, default is 4"`
  65. TaskArchiveThresholdHours int `default:"168" help:"The threshold in hours to migrate tasks to archive, default is 7days(168hours)"`
  66. TaskArchiveIntervalMinutes int `default:"60" help:"The interval in mibutes to migrate tasks to archive, default is 1 hour"`
  67. TaskArchiveBatchLimit int `default:"10000" help:"The maximal count of tasks to archivie in a batch, default is 10000"`
  68. DefaultProcessTimeoutSeconds int `default:"60" help:"request process timeout, default is 60 seconds"`
  69. EnableSsl bool `help:"Enable https"`
  70. SslCaCerts string `help:"ssl certificate ca root file, separating ca and cert file is not encouraged" alias:"ca-file"`
  71. SslCertfile string `help:"ssl certification file, normally combines all the certificates in the chain" alias:"cert-file"`
  72. SslKeyfile string `help:"ssl certification private key file" alias:"key-file"`
  73. NotifyAdminUsers []string `default:"sysadmin" help:"System administrator user ID or name to notify system events, if domain is not default, specify domain as prefix ending with double backslash, e.g. domain\\\\user"`
  74. NotifyAdminGroups []string `help:"System administrator group ID or name to notify system events, if domain is not default, specify domain as prefix ending with double backslash, e.g. domain\\\\group"`
  75. // EnableRbac bool `help:"Switch on Role-based Access Control" default:"true"`
  76. RbacDebug bool `help:"turn on rbac debug log" default:"false"`
  77. RbacPolicyRefreshIntervalSeconds int `help:"policy refresh interval in seconds, default half a minute" default:"30"`
  78. // RbacPolicySyncFailedRetrySeconds int `help:"seconds to wait after a failed sync, default 30 seconds" default:"30"`
  79. PolicyWorkerCount int `help:"Policy worker count" default:"1"`
  80. ConfigSyncPeriodSeconds int `help:"service config sync interval in seconds, default 30 minutes" default:"1800"`
  81. IsSlaveNode bool `help:"Slave mode"`
  82. CronJobWorkerCount int `help:"Cron job worker count" default:"4"`
  83. EnableQuotaCheck bool `help:"enable quota check" default:"false"`
  84. DefaultQuotaValue string `help:"default quota value" choices:"unlimit|zero|default" default:"default"`
  85. CalculateQuotaUsageIntervalSeconds int `help:"interval to calculate quota usages, default 30 minutes" default:"900"`
  86. NonDefaultDomainProjects bool `help:"allow projects in non-default domains" default:"false" json:",allowfalse"`
  87. TimeZone string `help:"time zone" default:"Asia/Shanghai"`
  88. DomainizedNamespace bool `help:"turn on global name space, default is on" default:"false" json:"domainized_namespace,allowfalse"`
  89. ApiServer string `help:"URL to access frontend webconsole"`
  90. CustomizedPrivatePrefixes []string `help:"customized private prefixes"`
  91. MetadataServerIp4s []string `help:"metadata server IPv4 addresses, default is 169.254.169.254" default:"169.254.169.254"`
  92. MetadataServerIp6s []string `help:"metadata server IPv6 addresses, default is fd00:ec2::254" default:"'fd00:ec2::254'"`
  93. structarg.BaseOptions
  94. GlobalHTTPProxy string `help:"Global http proxy"`
  95. GlobalHTTPSProxy string `help:"Global https proxy"`
  96. IgnoreNonrunningGuests bool `default:"true" help:"Count memory for running guests only when do scheduling. Ignore memory allocation for non-running guests"`
  97. VirtualDeviceNumaBalance bool `default:"true" help:"fix virtual device numa node balance on guest start"`
  98. PlatformName string `help:"identity name of this platform" default:"Cloudpods"`
  99. PlatformNames map[string]string `help:"identity name of this platform by language"`
  100. EnableAppProfiling bool `help:"enable profiling API" default:"false"`
  101. AllowTLS1x bool `help:"allow obsolete insecure TLS V1.0&1.1" default:"false" json:"allow_tls1x"`
  102. EnableChangeOwnerAutoRename bool `help:"Allows renaming when changing names" default:"false"`
  103. EnableDefaultPolicy bool `help:"Enable defualt policies" default:"true"`
  104. DefaultHandlersWhitelistUserAgents []string `help:"whitelist user agents, default is empty"`
  105. }
  106. const (
  107. LockMethodInMemory = "inmemory"
  108. LockMethodEtcd = "etcd"
  109. )
  110. type CommonOptions struct {
  111. AuthURL string `help:"Keystone auth URL" alias:"auth-uri"`
  112. AdminUser string `help:"Admin username"`
  113. AdminDomain string `help:"Admin user domain" default:"Default"`
  114. AdminPassword string `help:"Admin password" alias:"admin-passwd"`
  115. AdminProject string `help:"Admin project" default:"system" alias:"admin-tenant-name"`
  116. AdminProjectDomain string `help:"Domain of Admin project" default:"Default"`
  117. AuthTokenCacheSize uint32 `help:"Auth token Cache Size" default:"2048"`
  118. TenantCacheExpireSeconds int `help:"expire seconds of cached tenant/domain info. defailt 15 minutes" default:"900"`
  119. SessionEndpointType string `help:"Client session end point type" default:"internal"`
  120. BaseOptions
  121. }
  122. type HostCommonOptions struct {
  123. CommonOptions
  124. S3CommonOptions
  125. ExecutorSocketPath string `help:"Executor socket path" default:"/var/run/onecloud/exec.sock"`
  126. DeployServerSocketPath string `help:"Deploy server listen socket path" default:"/var/run/onecloud/deploy.sock"`
  127. EnableRemoteExecutor bool `help:"Enable remote executor" default:"false"`
  128. EnableIsolatedDeviceWhitelist bool `help:"enable isolated device white list" default:"false"`
  129. ExecutorConnectTimeoutSeconds int `help:"executor client connection timeout in seconds, default is 30" default:"30"`
  130. ImageDeployDriver string `help:"Image deploy driver" default:"qemu-kvm" choices:"qemu-kvm|nbd|libguestfs"`
  131. DeployConcurrent int `help:"qemu-kvm deploy driver concurrent" default:"5"`
  132. Qcow2Preallocation string `help:"Qcow2 image create preallocation" default:"metadata" choices:"disable|metadata|falloc|full"`
  133. }
  134. type S3CommonOptions struct {
  135. S3AccessKey string `help:"s3 access key"`
  136. S3SecretKey string `help:"s3 secret key"`
  137. S3Endpoint string `help:"s3 endpoint"`
  138. S3UseSSL bool `help:"s3 access use ssl"`
  139. S3BucketName string `help:"s3 bucket name"`
  140. S3BucketLifecycleKeepDay int `help:"s3 bucket lifecycle keep day" default:"180"`
  141. }
  142. type DBOptions struct {
  143. SqlConnection string `help:"SQL connection string" alias:"connection"`
  144. Clickhouse string `help:"Connection string for click house"`
  145. DbMaxWaitTimeoutSeconds int `help:"max wait timeout for db connection, default 1 hour" default:"3600"`
  146. OpsLogWithClickhouse bool `help:"store operation logs with clickhouse" default:"false"`
  147. EnableDBChecksumTables bool `help:"Enable DB tables with record checksum for consistency"`
  148. DBChecksumSkipInit bool `help:"Skip DB tables with record checksum calculation when init" default:"false"`
  149. DBChecksumHashAlgorithm string `help:"hash algorithm for db checksum hash" choices:"md5|sha256" default:"sha256"`
  150. AutoSyncTable bool `help:"Automatically synchronize table changes if differences are detected"`
  151. ExitAfterDBInit bool `help:"Exit program after db initialization" default:"false"`
  152. GlobalVirtualResourceNamespace bool `help:"Per project namespace or global namespace for virtual resources" default:"false"`
  153. DebugSqlchemy bool `default:"false" help:"Print SQL executed by sqlchemy"`
  154. QueryOffsetOptimization bool `help:"apply query offset optimization"`
  155. HistoricalUniqueName bool `help:"use historically unique name" default:"false"`
  156. LockmanMethod string `help:"method for lock synchronization" choices:"inmemory|etcd" default:"inmemory"`
  157. OpsLogMaxKeepMonths int `help:"maximal months of logs to keep, default 6 months" default:"6"`
  158. SplitableMaxDurationHours int `help:"maximal number of hours that a splitable segement lasts, default 30 days" default:"720"`
  159. EtcdOptions
  160. EtcdLockPrefix string `help:"prefix of etcd lock records" default:"/onecloud/lockman"`
  161. EtcdLockTTL int `help:"ttl of etcd lock records" default:"5"`
  162. }
  163. type EtcdOptions struct {
  164. EtcdEndpoints []string `help:"endpoints of etcd cluster"`
  165. EtcdUsername string `help:"username of etcd cluster"`
  166. EtcdPassword string `help:"password of etcd cluster"`
  167. EtcdUseTLS bool `help:"use tls transport to connect etcd cluster" default:"false"`
  168. EtcdSkipTLSVerify bool `help:"skip tls verification" default:"false"`
  169. EtcdCacert string `help:"path to cacert for connecting to etcd cluster"`
  170. EtcdCert string `help:"path to cert file for connecting to etcd cluster"`
  171. EtcdKey string `help:"path to key file for connecting to etcd cluster"`
  172. }
  173. func (opt *EtcdOptions) GetEtcdTLSConfig() (*tls.Config, error) {
  174. var (
  175. cert tls.Certificate
  176. certLoaded bool
  177. capool *x509.CertPool
  178. )
  179. if opt.EtcdCert != "" && opt.EtcdKey != "" {
  180. var err error
  181. cert, err = tls.LoadX509KeyPair(opt.EtcdCert, opt.EtcdKey)
  182. if err != nil {
  183. return nil, errors.Wrap(err, "load etcd cert and key")
  184. }
  185. certLoaded = true
  186. opt.EtcdUseTLS = true
  187. }
  188. if opt.EtcdCacert != "" {
  189. data, err := os.ReadFile(opt.EtcdCacert)
  190. if err != nil {
  191. return nil, errors.Wrap(err, "read cacert file")
  192. }
  193. capool = x509.NewCertPool()
  194. for {
  195. var block *pem.Block
  196. block, data = pem.Decode(data)
  197. if block == nil {
  198. break
  199. }
  200. cacert, err := x509.ParseCertificate(block.Bytes)
  201. if err != nil {
  202. return nil, errors.Wrap(err, "parse cacert file")
  203. }
  204. capool.AddCert(cacert)
  205. }
  206. opt.EtcdUseTLS = true
  207. }
  208. if opt.EtcdSkipTLSVerify { // it's false by default, true means user intends to use tls
  209. opt.EtcdUseTLS = true
  210. }
  211. if opt.EtcdUseTLS {
  212. cfg := &tls.Config{
  213. RootCAs: capool,
  214. InsecureSkipVerify: opt.EtcdSkipTLSVerify,
  215. }
  216. if certLoaded {
  217. cfg.Certificates = []tls.Certificate{cert}
  218. }
  219. return cfg, nil
  220. }
  221. return nil, nil
  222. }
  223. func (opt *DBOptions) GetDBConnection() (string, string, error) {
  224. if strings.HasPrefix(opt.SqlConnection, "mysql") {
  225. return utils.TransSQLAchemyURL(opt.SqlConnection)
  226. } else {
  227. pos := strings.Index(opt.SqlConnection, "://")
  228. if pos > 0 {
  229. return opt.SqlConnection[:pos], opt.SqlConnection[pos+3:], nil
  230. } else {
  231. return "", "", httperrors.ErrNotSupported
  232. }
  233. }
  234. }
  235. func (opt *DBOptions) GetClickhouseConnStr() (string, string, error) {
  236. if len(opt.Clickhouse) == 0 {
  237. return "", "", errors.ErrNotFound
  238. }
  239. return "clickhouse", opt.Clickhouse, nil
  240. }
  241. func ParseOptionsIgnoreNoConfigfile(optStruct interface{}, args []string, configFileName string, serviceType string) {
  242. parseOptions(optStruct, args, configFileName, serviceType, true)
  243. }
  244. func ParseOptions(optStruct interface{}, args []string, configFileName string, serviceType string) {
  245. parseOptions(optStruct, args, configFileName, serviceType, false)
  246. }
  247. func parseOptions(optStruct interface{}, args []string, configFileName string, serviceType string, ignoreNoConfigfile bool) {
  248. if len(serviceType) == 0 {
  249. log.Fatalf("ServiceType must provided!")
  250. }
  251. consts.SetServiceType(serviceType)
  252. serviceName := path.Base(args[0])
  253. parser, err := structarg.NewArgumentParser(optStruct,
  254. serviceName,
  255. fmt.Sprintf(`Yunion cloud service - %s`, serviceName),
  256. `Yunion Technology Co. Ltd. @ 2018-2019`)
  257. if err != nil {
  258. log.Fatalf("Error define argument parser: %v", err)
  259. }
  260. err = parser.ParseArgs2(args[1:], false, false)
  261. if err != nil {
  262. log.Fatalf("Parse arguments error: %v", err)
  263. }
  264. var optionsRef *BaseOptions
  265. err = reflectutils.FindAnonymouStructPointer(optStruct, &optionsRef)
  266. if err != nil {
  267. log.Fatalf("Find common options fail: %s", err)
  268. }
  269. if optionsRef.Help {
  270. fmt.Println(parser.HelpString())
  271. os.Exit(0)
  272. }
  273. if optionsRef.Version {
  274. fmt.Printf("Yunion cloud version:\n%s", version.GetJsonString())
  275. os.Exit(0)
  276. }
  277. if len(optionsRef.Config) == 0 {
  278. for _, p := range []string{"./etc", "/etc/yunion"} {
  279. confTmp := path.Join(p, configFileName)
  280. if _, err := os.Stat(confTmp); err == nil {
  281. optionsRef.Config = confTmp
  282. break
  283. }
  284. }
  285. }
  286. if len(optionsRef.Config) > 0 {
  287. if !fileutils2.Exists(optionsRef.Config) && !ignoreNoConfigfile {
  288. log.Fatalf("Configuration file %s not exist", optionsRef.Config)
  289. } else if fileutils2.Exists(optionsRef.Config) {
  290. log.Infof("Use configuration file: %s", optionsRef.Config)
  291. err = parser.ParseFile(optionsRef.Config)
  292. if err != nil {
  293. log.Fatalf("Parse configuration file: %v", err)
  294. }
  295. }
  296. }
  297. parser.SetDefault()
  298. if len(optionsRef.ApplicationID) == 0 {
  299. optionsRef.ApplicationID = serviceName
  300. }
  301. consts.SetServiceName(optionsRef.ApplicationID)
  302. httperrors.SetTimeZone(optionsRef.TimeZone)
  303. netutils2.SetIp4MetadataServers(optionsRef.MetadataServerIp4s)
  304. netutils2.SetIp6MetadataServers(optionsRef.MetadataServerIp6s)
  305. // log configuration
  306. log.SetVerboseLevel(int32(optionsRef.LogVerboseLevel))
  307. err = log.SetLogLevelByString(log.Logger(), optionsRef.LogLevel)
  308. if err != nil {
  309. log.Fatalf("Set log level %q: %v", optionsRef.LogLevel, err)
  310. }
  311. log.Infof("Set log level to %q", optionsRef.LogLevel)
  312. log.Logger().Formatter = &log.TextFormatter{
  313. TimeZone: optionsRef.LogWithTimeZone,
  314. TimestampFormat: optionsRef.LogTimestampFormat,
  315. }
  316. if optionsRef.LogFilePrefix != "" {
  317. dir, name := filepath.Split(optionsRef.LogFilePrefix)
  318. h := &hooks.LogFileRotateHook{
  319. RotateNum: 10,
  320. RotateSize: 100 * 1024 * 1024,
  321. LogFileHook: hooks.LogFileHook{
  322. FileDir: dir,
  323. FileName: name,
  324. },
  325. }
  326. h.Init()
  327. log.DisableColors()
  328. log.Logger().AddHook(h)
  329. log.Logger().Out = io.Discard
  330. atexit.Register(atexit.ExitHandler{
  331. Prio: atexit.PRIO_LOG_CLOSE,
  332. Reason: "deinit log rotate hook",
  333. Func: func(atexit.ExitHandler) {
  334. h.DeInit()
  335. },
  336. })
  337. }
  338. log.V(10).Debugf("Parsed options: %#v", optStruct)
  339. if len(optionsRef.Region) > 0 {
  340. consts.SetRegion(optionsRef.Region)
  341. }
  342. consts.SetDefaultPolicy(optionsRef.EnableDefaultPolicy)
  343. consts.SetDomainizedNamespace(optionsRef.DomainizedNamespace)
  344. consts.SetTaskWorkerCount(optionsRef.TaskWorkerCount)
  345. consts.SetLocalTaskWorkerCount(optionsRef.LocalTaskWorkerCount)
  346. consts.SetTaskArchiveThresholdHours(optionsRef.TaskArchiveThresholdHours)
  347. if optionsRef.Address == "0.0.0.0" {
  348. optionsRef.Address = ""
  349. }
  350. }
  351. func (self *BaseOptions) HttpTransportProxyFunc() httputils.TransportProxyFunc {
  352. cfg := &httpproxy.Config{
  353. HTTPProxy: self.GlobalHTTPProxy,
  354. HTTPSProxy: self.GlobalHTTPSProxy,
  355. }
  356. proxyFunc := cfg.ProxyFunc()
  357. return func(req *http.Request) (*url.URL, error) {
  358. return proxyFunc(req.URL)
  359. }
  360. }
  361. func (opt *BaseOptions) GetPlatformName(lang language.Tag) string {
  362. if len(opt.PlatformNames) > 0 {
  363. if name, ok := opt.PlatformNames[lang.String()]; ok {
  364. return name
  365. }
  366. }
  367. return opt.PlatformName
  368. }