15 "github.com/Sirupsen/logrus"
16 "github.com/pkg/errors"
19 type directLVMConfig struct {
22 ThinpMetaPercent uint64
23 AutoExtendPercent uint64
24 AutoExtendThreshold uint64
28 errThinpPercentMissing = errors.New("must set both `dm.thinp_percent` and `dm.thinp_metapercent` if either is specified")
29 errThinpPercentTooBig = errors.New("combined `dm.thinp_percent` and `dm.thinp_metapercent` must not be greater than 100")
30 errMissingSetupDevice = errors.New("must provide device path in `dm.setup_device` in order to configure direct-lvm")
33 func validateLVMConfig(cfg directLVMConfig) error {
34 if reflect.DeepEqual(cfg, directLVMConfig{}) {
38 return errMissingSetupDevice
40 if (cfg.ThinpPercent > 0 && cfg.ThinpMetaPercent == 0) || cfg.ThinpMetaPercent > 0 && cfg.ThinpPercent == 0 {
41 return errThinpPercentMissing
44 if cfg.ThinpPercent+cfg.ThinpMetaPercent > 100 {
45 return errThinpPercentTooBig
50 func checkDevAvailable(dev string) error {
51 lvmScan, err := exec.LookPath("lvmdiskscan")
53 logrus.Debug("could not find lvmdiskscan")
57 out, err := exec.Command(lvmScan).CombinedOutput()
59 logrus.WithError(err).Error(string(out))
63 if !bytes.Contains(out, []byte(dev)) {
64 return errors.Errorf("%s is not available for use with devicemapper", dev)
69 func checkDevInVG(dev string) error {
70 pvDisplay, err := exec.LookPath("pvdisplay")
72 logrus.Debug("could not find pvdisplay")
76 out, err := exec.Command(pvDisplay, dev).CombinedOutput()
78 logrus.WithError(err).Error(string(out))
82 scanner := bufio.NewScanner(bytes.NewReader(bytes.TrimSpace(out)))
84 fields := strings.SplitAfter(strings.TrimSpace(scanner.Text()), "VG Name")
86 // got "VG Name" line"
87 vg := strings.TrimSpace(fields[1])
89 return errors.Errorf("%s is already part of a volume group %q: must remove this device from any volume group or provide a different device", dev, vg)
98 func checkDevHasFS(dev string) error {
99 blkid, err := exec.LookPath("blkid")
101 logrus.Debug("could not find blkid")
105 out, err := exec.Command(blkid, dev).CombinedOutput()
107 logrus.WithError(err).Error(string(out))
111 fields := bytes.Fields(out)
112 for _, f := range fields {
113 kv := bytes.Split(f, []byte{'='})
114 if bytes.Equal(kv[0], []byte("TYPE")) {
115 v := bytes.Trim(kv[1], "\"")
117 return errors.Errorf("%s has a filesystem already, use dm.directlvm_device_force=true if you want to wipe the device", dev)
125 func verifyBlockDevice(dev string, force bool) error {
126 if err := checkDevAvailable(dev); err != nil {
129 if err := checkDevInVG(dev); err != nil {
137 if err := checkDevHasFS(dev); err != nil {
143 func readLVMConfig(root string) (directLVMConfig, error) {
144 var cfg directLVMConfig
146 p := filepath.Join(root, "setup-config.json")
147 b, err := ioutil.ReadFile(p)
149 if os.IsNotExist(err) {
152 return cfg, errors.Wrap(err, "error reading existing setup config")
155 // check if this is just an empty file, no need to produce a json error later if so
160 err = json.Unmarshal(b, &cfg)
161 return cfg, errors.Wrap(err, "error unmarshaling previous device setup config")
164 func writeLVMConfig(root string, cfg directLVMConfig) error {
165 p := filepath.Join(root, "setup-config.json")
166 b, err := json.Marshal(cfg)
168 return errors.Wrap(err, "error marshalling direct lvm config")
170 err = ioutil.WriteFile(p, b, 0600)
171 return errors.Wrap(err, "error writing direct lvm config to file")
174 func setupDirectLVM(cfg directLVMConfig) error {
175 pvCreate, err := exec.LookPath("pvcreate")
177 return errors.Wrap(err, "error lookuping up command `pvcreate` while setting up direct lvm")
180 vgCreate, err := exec.LookPath("vgcreate")
182 return errors.Wrap(err, "error lookuping up command `vgcreate` while setting up direct lvm")
185 lvCreate, err := exec.LookPath("lvcreate")
187 return errors.Wrap(err, "error lookuping up command `lvcreate` while setting up direct lvm")
190 lvConvert, err := exec.LookPath("lvconvert")
192 return errors.Wrap(err, "error lookuping up command `lvconvert` while setting up direct lvm")
195 lvChange, err := exec.LookPath("lvchange")
197 return errors.Wrap(err, "error lookuping up command `lvchange` while setting up direct lvm")
200 if cfg.AutoExtendPercent == 0 {
201 cfg.AutoExtendPercent = 20
204 if cfg.AutoExtendThreshold == 0 {
205 cfg.AutoExtendThreshold = 80
208 if cfg.ThinpPercent == 0 {
209 cfg.ThinpPercent = 95
211 if cfg.ThinpMetaPercent == 0 {
212 cfg.ThinpMetaPercent = 1
215 out, err := exec.Command(pvCreate, "-f", cfg.Device).CombinedOutput()
217 return errors.Wrap(err, string(out))
220 out, err = exec.Command(vgCreate, "docker", cfg.Device).CombinedOutput()
222 return errors.Wrap(err, string(out))
225 out, err = exec.Command(lvCreate, "--wipesignatures", "y", "-n", "thinpool", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpPercent)).CombinedOutput()
227 return errors.Wrap(err, string(out))
229 out, err = exec.Command(lvCreate, "--wipesignatures", "y", "-n", "thinpoolmeta", "docker", "--extents", fmt.Sprintf("%d%%VG", cfg.ThinpMetaPercent)).CombinedOutput()
231 return errors.Wrap(err, string(out))
234 out, err = exec.Command(lvConvert, "-y", "--zero", "n", "-c", "512K", "--thinpool", "docker/thinpool", "--poolmetadata", "docker/thinpoolmeta").CombinedOutput()
236 return errors.Wrap(err, string(out))
239 profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent)
240 err = ioutil.WriteFile("/etc/lvm/profile/docker-thinpool.profile", []byte(profile), 0600)
242 return errors.Wrap(err, "error writing docker thinp autoextend profile")
245 out, err = exec.Command(lvChange, "--metadataprofile", "docker-thinpool", "docker/thinpool").CombinedOutput()
246 return errors.Wrap(err, string(out))