PageRenderTime 15ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/github.com/mistifyio/go-zfs/utils.go

https://bitbucket.org/Jake-Qu/kubernetes-mirror
Go | 323 lines | 261 code | 43 blank | 19 comment | 63 complexity | be0c8c509f6c654b744e9064485a0ed8 MD5 | raw file
Possible License(s): MIT, MPL-2.0-no-copyleft-exception, 0BSD, CC0-1.0, BSD-2-Clause, Apache-2.0, BSD-3-Clause
  1. package zfs
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "os/exec"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "github.com/pborman/uuid"
  11. )
  12. type command struct {
  13. Command string
  14. Stdin io.Reader
  15. Stdout io.Writer
  16. }
  17. func (c *command) Run(arg ...string) ([][]string, error) {
  18. cmd := exec.Command(c.Command, arg...)
  19. var stdout, stderr bytes.Buffer
  20. if c.Stdout == nil {
  21. cmd.Stdout = &stdout
  22. } else {
  23. cmd.Stdout = c.Stdout
  24. }
  25. if c.Stdin != nil {
  26. cmd.Stdin = c.Stdin
  27. }
  28. cmd.Stderr = &stderr
  29. id := uuid.New()
  30. joinedArgs := strings.Join(cmd.Args, " ")
  31. logger.Log([]string{"ID:" + id, "START", joinedArgs})
  32. err := cmd.Run()
  33. logger.Log([]string{"ID:" + id, "FINISH"})
  34. if err != nil {
  35. return nil, &Error{
  36. Err: err,
  37. Debug: strings.Join([]string{cmd.Path, joinedArgs}, " "),
  38. Stderr: stderr.String(),
  39. }
  40. }
  41. // assume if you passed in something for stdout, that you know what to do with it
  42. if c.Stdout != nil {
  43. return nil, nil
  44. }
  45. lines := strings.Split(stdout.String(), "\n")
  46. //last line is always blank
  47. lines = lines[0 : len(lines)-1]
  48. output := make([][]string, len(lines))
  49. for i, l := range lines {
  50. output[i] = strings.Fields(l)
  51. }
  52. return output, nil
  53. }
  54. func setString(field *string, value string) {
  55. v := ""
  56. if value != "-" {
  57. v = value
  58. }
  59. *field = v
  60. }
  61. func setUint(field *uint64, value string) error {
  62. var v uint64
  63. if value != "-" {
  64. var err error
  65. v, err = strconv.ParseUint(value, 10, 64)
  66. if err != nil {
  67. return err
  68. }
  69. }
  70. *field = v
  71. return nil
  72. }
  73. func (ds *Dataset) parseLine(line []string) error {
  74. prop := line[1]
  75. val := line[2]
  76. var err error
  77. switch prop {
  78. case "available":
  79. err = setUint(&ds.Avail, val)
  80. case "compression":
  81. setString(&ds.Compression, val)
  82. case "mountpoint":
  83. setString(&ds.Mountpoint, val)
  84. case "quota":
  85. err = setUint(&ds.Quota, val)
  86. case "type":
  87. setString(&ds.Type, val)
  88. case "origin":
  89. setString(&ds.Origin, val)
  90. case "used":
  91. err = setUint(&ds.Used, val)
  92. case "volsize":
  93. err = setUint(&ds.Volsize, val)
  94. case "written":
  95. err = setUint(&ds.Written, val)
  96. case "logicalused":
  97. err = setUint(&ds.Logicalused, val)
  98. }
  99. return err
  100. }
  101. /*
  102. * from zfs diff`s escape function:
  103. *
  104. * Prints a file name out a character at a time. If the character is
  105. * not in the range of what we consider "printable" ASCII, display it
  106. * as an escaped 3-digit octal value. ASCII values less than a space
  107. * are all control characters and we declare the upper end as the
  108. * DELete character. This also is the last 7-bit ASCII character.
  109. * We choose to treat all 8-bit ASCII as not printable for this
  110. * application.
  111. */
  112. func unescapeFilepath(path string) (string, error) {
  113. buf := make([]byte, 0, len(path))
  114. llen := len(path)
  115. for i := 0; i < llen; {
  116. if path[i] == '\\' {
  117. if llen < i+4 {
  118. return "", fmt.Errorf("Invalid octal code: too short")
  119. }
  120. octalCode := path[(i + 1):(i + 4)]
  121. val, err := strconv.ParseUint(octalCode, 8, 8)
  122. if err != nil {
  123. return "", fmt.Errorf("Invalid octal code: %v", err)
  124. }
  125. buf = append(buf, byte(val))
  126. i += 4
  127. } else {
  128. buf = append(buf, path[i])
  129. i++
  130. }
  131. }
  132. return string(buf), nil
  133. }
  134. var changeTypeMap = map[string]ChangeType{
  135. "-": Removed,
  136. "+": Created,
  137. "M": Modified,
  138. "R": Renamed,
  139. }
  140. var inodeTypeMap = map[string]InodeType{
  141. "B": BlockDevice,
  142. "C": CharacterDevice,
  143. "/": Directory,
  144. ">": Door,
  145. "|": NamedPipe,
  146. "@": SymbolicLink,
  147. "P": EventPort,
  148. "=": Socket,
  149. "F": File,
  150. }
  151. // matches (+1) or (-1)
  152. var referenceCountRegex = regexp.MustCompile("\\(([+-]\\d+?)\\)")
  153. func parseReferenceCount(field string) (int, error) {
  154. matches := referenceCountRegex.FindStringSubmatch(field)
  155. if matches == nil {
  156. return 0, fmt.Errorf("Regexp does not match")
  157. }
  158. return strconv.Atoi(matches[1])
  159. }
  160. func parseInodeChange(line []string) (*InodeChange, error) {
  161. llen := len(line)
  162. if llen < 1 {
  163. return nil, fmt.Errorf("Empty line passed")
  164. }
  165. changeType := changeTypeMap[line[0]]
  166. if changeType == 0 {
  167. return nil, fmt.Errorf("Unknown change type '%s'", line[0])
  168. }
  169. switch changeType {
  170. case Renamed:
  171. if llen != 4 {
  172. return nil, fmt.Errorf("Mismatching number of fields: expect 4, got: %d", llen)
  173. }
  174. case Modified:
  175. if llen != 4 && llen != 3 {
  176. return nil, fmt.Errorf("Mismatching number of fields: expect 3..4, got: %d", llen)
  177. }
  178. default:
  179. if llen != 3 {
  180. return nil, fmt.Errorf("Mismatching number of fields: expect 3, got: %d", llen)
  181. }
  182. }
  183. inodeType := inodeTypeMap[line[1]]
  184. if inodeType == 0 {
  185. return nil, fmt.Errorf("Unknown inode type '%s'", line[1])
  186. }
  187. path, err := unescapeFilepath(line[2])
  188. if err != nil {
  189. return nil, fmt.Errorf("Failed to parse filename: %v", err)
  190. }
  191. var newPath string
  192. var referenceCount int
  193. switch changeType {
  194. case Renamed:
  195. newPath, err = unescapeFilepath(line[3])
  196. if err != nil {
  197. return nil, fmt.Errorf("Failed to parse filename: %v", err)
  198. }
  199. case Modified:
  200. if llen == 4 {
  201. referenceCount, err = parseReferenceCount(line[3])
  202. if err != nil {
  203. return nil, fmt.Errorf("Failed to parse reference count: %v", err)
  204. }
  205. }
  206. default:
  207. newPath = ""
  208. }
  209. return &InodeChange{
  210. Change: changeType,
  211. Type: inodeType,
  212. Path: path,
  213. NewPath: newPath,
  214. ReferenceCountChange: referenceCount,
  215. }, nil
  216. }
  217. // example input
  218. //M / /testpool/bar/
  219. //+ F /testpool/bar/hello.txt
  220. //M / /testpool/bar/hello.txt (+1)
  221. //M / /testpool/bar/hello-hardlink
  222. func parseInodeChanges(lines [][]string) ([]*InodeChange, error) {
  223. changes := make([]*InodeChange, len(lines))
  224. for i, line := range lines {
  225. c, err := parseInodeChange(line)
  226. if err != nil {
  227. return nil, fmt.Errorf("Failed to parse line %d of zfs diff: %v, got: '%s'", i, err, line)
  228. }
  229. changes[i] = c
  230. }
  231. return changes, nil
  232. }
  233. func listByType(t, filter string) ([]*Dataset, error) {
  234. args := []string{"get", "-rHp", "-t", t, "all"}
  235. if filter != "" {
  236. args = append(args, filter)
  237. }
  238. out, err := zfs(args...)
  239. if err != nil {
  240. return nil, err
  241. }
  242. var datasets []*Dataset
  243. name := ""
  244. var ds *Dataset
  245. for _, line := range out {
  246. if name != line[0] {
  247. name = line[0]
  248. ds = &Dataset{Name: name}
  249. datasets = append(datasets, ds)
  250. }
  251. if err := ds.parseLine(line); err != nil {
  252. return nil, err
  253. }
  254. }
  255. return datasets, nil
  256. }
  257. func propsSlice(properties map[string]string) []string {
  258. args := make([]string, 0, len(properties)*3)
  259. for k, v := range properties {
  260. args = append(args, "-o")
  261. args = append(args, fmt.Sprintf("%s=%s", k, v))
  262. }
  263. return args
  264. }
  265. func (z *Zpool) parseLine(line []string) error {
  266. prop := line[1]
  267. val := line[2]
  268. var err error
  269. switch prop {
  270. case "health":
  271. setString(&z.Health, val)
  272. case "allocated":
  273. err = setUint(&z.Allocated, val)
  274. case "size":
  275. err = setUint(&z.Size, val)
  276. case "free":
  277. err = setUint(&z.Free, val)
  278. }
  279. return err
  280. }