cubefs

Форк
0
/
quota.go 
436 строк · 12.6 Кб
1
// Copyright 2023 The CubeFS Authors.
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
12
// implied. See the License for the specific language governing
13
// permissions and limitations under the License.
14

15
package cmd
16

17
import (
18
	"fmt"
19
	"math"
20
	"sort"
21
	"strconv"
22
	"strings"
23

24
	"github.com/cubefs/cubefs/proto"
25
	"github.com/cubefs/cubefs/sdk/master"
26
	"github.com/cubefs/cubefs/sdk/meta"
27
	"github.com/spf13/cobra"
28
)
29

30
const (
31
	cmdQuotaUse           = "quota [COMMAND]"
32
	cmdQuotaShort         = "Manage cluster quota"
33
	cmdQuotaCreateUse     = "create [volname] [fullpath1,fullpath2]"
34
	cmdQuotaCreateShort   = "create paths quota"
35
	cmdQuotaListUse       = "list [volname]"
36
	cmdQuotaListShort     = "list volname all quota"
37
	cmdQuotaUpdateUse     = "update [volname] [quotaId]"
38
	cmdQuotaUpdateShort   = "update quota files or bytes by id"
39
	cmdQuotaDeleteUse     = "delete [volname] [quotaId]"
40
	cmdQUotaDeleteShort   = "delete quota by id"
41
	cmdQuotaGetInodeUse   = "getInode [volname] [inode]"
42
	cmdQuotaGetInodeShort = "get inode quotaInfo"
43
	cmdQuotaListAllUse    = "listAll"
44
	cmdQuotaListAllShort  = "list all volname has quota"
45
	cmdQuotaApplyUse      = "apply [volname] [quotaId]"
46
	cmdQuotaApplyShort    = "apply quota"
47
	cmdQuotaRevokeUse     = "revoke [volname] [quotaId]"
48
	cmdQuotaRevokeShort   = "revoke quota"
49
)
50

51
const (
52
	cmdQuotaDefaultMaxFiles = math.MaxUint64
53
	cmdQuotaDefaultMaxBytes = math.MaxUint64
54
)
55

56
func newQuotaCmd(client *master.MasterClient) *cobra.Command {
57
	cmd := &cobra.Command{
58
		Use:     cmdQuotaUse,
59
		Short:   cmdQuotaShort,
60
		Args:    cobra.MinimumNArgs(0),
61
		Aliases: []string{"quota"},
62
	}
63
	proto.InitBufferPool(32768)
64
	cmd.AddCommand(
65
		newQuotaListCmd(client),
66
		newQuotaCreateCmd(client),
67
		newQuotaUpdateCmd(client),
68
		newQuotaDelete(client),
69
		newQuotaGetInode(client),
70
		newQuotaListAllCmd(client),
71
		newQuotaApplyCmd(client),
72
		newQuotaRevokeCmd(client),
73
	)
74
	return cmd
75
}
76

