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