github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/storageprovisioner/volume_events.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/params" 11 "github.com/juju/juju/instance" 12 "github.com/juju/juju/storage" 13 "github.com/juju/juju/watcher" 14 ) 15 16 // volumesChanged is called when the lifecycle states of the volumes 17 // with the provided IDs have been seen to have changed. 18 func volumesChanged(ctx *context, changes []string) error { 19 tags := make([]names.Tag, len(changes)) 20 for i, change := range changes { 21 tags[i] = names.NewVolumeTag(change) 22 } 23 alive, dying, dead, err := storageEntityLife(ctx, tags) 24 if err != nil { 25 return errors.Trace(err) 26 } 27 logger.Debugf("volumes alive: %v, dying: %v, dead: %v", alive, dying, dead) 28 if err := processDyingVolumes(ctx, dying); err != nil { 29 return errors.Annotate(err, "processing dying volumes") 30 } 31 if len(alive)+len(dead) == 0 { 32 return nil 33 } 34 35 // Get volume information for alive and dead volumes, so 36 // we can provision/deprovision. 37 volumeTags := make([]names.VolumeTag, 0, len(alive)+len(dead)) 38 for _, tag := range alive { 39 volumeTags = append(volumeTags, tag.(names.VolumeTag)) 40 } 41 for _, tag := range dead { 42 volumeTags = append(volumeTags, tag.(names.VolumeTag)) 43 } 44 volumeResults, err := ctx.config.Volumes.Volumes(volumeTags) 45 if err != nil { 46 return errors.Annotatef(err, "getting volume information") 47 } 48 if err := processDeadVolumes(ctx, volumeTags[len(alive):], volumeResults[len(alive):]); err != nil { 49 return errors.Annotate(err, "deprovisioning volumes") 50 } 51 if err := processAliveVolumes(ctx, alive, volumeResults[:len(alive)]); err != nil { 52 return errors.Annotate(err, "provisioning volumes") 53 } 54 return nil 55 } 56 57 // volumeAttachmentsChanged is called when the lifecycle states of the volume 58 // attachments with the provided IDs have been seen to have changed. 59 func volumeAttachmentsChanged(ctx *context, watcherIds []watcher.MachineStorageId) error { 60 ids := copyMachineStorageIds(watcherIds) 61 alive, dying, dead, err := attachmentLife(ctx, ids) 62 if err != nil { 63 return errors.Trace(err) 64 } 65 logger.Debugf("volume attachments alive: %v, dying: %v, dead: %v", alive, dying, dead) 66 if len(dead) != 0 { 67 // We should not see dead volume attachments; 68 // attachments go directly from Dying to removed. 69 logger.Warningf("unexpected dead volume attachments: %v", dead) 70 } 71 if len(alive)+len(dying) == 0 { 72 return nil 73 } 74 75 // Get volume information for alive and dying volume attachments, so 76 // we can attach/detach. 77 ids = append(alive, dying...) 78 volumeAttachmentResults, err := ctx.config.Volumes.VolumeAttachments(ids) 79 if err != nil { 80 return errors.Annotatef(err, "getting volume attachment information") 81 } 82 83 // Deprovision Dying volume attachments. 84 dyingVolumeAttachmentResults := volumeAttachmentResults[len(alive):] 85 if err := processDyingVolumeAttachments(ctx, dying, dyingVolumeAttachmentResults); err != nil { 86 return errors.Annotate(err, "deprovisioning volume attachments") 87 } 88 89 // Provision Alive volume attachments. 90 aliveVolumeAttachmentResults := volumeAttachmentResults[:len(alive)] 91 if err := processAliveVolumeAttachments(ctx, alive, aliveVolumeAttachmentResults); err != nil { 92 return errors.Annotate(err, "provisioning volumes") 93 } 94 95 return nil 96 } 97 98 // processDyingVolumes processes the VolumeResults for Dying volumes, 99 // removing them from provisioning-pending as necessary. 100 func processDyingVolumes(ctx *context, tags []names.Tag) error { 101 for _, tag := range tags { 102 removePendingVolume(ctx, tag.(names.VolumeTag)) 103 } 104 return nil 105 } 106 107 // updateVolume updates the context with the given volume info. 108 func updateVolume(ctx *context, info storage.Volume) { 109 ctx.volumes[info.Tag] = info 110 for id, params := range ctx.incompleteVolumeAttachmentParams { 111 if params.VolumeId == "" && id.AttachmentTag == info.Tag.String() { 112 params.VolumeId = info.VolumeId 113 updatePendingVolumeAttachment(ctx, id, params) 114 } 115 } 116 } 117 118 // updatePendingVolume adds the given volume params to either the incomplete 119 // set or the schedule. If the params are incomplete due to a missing instance 120 // ID, updatePendingVolume will request that the machine be watched so its 121 // instance ID can be learned. 122 func updatePendingVolume(ctx *context, params storage.VolumeParams) { 123 if params.Attachment.InstanceId == "" { 124 watchMachine(ctx, params.Attachment.Machine) 125 ctx.incompleteVolumeParams[params.Tag] = params 126 } else { 127 delete(ctx.incompleteVolumeParams, params.Tag) 128 scheduleOperations(ctx, &createVolumeOp{args: params}) 129 } 130 } 131 132 // removePendingVolume removes the specified pending volume from the 133 // incomplete set and/or the schedule if it exists there. 134 func removePendingVolume(ctx *context, tag names.VolumeTag) { 135 delete(ctx.incompleteVolumeParams, tag) 136 ctx.schedule.Remove(tag) 137 } 138 139 // updatePendingVolumeAttachment adds the given volume attachment params to 140 // either the incomplete set or the schedule. If the params are incomplete 141 // due to a missing instance ID, updatePendingVolumeAttachment will request 142 // that the machine be watched so its instance ID can be learned. 143 func updatePendingVolumeAttachment( 144 ctx *context, 145 id params.MachineStorageId, 146 params storage.VolumeAttachmentParams, 147 ) { 148 if params.InstanceId == "" { 149 watchMachine(ctx, params.Machine) 150 } else if params.VolumeId != "" { 151 delete(ctx.incompleteVolumeAttachmentParams, id) 152 scheduleOperations(ctx, &attachVolumeOp{args: params}) 153 return 154 } 155 ctx.incompleteVolumeAttachmentParams[id] = params 156 } 157 158 // removePendingVolumeAttachment removes the specified pending volume 159 // attachment from the incomplete set and/or the schedule if it exists 160 // there. 161 func removePendingVolumeAttachment(ctx *context, id params.MachineStorageId) { 162 delete(ctx.incompleteVolumeAttachmentParams, id) 163 ctx.schedule.Remove(id) 164 } 165 166 // processDeadVolumes processes the VolumeResults for Dead volumes, 167 // deprovisioning volumes and removing from state as necessary. 168 func processDeadVolumes(ctx *context, tags []names.VolumeTag, volumeResults []params.VolumeResult) error { 169 for _, tag := range tags { 170 removePendingVolume(ctx, tag) 171 } 172 var destroy []names.VolumeTag 173 var remove []names.Tag 174 for i, result := range volumeResults { 175 tag := tags[i] 176 if result.Error == nil { 177 logger.Debugf("volume %s is provisioned, queuing for deprovisioning", tag.Id()) 178 volume, err := volumeFromParams(result.Result) 179 if err != nil { 180 return errors.Annotate(err, "getting volume info") 181 } 182 updateVolume(ctx, volume) 183 destroy = append(destroy, tag) 184 continue 185 } 186 if params.IsCodeNotProvisioned(result.Error) { 187 logger.Debugf("volume %s is not provisioned, queuing for removal", tag.Id()) 188 remove = append(remove, tag) 189 continue 190 } 191 return errors.Annotatef(result.Error, "getting volume information for volume %s", tag.Id()) 192 } 193 if len(destroy) > 0 { 194 ops := make([]scheduleOp, len(destroy)) 195 for i, tag := range destroy { 196 ops[i] = &destroyVolumeOp{tag: tag} 197 } 198 scheduleOperations(ctx, ops...) 199 } 200 if err := removeEntities(ctx, remove); err != nil { 201 return errors.Annotate(err, "removing volumes from state") 202 } 203 return nil 204 } 205 206 // processDyingVolumeAttachments processes the VolumeAttachmentResults for 207 // Dying volume attachments, detaching volumes and updating state as necessary. 208 func processDyingVolumeAttachments( 209 ctx *context, 210 ids []params.MachineStorageId, 211 volumeAttachmentResults []params.VolumeAttachmentResult, 212 ) error { 213 for _, id := range ids { 214 removePendingVolumeAttachment(ctx, id) 215 } 216 detach := make([]params.MachineStorageId, 0, len(ids)) 217 remove := make([]params.MachineStorageId, 0, len(ids)) 218 for i, result := range volumeAttachmentResults { 219 id := ids[i] 220 if result.Error == nil { 221 detach = append(detach, id) 222 continue 223 } 224 if params.IsCodeNotProvisioned(result.Error) { 225 remove = append(remove, id) 226 continue 227 } 228 return errors.Annotatef(result.Error, "getting information for volume attachment %v", id) 229 } 230 if len(detach) > 0 { 231 attachmentParams, err := volumeAttachmentParams(ctx, detach) 232 if err != nil { 233 return errors.Trace(err) 234 } 235 ops := make([]scheduleOp, len(attachmentParams)) 236 for i, p := range attachmentParams { 237 ops[i] = &detachVolumeOp{args: p} 238 } 239 scheduleOperations(ctx, ops...) 240 } 241 if err := removeAttachments(ctx, remove); err != nil { 242 return errors.Annotate(err, "removing attachments from state") 243 } 244 for _, id := range remove { 245 delete(ctx.volumeAttachments, id) 246 } 247 return nil 248 } 249 250 // processAliveVolumes processes the VolumeResults for Alive volumes, 251 // provisioning volumes and setting the info in state as necessary. 252 func processAliveVolumes(ctx *context, tags []names.Tag, volumeResults []params.VolumeResult) error { 253 // Filter out the already-provisioned volumes. 254 pending := make([]names.VolumeTag, 0, len(tags)) 255 for i, result := range volumeResults { 256 volumeTag := tags[i].(names.VolumeTag) 257 if result.Error == nil { 258 // Volume is already provisioned: skip. 259 logger.Debugf("volume %q is already provisioned, nothing to do", tags[i].Id()) 260 volume, err := volumeFromParams(result.Result) 261 if err != nil { 262 return errors.Annotate(err, "getting volume info") 263 } 264 updateVolume(ctx, volume) 265 removePendingVolume(ctx, volumeTag) 266 continue 267 } 268 if !params.IsCodeNotProvisioned(result.Error) { 269 return errors.Annotatef( 270 result.Error, "getting volume information for volume %q", tags[i].Id(), 271 ) 272 } 273 // The volume has not yet been provisioned, so record its tag 274 // to enquire about parameters below. 275 pending = append(pending, volumeTag) 276 } 277 if len(pending) == 0 { 278 return nil 279 } 280 volumeParams, err := volumeParams(ctx, pending) 281 if err != nil { 282 return errors.Annotate(err, "getting volume params") 283 } 284 for _, params := range volumeParams { 285 updatePendingVolume(ctx, params) 286 } 287 return nil 288 } 289 290 // processAliveVolumeAttachments processes the VolumeAttachmentResults 291 // for Alive volume attachments, attaching volumes and setting the info 292 // in state as necessary. 293 func processAliveVolumeAttachments( 294 ctx *context, 295 ids []params.MachineStorageId, 296 volumeAttachmentResults []params.VolumeAttachmentResult, 297 ) error { 298 // Filter out the already-attached. 299 pending := make([]params.MachineStorageId, 0, len(ids)) 300 for i, result := range volumeAttachmentResults { 301 if result.Error == nil { 302 // Volume attachment is already provisioned: if we 303 // didn't (re)attach in this session, then we must 304 // do so now. 305 action := "nothing to do" 306 if _, ok := ctx.volumeAttachments[ids[i]]; !ok { 307 // Not yet (re)attached in this session. 308 pending = append(pending, ids[i]) 309 action = "will reattach" 310 } 311 logger.Debugf( 312 "%s is already attached to %s, %s", 313 ids[i].AttachmentTag, ids[i].MachineTag, action, 314 ) 315 removePendingVolumeAttachment(ctx, ids[i]) 316 continue 317 } 318 if !params.IsCodeNotProvisioned(result.Error) { 319 return errors.Annotatef( 320 result.Error, "getting information for attachment %v", ids[i], 321 ) 322 } 323 // The volume has not yet been provisioned, so record its tag 324 // to enquire about parameters below. 325 pending = append(pending, ids[i]) 326 } 327 if len(pending) == 0 { 328 return nil 329 } 330 params, err := volumeAttachmentParams(ctx, pending) 331 if err != nil { 332 return errors.Trace(err) 333 } 334 for i, params := range params { 335 if volume, ok := ctx.volumes[params.Volume]; ok { 336 params.VolumeId = volume.VolumeId 337 } 338 updatePendingVolumeAttachment(ctx, pending[i], params) 339 } 340 return nil 341 } 342 343 // volumeAttachmentParams obtains the specified attachments' parameters. 344 func volumeAttachmentParams( 345 ctx *context, ids []params.MachineStorageId, 346 ) ([]storage.VolumeAttachmentParams, error) { 347 paramsResults, err := ctx.config.Volumes.VolumeAttachmentParams(ids) 348 if err != nil { 349 return nil, errors.Annotate(err, "getting volume attachment params") 350 } 351 attachmentParams := make([]storage.VolumeAttachmentParams, len(ids)) 352 for i, result := range paramsResults { 353 if result.Error != nil { 354 return nil, errors.Annotate(result.Error, "getting volume attachment parameters") 355 } 356 params, err := volumeAttachmentParamsFromParams(result.Result) 357 if err != nil { 358 return nil, errors.Annotate(err, "getting volume attachment parameters") 359 } 360 attachmentParams[i] = params 361 } 362 return attachmentParams, nil 363 } 364 365 // volumeParams obtains the specified volumes' parameters. 366 func volumeParams(ctx *context, tags []names.VolumeTag) ([]storage.VolumeParams, error) { 367 paramsResults, err := ctx.config.Volumes.VolumeParams(tags) 368 if err != nil { 369 return nil, errors.Annotate(err, "getting volume params") 370 } 371 allParams := make([]storage.VolumeParams, len(tags)) 372 for i, result := range paramsResults { 373 if result.Error != nil { 374 return nil, errors.Annotate(result.Error, "getting volume parameters") 375 } 376 params, err := volumeParamsFromParams(result.Result) 377 if err != nil { 378 return nil, errors.Annotate(err, "getting volume parameters") 379 } 380 allParams[i] = params 381 } 382 return allParams, nil 383 } 384 385 func volumesFromStorage(in []storage.Volume) []params.Volume { 386 out := make([]params.Volume, len(in)) 387 for i, v := range in { 388 out[i] = params.Volume{ 389 v.Tag.String(), 390 params.VolumeInfo{ 391 v.VolumeId, 392 v.HardwareId, 393 v.Size, 394 v.Persistent, 395 }, 396 } 397 } 398 return out 399 } 400 401 func volumeAttachmentsFromStorage(in []storage.VolumeAttachment) []params.VolumeAttachment { 402 out := make([]params.VolumeAttachment, len(in)) 403 for i, v := range in { 404 out[i] = params.VolumeAttachment{ 405 v.Volume.String(), 406 v.Machine.String(), 407 params.VolumeAttachmentInfo{ 408 v.DeviceName, 409 v.DeviceLink, 410 v.BusAddress, 411 v.ReadOnly, 412 }, 413 } 414 } 415 return out 416 } 417 418 func volumeFromParams(in params.Volume) (storage.Volume, error) { 419 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 420 if err != nil { 421 return storage.Volume{}, errors.Trace(err) 422 } 423 return storage.Volume{ 424 volumeTag, 425 storage.VolumeInfo{ 426 in.Info.VolumeId, 427 in.Info.HardwareId, 428 in.Info.Size, 429 in.Info.Persistent, 430 }, 431 }, nil 432 } 433 434 func volumeParamsFromParams(in params.VolumeParams) (storage.VolumeParams, error) { 435 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 436 if err != nil { 437 return storage.VolumeParams{}, errors.Trace(err) 438 } 439 providerType := storage.ProviderType(in.Provider) 440 441 var attachment *storage.VolumeAttachmentParams 442 if in.Attachment != nil { 443 if in.Attachment.Provider != in.Provider { 444 return storage.VolumeParams{}, errors.Errorf( 445 "storage provider mismatch: volume (%q), attachment (%q)", 446 in.Provider, in.Attachment.Provider, 447 ) 448 } 449 if in.Attachment.VolumeTag != in.VolumeTag { 450 return storage.VolumeParams{}, errors.Errorf( 451 "volume tag mismatch: volume (%q), attachment (%q)", 452 in.VolumeTag, in.Attachment.VolumeTag, 453 ) 454 } 455 machineTag, err := names.ParseMachineTag(in.Attachment.MachineTag) 456 if err != nil { 457 return storage.VolumeParams{}, errors.Annotate( 458 err, "parsing attachment machine tag", 459 ) 460 } 461 attachment = &storage.VolumeAttachmentParams{ 462 AttachmentParams: storage.AttachmentParams{ 463 Provider: providerType, 464 Machine: machineTag, 465 InstanceId: instance.Id(in.Attachment.InstanceId), 466 ReadOnly: in.Attachment.ReadOnly, 467 }, 468 Volume: volumeTag, 469 } 470 } 471 return storage.VolumeParams{ 472 volumeTag, 473 in.Size, 474 providerType, 475 in.Attributes, 476 in.Tags, 477 attachment, 478 }, nil 479 } 480 481 func volumeAttachmentParamsFromParams(in params.VolumeAttachmentParams) (storage.VolumeAttachmentParams, error) { 482 machineTag, err := names.ParseMachineTag(in.MachineTag) 483 if err != nil { 484 return storage.VolumeAttachmentParams{}, errors.Trace(err) 485 } 486 volumeTag, err := names.ParseVolumeTag(in.VolumeTag) 487 if err != nil { 488 return storage.VolumeAttachmentParams{}, errors.Trace(err) 489 } 490 return storage.VolumeAttachmentParams{ 491 AttachmentParams: storage.AttachmentParams{ 492 Provider: storage.ProviderType(in.Provider), 493 Machine: machineTag, 494 InstanceId: instance.Id(in.InstanceId), 495 ReadOnly: in.ReadOnly, 496 }, 497 Volume: volumeTag, 498 VolumeId: in.VolumeId, 499 }, nil 500 }