77
func newQuotaCreateCmd(client *master.MasterClient) *cobra.Command {
78
	var maxFiles uint64
79
	var maxBytes uint64
80

81
	cmd := &cobra.Command{
82
		Use:   cmdQuotaCreateUse,
83
		Short: cmdQuotaCreateShort,
84
		Args:  cobra.MinimumNArgs(2),
85
		Run: func(cmd *cobra.Command, args []string) {
86
			var err error
87
			volName := args[0]
88
			fullPath := args[1]
89

90
			metaConfig := &meta.MetaConfig{
91
				Volume:  volName,
92
				Masters: client.Nodes(),
93
			}
94
			metaWrapper, err := meta.NewMetaWrapper(metaConfig)
95
			if err != nil {
96
				stdout("NewMetaWrapper failed: %v\n", err)
97
				return
98
			}
99
			fullPaths := strings.Split(fullPath, ",")
100
			err = checkNestedDirectories(fullPaths)
101
			if err != nil {
102
				stdout("create quota failed, fullPaths %v error %v.\n", fullPaths, err)
103
				return
104
			}
105
			if len(fullPaths) > 5 {
106
				stdout("create quota failed, fullPath %v has more than 5 path.\n", fullPaths)
107
				return
108
			}
109
			quotaPathInofs := make([]proto.QuotaPathInfo, 0)
110
			for _, path := range fullPaths {
111
				var quotaPathInfo proto.QuotaPathInfo
112
				quotaPathInfo.FullPath = path
113

114
				if !strings.HasPrefix(path, "/") {
115
					stdout("create quota failed, path %v does not start with / \n", path)
116
					return
117
				}
118

119
				inodeId, err := metaWrapper.LookupPath(path)
120
				if err != nil {
121
					stdout("get inode by fullPath %v fail %v\n", path, err)
122
					return
123
				}
124
				quotaPathInfo.RootInode = inodeId
125
				inodeInfo, err := metaWrapper.InodeGet_ll(inodeId)
126
				if err != nil {
127
					stdout("get inode %v info fail %v\n", inodeId, err)
128
					return
129
				}
130

131
				if !proto.IsDir(inodeInfo.Mode) {
132
					stdout("create quota failed, inode [%v] is not dir\n", inodeId)
133
					return
134
				}
135

136
				mp := metaWrapper.GetPartitionByInodeId_ll(inodeId)
137
				if mp == nil {
138
					stdout("can not find mp by inodeId: %v\n", inodeId)
139
					return
140
				}
141
				quotaPathInfo.PartitionId = mp.PartitionID
142
				quotaPathInofs = append(quotaPathInofs, quotaPathInfo)
143
			}
144
			var quotaId uint32
145
			if quotaId, err = client.AdminAPI().CreateQuota(volName, quotaPathInofs, maxFiles, maxBytes); err != nil {
146
				stdout("volName %v path %v quota create failed(%v)\n", volName, fullPath, err)
147
				return
148
			}
149
			stdout("createQuota: volName %v path %v  maxFiles %v maxBytes %v quotaId %v success.\n",
150
				volName, fullPath, maxFiles, maxBytes, quotaId)
151
		},
152
	}
153
	cmd.Flags().Uint64Var(&maxFiles, CliFlagMaxFiles, cmdQuotaDefaultMaxFiles, "Specify quota max files")
154
	cmd.Flags().Uint64Var(&maxBytes, CliFlagMaxBytes, cmdQuotaDefaultMaxBytes, "Specify quota max bytes")
155
	return cmd
156
}
157

158
func newQuotaListCmd(client *master.MasterClient) *cobra.Command {
159
	cmd := &cobra.Command{
160
		Use:   cmdQuotaListUse,
161
		Short: cmdQuotaListShort,
162
		Args:  cobra.MinimumNArgs(1),
163
		Run: func(cmd *cobra.Command, args []string) {
164
			var quotas []*proto.QuotaInfo
165
			var err error
166
			volName := args[0]
167
			if quotas, err = client.AdminAPI().ListQuota(volName); err != nil {
168
				stdout("volName %v quota list failed(%v)\n", volName, err)
169
				return
170
			}
171
			sort.Slice(quotas, func(i, j int) bool {
172
				return quotas[i].QuotaId < quotas[j].QuotaId
173
			})
174
			stdout("[quotas]\n")
175
			stdout("%v\n", formatQuotaTableHeader())
176
			for _, quotaInfo := range quotas {
177
				stdout("%v\n", formatQuotaInfo(quotaInfo))
178
			}
179
		},
180
	}
181
	return cmd
182
}
183

184
func newQuotaListAllCmd(client *master.MasterClient) *cobra.Command {
185
	cmd := &cobra.Command{
186
		Use:   cmdQuotaListAllUse,
187
		Short: cmdQuotaListAllShort,
188
		Args:  cobra.MinimumNArgs(0),
189
		Run: func(cmd *cobra.Command, args []string) {
190
			var err error
191
			var vols []*proto.VolInfo
192

193
			if vols, err = client.AdminAPI().ListQuotaAll(); err != nil {
194
				stdout("quota list all failed(%v)\n", err)
195
				return
196
			}
197
			stdout("%v\n", volumeInfoTableHeader)
198
			for _, vol := range vols {
199
				stdout("%v\n", formatVolInfoTableRow(vol))
200
			}
201
		},
202
	}
203

204
	return cmd
205
}
206

