@@ -58,6 +58,7 @@ func NewPoolDevice(ctx context.Context, config *Config) (*PoolDevice, error) {
5858 if _ , err := dmsetup .Info (poolPath ); err != nil {
5959 return nil , errors .Wrapf (err , "failed to query pool %q" , poolPath )
6060 }
61+
6162 return & PoolDevice {
6263 poolName : config .PoolName ,
6364 metadata : poolMetaStore ,
@@ -108,7 +109,7 @@ func (p *PoolDevice) transition(ctx context.Context, deviceName string, tryingSt
108109// CreateThinDevice creates new devmapper thin-device with given name and size.
109110// Device ID for thin-device will be allocated from metadata store.
110111// If allocation successful, device will be activated with /dev/mapper/<deviceName>
111- func (p * PoolDevice ) CreateThinDevice (ctx context.Context , deviceName string , virtualSizeBytes uint64 ) error {
112+ func (p * PoolDevice ) CreateThinDevice (ctx context.Context , deviceName string , virtualSizeBytes uint64 ) ( retErr error ) {
112113 info := & DeviceInfo {
113114 Name : deviceName ,
114115 Size : virtualSizeBytes ,
@@ -120,15 +121,46 @@ func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, vi
120121 return errors .Wrapf (err , "failed to save initial metadata for new thin device %q" , deviceName )
121122 }
122123
124+ defer func () {
125+ if retErr == nil {
126+ return
127+ }
128+
129+ // Rollback metadata
130+ retErr = multierror .Append (retErr , p .metadata .RemoveDevice (ctx , info .Name ))
131+ }()
132+
123133 // Create thin device
124- if err := p .transition (ctx , deviceName , Creating , Created , func () error {
134+ if err := p .createDevice (ctx , info ); err != nil {
135+ return err
136+ }
137+
138+ defer func () {
139+ if retErr == nil {
140+ return
141+ }
142+
143+ // Rollback creation
144+ retErr = multierror .Append (retErr , p .deleteDevice (ctx , info ))
145+ }()
146+
147+ return p .activateDevice (ctx , info )
148+ }
149+
150+ // createDevice creates thin device
151+ func (p * PoolDevice ) createDevice (ctx context.Context , info * DeviceInfo ) error {
152+ if err := p .transition (ctx , info .Name , Creating , Created , func () error {
125153 return dmsetup .CreateDevice (p .poolName , info .DeviceID )
126154 }); err != nil {
127155 return errors .Wrapf (err , "failed to create new thin device %q (dev: %d)" , info .Name , info .DeviceID )
128156 }
129157
130- // Activate thin device
131- if err := p .transition (ctx , deviceName , Activating , Activated , func () error {
158+ return nil
159+ }
160+
161+ // activateDevice activates thin device
162+ func (p * PoolDevice ) activateDevice (ctx context.Context , info * DeviceInfo ) error {
163+ if err := p .transition (ctx , info .Name , Activating , Activated , func () error {
132164 return dmsetup .ActivateDevice (p .poolName , info .Name , info .DeviceID , info .Size , "" )
133165 }); err != nil {
134166 return errors .Wrapf (err , "failed to activate new thin device %q (dev: %d)" , info .Name , info .DeviceID )
@@ -138,20 +170,23 @@ func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, vi
138170}
139171
140172// CreateSnapshotDevice creates and activates new thin-device from parent thin-device (makes snapshot)
141- func (p * PoolDevice ) CreateSnapshotDevice (ctx context.Context , deviceName string , snapshotName string , virtualSizeBytes uint64 ) error {
173+ func (p * PoolDevice ) CreateSnapshotDevice (ctx context.Context , deviceName string , snapshotName string , virtualSizeBytes uint64 ) ( retErr error ) {
142174 baseInfo , err := p .metadata .GetDevice (ctx , deviceName )
143175 if err != nil {
144176 return errors .Wrapf (err , "failed to query device metadata for %q" , deviceName )
145177 }
146178
147- // Suspend thin device if it was activated previously
179+ // Suspend thin device if it was activated previously to avoid corruptions
148180 isActivated := p .IsActivated (baseInfo .Name )
149181 if isActivated {
150- if err := p .transition (ctx , baseInfo .Name , Suspending , Suspended , func () error {
151- return dmsetup .SuspendDevice (baseInfo .Name )
152- }); err != nil {
153- return errors .Wrapf (err , "failed to suspend device %q" , baseInfo .Name )
182+ if err := p .suspendDevice (ctx , baseInfo ); err != nil {
183+ return err
154184 }
185+
186+ // Resume back base thin device on exit
187+ defer func () {
188+ retErr = multierror .Append (retErr , p .resumeDevice (ctx , baseInfo )).ErrorOrNil ()
189+ }()
155190 }
156191
157192 snapInfo := & DeviceInfo {
@@ -166,33 +201,63 @@ func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string
166201 return errors .Wrapf (err , "failed to save initial metadata for snapshot %q" , snapshotName )
167202 }
168203
204+ defer func () {
205+ if retErr == nil {
206+ return
207+ }
208+
209+ // Rollback metadata
210+ retErr = multierror .Append (retErr , p .metadata .RemoveDevice (ctx , snapInfo .Name ))
211+ }()
212+
169213 // Create thin device snapshot
214+ if err := p .createSnapshot (ctx , baseInfo , snapInfo ); err != nil {
215+ return err
216+ }
217+
218+ defer func () {
219+ if retErr == nil {
220+ return
221+ }
222+
223+ // Rollback snapshot creation
224+ retErr = multierror .Append (retErr , p .deleteDevice (ctx , snapInfo ))
225+ }()
226+
227+ // Activate snapshot device
228+ return p .activateDevice (ctx , snapInfo )
229+ }
230+
231+ func (p * PoolDevice ) suspendDevice (ctx context.Context , info * DeviceInfo ) error {
232+ if err := p .transition (ctx , info .Name , Suspending , Suspended , func () error {
233+ return dmsetup .SuspendDevice (info .Name )
234+ }); err != nil {
235+ return errors .Wrapf (err , "failed to suspend device %q" , info .Name )
236+ }
237+
238+ return nil
239+ }
240+
241+ func (p * PoolDevice ) resumeDevice (ctx context.Context , info * DeviceInfo ) error {
242+ if err := p .transition (ctx , info .Name , Resuming , Resumed , func () error {
243+ return dmsetup .ResumeDevice (info .Name )
244+ }); err != nil {
245+ return errors .Wrapf (err , "failed to resume device %q" , info .Name )
246+ }
247+
248+ return nil
249+ }
250+
251+ func (p * PoolDevice ) createSnapshot (ctx context.Context , baseInfo , snapInfo * DeviceInfo ) error {
170252 if err := p .transition (ctx , snapInfo .Name , Creating , Created , func () error {
171253 return dmsetup .CreateSnapshot (p .poolName , snapInfo .DeviceID , baseInfo .DeviceID )
172254 }); err != nil {
173255 return errors .Wrapf (err ,
174- "failed to create snapshot %q (dev: %d) from %q (dev: %d, activated: %t )" ,
256+ "failed to create snapshot %q (dev: %d) from %q (dev: %d)" ,
175257 snapInfo .Name ,
176258 snapInfo .DeviceID ,
177259 baseInfo .Name ,
178- baseInfo .DeviceID ,
179- isActivated )
180- }
181-
182- if isActivated {
183- // Resume base thin-device
184- if err := p .transition (ctx , baseInfo .Name , Resuming , Resumed , func () error {
185- return dmsetup .ResumeDevice (baseInfo .Name )
186- }); err != nil {
187- return errors .Wrapf (err , "failed to resume device %q" , deviceName )
188- }
189- }
190-
191- // Activate snapshot
192- if err := p .transition (ctx , snapInfo .Name , Activating , Activated , func () error {
193- return dmsetup .ActivateDevice (p .poolName , snapInfo .Name , snapInfo .DeviceID , snapInfo .Size , "" )
194- }); err != nil {
195- return errors .Wrapf (err , "failed to activate snapshot device %q (dev: %d)" , snapInfo .Name , snapInfo .DeviceID )
260+ baseInfo .DeviceID )
196261 }
197262
198263 return nil
@@ -221,7 +286,7 @@ func (p *PoolDevice) DeactivateDevice(ctx context.Context, deviceName string, de
221286// IsActivated returns true if thin-device is activated and not suspended
222287func (p * PoolDevice ) IsActivated (deviceName string ) bool {
223288 infos , err := dmsetup .Info (deviceName )
224- if err != nil || len (infos ) == 0 {
289+ if err != nil || len (infos ) != 1 {
225290 // Couldn't query device info, device not active
226291 return false
227292 }
@@ -244,11 +309,8 @@ func (p *PoolDevice) RemoveDevice(ctx context.Context, deviceName string) error
244309 return err
245310 }
246311
247- if err := p .transition (ctx , deviceName , Removing , Removed , func () error {
248- // Send 'delete' message to thin-pool
249- return dmsetup .DeleteDevice (p .poolName , info .DeviceID )
250- }); err != nil {
251- return errors .Wrapf (err , "failed to delete device %q (dev id: %d)" , info .Name , info .DeviceID )
312+ if err := p .deleteDevice (ctx , info ); err != nil {
313+ return err
252314 }
253315
254316 // Remove record from meta store and free device ID
@@ -259,6 +321,17 @@ func (p *PoolDevice) RemoveDevice(ctx context.Context, deviceName string) error
259321 return nil
260322}
261323
324+ func (p * PoolDevice ) deleteDevice (ctx context.Context , info * DeviceInfo ) error {
325+ if err := p .transition (ctx , info .Name , Removing , Removed , func () error {
326+ // Send 'delete' message to thin-pool
327+ return dmsetup .DeleteDevice (p .poolName , info .DeviceID )
328+ }); err != nil {
329+ return errors .Wrapf (err , "failed to delete device %q (dev id: %d)" , info .Name , info .DeviceID )
330+ }
331+
332+ return nil
333+ }
334+
262335// RemovePool deactivates all child thin-devices and removes thin-pool device
263336func (p * PoolDevice ) RemovePool (ctx context.Context ) error {
264337 deviceNames , err := p .metadata .GetDeviceNames (ctx )
0 commit comments