github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/storageprovisioner/filesystem_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 // filesystemsChanged is called when the lifecycle states of the filesystems 17 // with the provided IDs have been seen to have changed. 18 func filesystemsChanged(ctx *context, changes []string) error { 19 tags := make([]names.Tag, len(changes)) 20 for i, change := range changes { 21 tags[i] = names.NewFilesystemTag(change) 22 } 23 alive, dying, dead, err := storageEntityLife(ctx, tags) 24 if err != nil { 25 return errors.Trace(err) 26 } 27 logger.Debugf("filesystems alive: %v, dying: %v, dead: %v", alive, dying, dead) 28 if len(alive)+len(dying)+len(dead) == 0 { 29 return nil 30 } 31 32 // Get filesystem information for filesystems, so we can provision, 33 // deprovision, attach and detach. 34 filesystemTags := make([]names.FilesystemTag, 0, len(alive)+len(dying)+len(dead)) 35 for _, tag := range alive { 36 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 37 } 38 for _, tag := range dying { 39 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 40 } 41 for _, tag := range dead { 42 filesystemTags = append(filesystemTags, tag.(names.FilesystemTag)) 43 } 44 filesystemResults, err := ctx.config.Filesystems.Filesystems(filesystemTags) 45 if err != nil { 46 return errors.Annotatef(err, "getting filesystem information") 47 } 48 49 aliveFilesystemTags := filesystemTags[:len(alive)] 50 dyingFilesystemTags := filesystemTags[len(alive) : len(alive)+len(dying)] 51 deadFilesystemTags := filesystemTags[len(alive)+len(dying):] 52 aliveFilesystemResults := filesystemResults[:len(alive)] 53 dyingFilesystemResults := filesystemResults[len(alive) : len(alive)+len(dying)] 54 deadFilesystemResults := filesystemResults[len(alive)+len(dying):] 55 56 if err := processDeadFilesystems(ctx, deadFilesystemTags, deadFilesystemResults); err != nil { 57 return errors.Annotate(err, "deprovisioning filesystems") 58 } 59 if err := processDyingFilesystems(ctx, dyingFilesystemTags, dyingFilesystemResults); err != nil { 60 return errors.Annotate(err, "processing dying filesystems") 61 } 62 if err := processAliveFilesystems(ctx, aliveFilesystemTags, aliveFilesystemResults); err != nil { 63 return errors.Annotate(err, "provisioning filesystems") 64 } 65 return nil 66 } 67 68 // filesystemAttachmentsChanged is called when the lifecycle states of the filesystem 69 // attachments with the provided IDs have been seen to have changed. 70 func filesystemAttachmentsChanged(ctx *context, watcherIds []watcher.MachineStorageId) error { 71 ids := copyMachineStorageIds(watcherIds) 72 alive, dying, dead, err := attachmentLife(ctx, ids) 73 if err != nil { 74 return errors.Trace(err) 75 } 76 logger.Debugf("filesystem attachment alive: %v, dying: %v, dead: %v", alive, dying, dead) 77 if len(dead) != 0 { 78 // We should not see dead filesystem attachments; 79 // attachments go directly from Dying to removed. 80 logger.Warningf("unexpected dead filesystem attachments: %v", dead) 81 } 82 if len(alive)+len(dying) == 0 { 83 return nil 84 } 85 86 // Get filesystem information for alive and dying filesystem attachments, so 87 // we can attach/detach. 88 ids = append(alive, dying...) 89 filesystemAttachmentResults, err := ctx.config.Filesystems.FilesystemAttachments(ids) 90 if err != nil { 91 return errors.Annotatef(err, "getting filesystem attachment information") 92 } 93 94 // Deprovision Dying filesystem attachments. 95 dyingFilesystemAttachmentResults := filesystemAttachmentResults[len(alive):] 96 if err := processDyingFilesystemAttachments(ctx, dying, dyingFilesystemAttachmentResults); err != nil { 97 return errors.Annotate(err, "destroying filesystem attachments") 98 } 99 100 // Provision Alive filesystem attachments. 101 aliveFilesystemAttachmentResults := filesystemAttachmentResults[:len(alive)] 102 if err := processAliveFilesystemAttachments(ctx, alive, aliveFilesystemAttachmentResults); err != nil { 103 return errors.Annotate(err, "creating filesystem attachments") 104 } 105 106 return nil 107 } 108 109 // processDyingFilesystems processes the FilesystemResults for Dying filesystems, 110 // removing them from provisioning-pending as necessary. 111 func processDyingFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 112 for _, tag := range tags { 113 removePendingFilesystem(ctx, tag) 114 } 115 return nil 116 } 117 118 func updateFilesystem(ctx *context, info storage.Filesystem) { 119 ctx.filesystems[info.Tag] = info 120 for id, params := range ctx.incompleteFilesystemAttachmentParams { 121 if params.FilesystemId == "" && id.AttachmentTag == info.Tag.String() { 122 updatePendingFilesystemAttachment(ctx, id, params) 123 } 124 } 125 } 126 127 func updatePendingFilesystem(ctx *context, params storage.FilesystemParams) { 128 if params.Volume != (names.VolumeTag{}) { 129 // The filesystem is volume-backed: we must watch for 130 // the corresponding block device. This will trigger a 131 // one-time (for the volume) forced update of block 132 // devices. If the block device is not immediately 133 // available, then we rely on the watcher. The forced 134 // update is necessary in case the block device was 135 // added to state already, and we didn't observe it. 136 if _, ok := ctx.volumeBlockDevices[params.Volume]; !ok { 137 ctx.pendingVolumeBlockDevices.Add(params.Volume) 138 ctx.incompleteFilesystemParams[params.Tag] = params 139 return 140 } 141 } 142 delete(ctx.incompleteFilesystemParams, params.Tag) 143 scheduleOperations(ctx, &createFilesystemOp{args: params}) 144 } 145 146 func removePendingFilesystem(ctx *context, tag names.FilesystemTag) { 147 delete(ctx.incompleteFilesystemParams, tag) 148 ctx.schedule.Remove(tag) 149 } 150 151 // updatePendingFilesystemAttachment adds the given filesystem attachment params to 152 // either the incomplete set or the schedule. If the params are incomplete 153 // due to a missing instance ID, updatePendingFilesystemAttachment will request 154 // that the machine be watched so its instance ID can be learned. 155 func updatePendingFilesystemAttachment( 156 ctx *context, 157 id params.MachineStorageId, 158 params storage.FilesystemAttachmentParams, 159 ) { 160 var incomplete bool 161 filesystem, ok := ctx.filesystems[params.Filesystem] 162 if !ok { 163 incomplete = true 164 } else { 165 params.FilesystemId = filesystem.FilesystemId 166 if filesystem.Volume != (names.VolumeTag{}) { 167 // The filesystem is volume-backed: if the filesystem 168 // was created in another session, then the block device 169 // may not have been seen yet. We must wait for the block 170 // device watcher to trigger. 171 if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; !ok { 172 incomplete = true 173 } 174 } 175 } 176 if params.InstanceId == "" { 177 watchMachine(ctx, params.Machine) 178 incomplete = true 179 } 180 if params.FilesystemId == "" { 181 incomplete = true 182 } 183 if incomplete { 184 ctx.incompleteFilesystemAttachmentParams[id] = params 185 return 186 } 187 delete(ctx.incompleteFilesystemAttachmentParams, id) 188 scheduleOperations(ctx, &attachFilesystemOp{args: params}) 189 } 190 191 // removePendingFilesystemAttachment removes the specified pending filesystem 192 // attachment from the incomplete set and/or the schedule if it exists 193 // there. 194 func removePendingFilesystemAttachment(ctx *context, id params.MachineStorageId) { 195 delete(ctx.incompleteFilesystemAttachmentParams, id) 196 ctx.schedule.Remove(id) 197 } 198 199 // processDeadFilesystems processes the FilesystemResults for Dead filesystems, 200 // deprovisioning filesystems and removing from state as necessary. 201 func processDeadFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 202 for _, tag := range tags { 203 removePendingFilesystem(ctx, tag) 204 } 205 var destroy []names.FilesystemTag 206 var remove []names.Tag 207 for i, result := range filesystemResults { 208 tag := tags[i] 209 if result.Error == nil { 210 logger.Debugf("filesystem %s is provisioned, queuing for deprovisioning", tag.Id()) 211 filesystem, err := filesystemFromParams(result.Result) 212 if err != nil { 213 return errors.Annotate(err, "getting filesystem info") 214 } 215 updateFilesystem(ctx, filesystem) 216 destroy = append(destroy, tag) 217 continue 218 } 219 if params.IsCodeNotProvisioned(result.Error) { 220 logger.Debugf("filesystem %s is not provisioned, queuing for removal", tag.Id()) 221 remove = append(remove, tag) 222 continue 223 } 224 return errors.Annotatef(result.Error, "getting filesystem information for filesystem %s", tag.Id()) 225 } 226 if len(destroy) > 0 { 227 ops := make([]scheduleOp, len(destroy)) 228 for i, tag := range destroy { 229 ops[i] = &destroyFilesystemOp{tag: tag} 230 } 231 scheduleOperations(ctx, ops...) 232 } 233 if err := removeEntities(ctx, remove); err != nil { 234 return errors.Annotate(err, "removing filesystems from state") 235 } 236 return nil 237 } 238 239 // processDyingFilesystemAttachments processes the FilesystemAttachmentResults for 240 // Dying filesystem attachments, detaching filesystems and updating state as necessary. 241 func processDyingFilesystemAttachments( 242 ctx *context, 243 ids []params.MachineStorageId, 244 filesystemAttachmentResults []params.FilesystemAttachmentResult, 245 ) error { 246 for _, id := range ids { 247 removePendingFilesystemAttachment(ctx, id) 248 } 249 detach := make([]params.MachineStorageId, 0, len(ids)) 250 remove := make([]params.MachineStorageId, 0, len(ids)) 251 for i, result := range filesystemAttachmentResults { 252 id := ids[i] 253 if result.Error == nil { 254 detach = append(detach, id) 255 continue 256 } 257 if params.IsCodeNotProvisioned(result.Error) { 258 remove = append(remove, id) 259 continue 260 } 261 return errors.Annotatef(result.Error, "getting information for filesystem attachment %v", id) 262 } 263 if len(detach) > 0 { 264 attachmentParams, err := filesystemAttachmentParams(ctx, detach) 265 if err != nil { 266 return errors.Trace(err) 267 } 268 ops := make([]scheduleOp, len(attachmentParams)) 269 for i, p := range attachmentParams { 270 ops[i] = &detachFilesystemOp{args: p} 271 } 272 scheduleOperations(ctx, ops...) 273 } 274 if err := removeAttachments(ctx, remove); err != nil { 275 return errors.Annotate(err, "removing attachments from state") 276 } 277 return nil 278 } 279 280 // processAliveFilesystems processes the FilesystemResults for Alive filesystems, 281 // provisioning filesystems and setting the info in state as necessary. 282 func processAliveFilesystems(ctx *context, tags []names.FilesystemTag, filesystemResults []params.FilesystemResult) error { 283 // Filter out the already-provisioned filesystems. 284 pending := make([]names.FilesystemTag, 0, len(tags)) 285 for i, result := range filesystemResults { 286 tag := tags[i] 287 if result.Error == nil { 288 // Filesystem is already provisioned: skip. 289 logger.Debugf("filesystem %q is already provisioned, nothing to do", tag.Id()) 290 filesystem, err := filesystemFromParams(result.Result) 291 if err != nil { 292 return errors.Annotate(err, "getting filesystem info") 293 } 294 updateFilesystem(ctx, filesystem) 295 if filesystem.Volume != (names.VolumeTag{}) { 296 // Ensure that volume-backed filesystems' block 297 // devices are present even after creating the 298 // filesystem, so that attachments can be made. 299 maybeAddPendingVolumeBlockDevice(ctx, filesystem.Volume) 300 } 301 continue 302 } 303 if !params.IsCodeNotProvisioned(result.Error) { 304 return errors.Annotatef( 305 result.Error, "getting filesystem information for filesystem %q", tag.Id(), 306 ) 307 } 308 // The filesystem has not yet been provisioned, so record its tag 309 // to enquire about parameters below. 310 pending = append(pending, tag) 311 } 312 if len(pending) == 0 { 313 return nil 314 } 315 params, err := filesystemParams(ctx, pending) 316 if err != nil { 317 return errors.Annotate(err, "getting filesystem params") 318 } 319 for _, params := range params { 320 updatePendingFilesystem(ctx, params) 321 } 322 return nil 323 } 324 325 func maybeAddPendingVolumeBlockDevice(ctx *context, v names.VolumeTag) { 326 if _, ok := ctx.volumeBlockDevices[v]; !ok { 327 ctx.pendingVolumeBlockDevices.Add(v) 328 } 329 } 330 331 // processAliveFilesystemAttachments processes the FilesystemAttachmentResults 332 // for Alive filesystem attachments, attaching filesystems and setting the info 333 // in state as necessary. 334 func processAliveFilesystemAttachments( 335 ctx *context, 336 ids []params.MachineStorageId, 337 filesystemAttachmentResults []params.FilesystemAttachmentResult, 338 ) error { 339 // Filter out the already-attached. 340 pending := make([]params.MachineStorageId, 0, len(ids)) 341 for i, result := range filesystemAttachmentResults { 342 if result.Error == nil { 343 // Filesystem attachment is already provisioned: if we 344 // didn't (re)attach in this session, then we must do 345 // so now. 346 action := "nothing to do" 347 if _, ok := ctx.filesystemAttachments[ids[i]]; !ok { 348 // Not yet (re)attached in this session. 349 pending = append(pending, ids[i]) 350 action = "will reattach" 351 } 352 logger.Debugf( 353 "%s is already attached to %s, %s", 354 ids[i].AttachmentTag, ids[i].MachineTag, action, 355 ) 356 removePendingFilesystemAttachment(ctx, ids[i]) 357 continue 358 } 359 if !params.IsCodeNotProvisioned(result.Error) { 360 return errors.Annotatef( 361 result.Error, "getting information for attachment %v", ids[i], 362 ) 363 } 364 // The filesystem has not yet been attached, so 365 // record its tag to enquire about parameters below. 366 pending = append(pending, ids[i]) 367 } 368 if len(pending) == 0 { 369 return nil 370 } 371 params, err := filesystemAttachmentParams(ctx, pending) 372 if err != nil { 373 return errors.Trace(err) 374 } 375 for i, params := range params { 376 updatePendingFilesystemAttachment(ctx, pending[i], params) 377 } 378 return nil 379 } 380 381 // filesystemAttachmentParams obtains the specified attachments' parameters. 382 func filesystemAttachmentParams( 383 ctx *context, ids []params.MachineStorageId, 384 ) ([]storage.FilesystemAttachmentParams, error) { 385 paramsResults, err := ctx.config.Filesystems.FilesystemAttachmentParams(ids) 386 if err != nil { 387 return nil, errors.Annotate(err, "getting filesystem attachment params") 388 } 389 attachmentParams := make([]storage.FilesystemAttachmentParams, len(ids)) 390 for i, result := range paramsResults { 391 if result.Error != nil { 392 return nil, errors.Annotate(result.Error, "getting filesystem attachment parameters") 393 } 394 params, err := filesystemAttachmentParamsFromParams(result.Result) 395 if err != nil { 396 return nil, errors.Annotate(err, "getting filesystem attachment parameters") 397 } 398 attachmentParams[i] = params 399 } 400 return attachmentParams, nil 401 } 402 403 // filesystemParams obtains the specified filesystems' parameters. 404 func filesystemParams(ctx *context, tags []names.FilesystemTag) ([]storage.FilesystemParams, error) { 405 paramsResults, err := ctx.config.Filesystems.FilesystemParams(tags) 406 if err != nil { 407 return nil, errors.Annotate(err, "getting filesystem params") 408 } 409 allParams := make([]storage.FilesystemParams, len(tags)) 410 for i, result := range paramsResults { 411 if result.Error != nil { 412 return nil, errors.Annotate(result.Error, "getting filesystem parameters") 413 } 414 params, err := filesystemParamsFromParams(result.Result) 415 if err != nil { 416 return nil, errors.Annotate(err, "getting filesystem parameters") 417 } 418 allParams[i] = params 419 } 420 return allParams, nil 421 } 422 423 func filesystemFromParams(in params.Filesystem) (storage.Filesystem, error) { 424 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 425 if err != nil { 426 return storage.Filesystem{}, errors.Trace(err) 427 } 428 var volumeTag names.VolumeTag 429 if in.VolumeTag != "" { 430 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 431 if err != nil { 432 return storage.Filesystem{}, errors.Trace(err) 433 } 434 } 435 return storage.Filesystem{ 436 filesystemTag, 437 volumeTag, 438 storage.FilesystemInfo{ 439 in.Info.FilesystemId, 440 in.Info.Size, 441 }, 442 }, nil 443 } 444 445 func filesystemParamsFromParams(in params.FilesystemParams) (storage.FilesystemParams, error) { 446 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 447 if err != nil { 448 return storage.FilesystemParams{}, errors.Trace(err) 449 } 450 var volumeTag names.VolumeTag 451 if in.VolumeTag != "" { 452 volumeTag, err = names.ParseVolumeTag(in.VolumeTag) 453 if err != nil { 454 return storage.FilesystemParams{}, errors.Trace(err) 455 } 456 } 457 providerType := storage.ProviderType(in.Provider) 458 return storage.FilesystemParams{ 459 filesystemTag, 460 volumeTag, 461 in.Size, 462 providerType, 463 in.Attributes, 464 in.Tags, 465 }, nil 466 } 467 468 func filesystemAttachmentParamsFromParams(in params.FilesystemAttachmentParams) (storage.FilesystemAttachmentParams, error) { 469 machineTag, err := names.ParseMachineTag(in.MachineTag) 470 if err != nil { 471 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 472 } 473 filesystemTag, err := names.ParseFilesystemTag(in.FilesystemTag) 474 if err != nil { 475 return storage.FilesystemAttachmentParams{}, errors.Trace(err) 476 } 477 return storage.FilesystemAttachmentParams{ 478 AttachmentParams: storage.AttachmentParams{ 479 Provider: storage.ProviderType(in.Provider), 480 Machine: machineTag, 481 InstanceId: instance.Id(in.InstanceId), 482 ReadOnly: in.ReadOnly, 483 }, 484 Filesystem: filesystemTag, 485 FilesystemId: in.FilesystemId, 486 Path: in.MountPoint, 487 }, nil 488 }