207
func newQuotaUpdateCmd(client *master.MasterClient) *cobra.Command {
208
	var maxFiles uint64
209
	var maxBytes uint64
210

211
	cmd := &cobra.Command{
212
		Use:   cmdQuotaUpdateUse,
213
		Short: cmdQuotaUpdateShort,
214
		Args:  cobra.MinimumNArgs(2),
215
		Run: func(cmd *cobra.Command, args []string) {
216
			volName := args[0]
217
			quotaId := args[1]
218

219
			quotaInfo, err := client.AdminAPI().GetQuota(volName, quotaId)
220
			if err != nil {
221
				stdout("get quota vol: %v ,quotaId: %v failed err %v.\n", volName, quotaId, err)
222
				return
223
			}
224
			if maxFiles == 0 {
225
				maxFiles = quotaInfo.MaxFiles
226
			}
227
			if maxBytes == 0 {
228
				maxBytes = quotaInfo.MaxBytes
229
			}
230
			if err = client.AdminAPI().UpdateQuota(volName, quotaId, maxFiles, maxBytes); err != nil {
231
				stdout("volName %v quotaId %v quota update failed(%v)\n", volName, quotaId, err)
232
				return
233
			}
234
			stdout("updateQuota: volName %v quotaId %v maxFiles %v maxBytes %v success.\n",
235
				volName, quotaId, maxFiles, maxBytes)
236
		},
237
	}
238
	cmd.Flags().Uint64Var(&maxFiles, CliFlagMaxFiles, 0, "Specify quota max files")
239
	cmd.Flags().Uint64Var(&maxBytes, CliFlagMaxBytes, 0, "Specify quota max bytes")
240
	return cmd
241
}
242

243
func newQuotaDelete(client *master.MasterClient) *cobra.Command {
244
	var optYes bool
245
	cmd := &cobra.Command{
246
		Use:   cmdQuotaDeleteUse,
247
		Short: cmdQUotaDeleteShort,
248
		Args:  cobra.MinimumNArgs(2),
249
		Run: func(cmd *cobra.Command, args []string) {
250
			volName := args[0]
251
			quotaId := args[1]
252
			var err error
253
			if !optYes {
254
				stdout("Before deleting the quota, please confirm that the quota of the inode in the subdirectory has been cleared\n")
255
				stdout("ensure that the quota list %v usedFiles is displayed as 0\n", volName)
256
				stdout("\nConfirm (yes/no)[yes]:")
257
				var userConfirm string
258
				_, _ = fmt.Scanln(&userConfirm)
259
				if userConfirm != "yes" {
260
					stdout("Abort by user.\n")
261
					return
262
				}
263
			}
264
			if err = client.AdminAPI().DeleteQuota(volName, quotaId); err != nil {
265
				stdout("volName %v quotaId %v quota delete failed(%v)\n", volName, quotaId, err)
266
				return
267
			}
268
			stdout("deleteQuota: volName %v quotaId %v success.\n", volName, quotaId)
269
		},
270
	}
271
	cmd.Flags().BoolVarP(&optYes, "yes", "y", false, "Do not prompt to clear the quota of inodes")
272
	return cmd
273
}
274

275
func newQuotaGetInode(client *master.MasterClient) *cobra.Command {
276
	cmd := &cobra.Command{
277
		Use:   cmdQuotaGetInodeUse,
278
		Short: cmdQuotaGetInodeShort,
279
		Args:  cobra.MinimumNArgs(2),
280
		Run: func(cmd *cobra.Command, args []string) {
281
			volName := args[0]
282
			inodeId, err := strconv.ParseUint(args[1], 10, 64)
283
			if err != nil {
284
				stdout("inodeId %v is illegal", args[2])
285
				return
286
			}
287

288
			metaConfig := &meta.MetaConfig{
289
				Volume:  volName,
290
				Masters: client.Nodes(),
291
			}
292
			metaWrapper, err := meta.NewMetaWrapper(metaConfig)
293
			if err != nil {
294
				stdout("NewMetaWrapper failed: %v\n", err)
295
				return
296
			}
297

298
			quotaInfos, err := metaWrapper.GetInodeQuota_ll(inodeId)
299
			if err != nil {
300
				stdout("get indoe quota failed %v\n", err)
301
				return
302
			}
303
			for quotaId, quotaInfo := range quotaInfos {
304
				stdout("quotaId [%v] quotaInfo [%v] \n", quotaId, quotaInfo)
305
			}
306
		},
307
	}
308
	return cmd
309
}
310

311
func newQuotaApplyCmd(client *master.MasterClient) *cobra.Command {
312
	var maxConcurrencyInode uint64
313
	cmd := &cobra.Command{
314
		Use:   cmdQuotaApplyUse,
315
		Short: cmdQuotaApplyShort,
316
		Args:  cobra.MinimumNArgs(2),
317
		Run: func(cmd *cobra.Command, args []string) {
318
			volName := args[0]
319
			quotaId := args[1]
320
			var err error
321
			var quotaInfo *proto.QuotaInfo
322

323
			if quotaInfo, err = client.AdminAPI().GetQuota(volName, quotaId); err != nil {
324
				stdout("volName %v get quota %v failed(%v)\n", volName, quotaId, err)
325
				return
326
			}
327

328
			metaConfig := &meta.MetaConfig{
329
				Volume:  volName,
330
				Masters: client.Nodes(),
331
			}
332

333
			metaWrapper, err := meta.NewMetaWrapper(metaConfig)
334
			if err != nil {
335
				stdout("NewMetaWrapper failed: %v\n", err)
336
				return
337
			}
338
			var totalNums uint64
339
			var quotaIdNum uint32
340
			tmp, err := strconv.ParseUint(quotaId, 10, 32)
341
			if err != nil {
342
				stdout("quotaId %v is illegal", quotaId)
343
				return
344
			}
345
			quotaIdNum = uint32(tmp)
346
			for _, pathInfo := range quotaInfo.PathInfos {
347
				inodeNums, err := metaWrapper.ApplyQuota_ll(pathInfo.RootInode, quotaIdNum, maxConcurrencyInode)
348
				if err != nil {
349
					stdout("apply quota failed: %v\n", err)
350
					return
351
				}
352
				totalNums += inodeNums
353
			}
354
			stdout("apply quota num [%v] success.\n", totalNums)
355
		},
356
	}
357
	cmd.Flags().Uint64Var(&maxConcurrencyInode, CliFlagMaxConcurrencyInode, 1000, "max concurrency set Inodes")
358
	return cmd
359
}
360

361
func newQuotaRevokeCmd(client *master.MasterClient) *cobra.Command {
362
	var maxConcurrencyInode uint64
363
	var forceInode uint64
364
	cmd := &cobra.Command{
365
		Use:   cmdQuotaRevokeUse,
366
		Short: cmdQuotaRevokeShort,
367
		Args:  cobra.MinimumNArgs(2),
368
		Run: func(cmd *cobra.Command, args []string) {
369
			volName := args[0]
370
			quotaId := args[1]
371
			var err error
372
			var quotaInfo *proto.QuotaInfo
373
			var totalNums uint64
374

375
			metaConfig := &meta.MetaConfig{
376
				Volume:  volName,
377
				Masters: client.Nodes(),
378
			}
379

380
			metaWrapper, err := meta.NewMetaWrapper(metaConfig)
381
			if err != nil {
382
				stdout("NewMetaWrapper failed: %v\n", err)
383
				return
384
			}
385
			var quotaIdNum uint32
386
			tmp, err := strconv.ParseUint(quotaId, 10, 32)
387
			if err != nil {
388
				stdout("quotaId %v is illegal", quotaId)
389
				return
390
			}
391
			quotaIdNum = uint32(tmp)
392
			if forceInode == 0 {
393
				if quotaInfo, err = client.AdminAPI().GetQuota(volName, quotaId); err != nil {
394
					stdout("volName %v get quota %v failed(%v)\n", volName, quotaId, err)
395
					return
396
				}
397

398
				for _, pathInfo := range quotaInfo.PathInfos {
399
					inodeNums, err := metaWrapper.RevokeQuota_ll(pathInfo.RootInode, quotaIdNum, maxConcurrencyInode)
400
					if err != nil {
401
						stdout("revoke quota inodeNums %v failed %v\n", inodeNums, err)
402
					}
403
					totalNums += inodeNums
404
				}
405
			} else {
406
				totalNums, err = metaWrapper.RevokeQuota_ll(forceInode, quotaIdNum, maxConcurrencyInode)
407
				if err != nil {
408
					stdout("revoke quota inodeNums %v failed %v\n", totalNums, err)
409
				}
410
			}
411
			stdout("revoke num [%v] success.\n", totalNums)
412
		},
413
	}
414
	cmd.Flags().Uint64Var(&maxConcurrencyInode, CliFlagMaxConcurrencyInode, 1000, "max concurrency delete Inodes")
415
	cmd.Flags().Uint64Var(&forceInode, CliFlagForceInode, 0, "force revoke quota inode")
416
	return cmd
417
}
418

419
func checkNestedDirectories(paths []string) error {
420
	for i, path := range paths {
421
		for j := i + 1; j < len(paths); j++ {
422
			if path == paths[j] {
423
				return fmt.Errorf("the same directories found: %s", path)
424
			}
425

426
			if proto.IsAncestor(path, paths[j]) {
427
				return fmt.Errorf("Nested directories found: %s and %s", path, paths[j])
428
			}
429

430
			if proto.IsAncestor(paths[j], path) {
431
				return fmt.Errorf("Nested directories found: %s and %s", path, paths[j])
432
			}
433
		}
434
	}
435
	return nil
436
}
437

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.