github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/storageprovisioner/storageprovisioner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package storageprovisioner provides a worker that manages the provisioning 5 // and deprovisioning of storage volumes and filesystems, and attaching them 6 // to and detaching them from machines. 7 // 8 // A storage provisioner worker is run at each model manager, which 9 // manages model-scoped storage such as virtual disk services of the 10 // cloud provider. In addition to this, each machine agent runs a machine- 11 // storage provisioner worker that manages storage scoped to that machine, 12 // such as loop devices, temporary filesystems (tmpfs), and rootfs. 13 // 14 // The storage provisioner worker is comprised of the following major 15 // components: 16 // - a set of watchers for provisioning and attachment events 17 // - a schedule of pending operations 18 // - event-handling code fed by the watcher, that identifies 19 // interesting changes (unprovisioned -> provisioned, etc.), 20 // ensures prerequisites are met (e.g. volume and machine are both 21 // provisioned before attachment is attempted), and populates 22 // operations into the schedule 23 // - operation execution code fed by the schedule, that groups 24 // operations to make bulk calls to storage providers; updates 25 // status; and reschedules operations upon failure 26 // 27 package storageprovisioner 28 29 import ( 30 "github.com/juju/errors" 31 "github.com/juju/loggo" 32 "github.com/juju/names" 33 "github.com/juju/utils/set" 34 35 "github.com/juju/juju/apiserver/params" 36 "github.com/juju/juju/environs/config" 37 "github.com/juju/juju/storage" 38 "github.com/juju/juju/storage/provider" 39 "github.com/juju/juju/watcher" 40 "github.com/juju/juju/worker" 41 "github.com/juju/juju/worker/catacomb" 42 "github.com/juju/juju/worker/storageprovisioner/internal/schedule" 43 ) 44 45 var logger = loggo.GetLogger("juju.worker.storageprovisioner") 46 47 var newManagedFilesystemSource = provider.NewManagedFilesystemSource 48 49 // VolumeAccessor defines an interface used to allow a storage provisioner 50 // worker to perform volume related operations. 51 type VolumeAccessor interface { 52 // WatchBlockDevices watches for changes to the block devices of the 53 // specified machine. 54 WatchBlockDevices(names.MachineTag) (watcher.NotifyWatcher, error) 55 56 // WatchVolumes watches for changes to volumes that this storage 57 // provisioner is responsible for. 58 WatchVolumes() (watcher.StringsWatcher, error) 59 60 // WatchVolumeAttachments watches for changes to volume attachments 61 // that this storage provisioner is responsible for. 62 WatchVolumeAttachments() (watcher.MachineStorageIdsWatcher, error) 63 64 // Volumes returns details of volumes with the specified tags. 65 Volumes([]names.VolumeTag) ([]params.VolumeResult, error) 66 67 // VolumeBlockDevices returns details of block devices corresponding to 68 // the specified volume attachment IDs. 69 VolumeBlockDevices([]params.MachineStorageId) ([]params.BlockDeviceResult, error) 70 71 // VolumeAttachments returns details of volume attachments with 72 // the specified tags. 73 VolumeAttachments([]params.MachineStorageId) ([]params.VolumeAttachmentResult, error) 74 75 // VolumeParams returns the parameters for creating the volumes 76 // with the specified tags. 77 VolumeParams([]names.VolumeTag) ([]params.VolumeParamsResult, error) 78 79 // VolumeAttachmentParams returns the parameters for creating the 80 // volume attachments with the specified tags. 81 VolumeAttachmentParams([]params.MachineStorageId) ([]params.VolumeAttachmentParamsResult, error) 82 83 // SetVolumeInfo records the details of newly provisioned volumes. 84 SetVolumeInfo([]params.Volume) ([]params.ErrorResult, error) 85 86 // SetVolumeAttachmentInfo records the details of newly provisioned 87 // volume attachments. 88 SetVolumeAttachmentInfo([]params.VolumeAttachment) ([]params.ErrorResult, error) 89 } 90 91 // FilesystemAccessor defines an interface used to allow a storage provisioner 92 // worker to perform filesystem related operations. 93 type FilesystemAccessor interface { 94 // WatchFilesystems watches for changes to filesystems that this 95 // storage provisioner is responsible for. 96 WatchFilesystems() (watcher.StringsWatcher, error) 97 98 // WatchFilesystemAttachments watches for changes to filesystem attachments 99 // that this storage provisioner is responsible for. 100 WatchFilesystemAttachments() (watcher.MachineStorageIdsWatcher, error) 101 102 // Filesystems returns details of filesystems with the specified tags. 103 Filesystems([]names.FilesystemTag) ([]params.FilesystemResult, error) 104 105 // FilesystemAttachments returns details of filesystem attachments with 106 // the specified tags. 107 FilesystemAttachments([]params.MachineStorageId) ([]params.FilesystemAttachmentResult, error) 108 109 // FilesystemParams returns the parameters for creating the filesystems 110 // with the specified tags. 111 FilesystemParams([]names.FilesystemTag) ([]params.FilesystemParamsResult, error) 112 113 // FilesystemAttachmentParams returns the parameters for creating the 114 // filesystem attachments with the specified tags. 115 FilesystemAttachmentParams([]params.MachineStorageId) ([]params.FilesystemAttachmentParamsResult, error) 116 117 // SetFilesystemInfo records the details of newly provisioned filesystems. 118 SetFilesystemInfo([]params.Filesystem) ([]params.ErrorResult, error) 119 120 // SetFilesystemAttachmentInfo records the details of newly provisioned 121 // filesystem attachments. 122 SetFilesystemAttachmentInfo([]params.FilesystemAttachment) ([]params.ErrorResult, error) 123 } 124 125 // MachineAccessor defines an interface used to allow a storage provisioner 126 // worker to perform machine related operations. 127 type MachineAccessor interface { 128 // WatchMachine watches for changes to the specified machine. 129 WatchMachine(names.MachineTag) (watcher.NotifyWatcher, error) 130 131 // InstanceIds returns the instance IDs of each machine. 132 InstanceIds([]names.MachineTag) ([]params.StringResult, error) 133 } 134 135 // LifecycleManager defines an interface used to enable a storage provisioner 136 // worker to perform lifcycle-related operations on storage entities and 137 // attachments. 138 type LifecycleManager interface { 139 // Life returns the lifecycle state of the specified entities. 140 Life([]names.Tag) ([]params.LifeResult, error) 141 142 // Remove removes the specified entities from state. 143 Remove([]names.Tag) ([]params.ErrorResult, error) 144 145 // AttachmentLife returns the lifecycle state of the specified 146 // machine/entity attachments. 147 AttachmentLife([]params.MachineStorageId) ([]params.LifeResult, error) 148 149 // RemoveAttachments removes the specified machine/entity attachments 150 // from state. 151 RemoveAttachments([]params.MachineStorageId) ([]params.ErrorResult, error) 152 } 153 154 // StatusSetter defines an interface used to set the status of entities. 155 type StatusSetter interface { 156 SetStatus([]params.EntityStatusArgs) error 157 } 158 159 // ModelAccessor defines an interface used to enable a storage provisioner 160 // worker to watch changes to and read model config, to use when 161 // provisioning storage. 162 type ModelAccessor interface { 163 // WatchForModelConfigChanges returns a watcher that will be notified 164 // whenever the model config changes in state. 165 WatchForModelConfigChanges() (watcher.NotifyWatcher, error) 166 167 // ModelConfig returns the current model config. 168 ModelConfig() (*config.Config, error) 169 } 170 171 // NewStorageProvisioner returns a Worker which manages 172 // provisioning (deprovisioning), and attachment (detachment) 173 // of first-class volumes and filesystems. 174 // 175 // Machine-scoped storage workers will be provided with 176 // a storage directory, while model-scoped workers 177 // will not. If the directory path is non-empty, then it 178 // will be passed to the storage source via its config. 179 var NewStorageProvisioner = func(config Config) (worker.Worker, error) { 180 if err := config.Validate(); err != nil { 181 return nil, errors.Trace(err) 182 } 183 w := &storageProvisioner{ 184 config: config, 185 } 186 err := catacomb.Invoke(catacomb.Plan{ 187 Site: &w.catacomb, 188 Work: w.loop, 189 }) 190 if err != nil { 191 return nil, errors.Trace(err) 192 } 193 return w, nil 194 } 195 196 type storageProvisioner struct { 197 catacomb catacomb.Catacomb 198 config Config 199 } 200 201 // Kill implements Worker.Kill(). 202 func (w *storageProvisioner) Kill() { 203 w.catacomb.Kill(nil) 204 } 205 206 // Wait implements Worker.Wait(). 207 func (w *storageProvisioner) Wait() error { 208 return w.catacomb.Wait() 209 } 210 211 func (w *storageProvisioner) loop() error { 212 var ( 213 volumesChanges watcher.StringsChannel 214 filesystemsChanges watcher.StringsChannel 215 volumeAttachmentsChanges watcher.MachineStorageIdsChannel 216 filesystemAttachmentsChanges watcher.MachineStorageIdsChannel 217 machineBlockDevicesChanges <-chan struct{} 218 ) 219 machineChanges := make(chan names.MachineTag) 220 221 modelConfigWatcher, err := w.config.Environ.WatchForModelConfigChanges() 222 if err != nil { 223 return errors.Annotate(err, "watching model config") 224 } 225 if err := w.catacomb.Add(modelConfigWatcher); err != nil { 226 return errors.Trace(err) 227 } 228 229 // Machine-scoped provisioners need to watch block devices, to create 230 // volume-backed filesystems. 231 if machineTag, ok := w.config.Scope.(names.MachineTag); ok { 232 machineBlockDevicesWatcher, err := w.config.Volumes.WatchBlockDevices(machineTag) 233 if err != nil { 234 return errors.Annotate(err, "watching block devices") 235 } 236 if err := w.catacomb.Add(machineBlockDevicesWatcher); err != nil { 237 return errors.Trace(err) 238 } 239 machineBlockDevicesChanges = machineBlockDevicesWatcher.Changes() 240 } 241 242 startWatchers := func() error { 243 volumesWatcher, err := w.config.Volumes.WatchVolumes() 244 if err != nil { 245 return errors.Annotate(err, "watching volumes") 246 } 247 if err := w.catacomb.Add(volumesWatcher); err != nil { 248 return errors.Trace(err) 249 } 250 volumesChanges = volumesWatcher.Changes() 251 252 filesystemsWatcher, err := w.config.Filesystems.WatchFilesystems() 253 if err != nil { 254 return errors.Annotate(err, "watching filesystems") 255 } 256 if err := w.catacomb.Add(filesystemsWatcher); err != nil { 257 return errors.Trace(err) 258 } 259 filesystemsChanges = filesystemsWatcher.Changes() 260 261 volumeAttachmentsWatcher, err := w.config.Volumes.WatchVolumeAttachments() 262 if err != nil { 263 return errors.Annotate(err, "watching volume attachments") 264 } 265 if err := w.catacomb.Add(volumeAttachmentsWatcher); err != nil { 266 return errors.Trace(err) 267 } 268 volumeAttachmentsChanges = volumeAttachmentsWatcher.Changes() 269 270 filesystemAttachmentsWatcher, err := w.config.Filesystems.WatchFilesystemAttachments() 271 if err != nil { 272 return errors.Annotate(err, "watching filesystem attachments") 273 } 274 if err := w.catacomb.Add(filesystemAttachmentsWatcher); err != nil { 275 return errors.Trace(err) 276 } 277 filesystemAttachmentsChanges = filesystemAttachmentsWatcher.Changes() 278 return nil 279 } 280 281 ctx := context{ 282 kill: w.catacomb.Kill, 283 addWorker: w.catacomb.Add, 284 config: w.config, 285 volumes: make(map[names.VolumeTag]storage.Volume), 286 volumeAttachments: make(map[params.MachineStorageId]storage.VolumeAttachment), 287 volumeBlockDevices: make(map[names.VolumeTag]storage.BlockDevice), 288 filesystems: make(map[names.FilesystemTag]storage.Filesystem), 289 filesystemAttachments: make(map[params.MachineStorageId]storage.FilesystemAttachment), 290 machines: make(map[names.MachineTag]*machineWatcher), 291 machineChanges: machineChanges, 292 schedule: schedule.NewSchedule(w.config.Clock), 293 incompleteVolumeParams: make(map[names.VolumeTag]storage.VolumeParams), 294 incompleteVolumeAttachmentParams: make(map[params.MachineStorageId]storage.VolumeAttachmentParams), 295 incompleteFilesystemParams: make(map[names.FilesystemTag]storage.FilesystemParams), 296 incompleteFilesystemAttachmentParams: make(map[params.MachineStorageId]storage.FilesystemAttachmentParams), 297 pendingVolumeBlockDevices: make(set.Tags), 298 } 299 ctx.managedFilesystemSource = newManagedFilesystemSource( 300 ctx.volumeBlockDevices, ctx.filesystems, 301 ) 302 for { 303 304 // Check if block devices need to be refreshed. 305 if err := processPendingVolumeBlockDevices(&ctx); err != nil { 306 return errors.Annotate(err, "processing pending block devices") 307 } 308 309 select { 310 case <-w.catacomb.Dying(): 311 return w.catacomb.ErrDying() 312 case _, ok := <-modelConfigWatcher.Changes(): 313 if !ok { 314 return errors.New("environ config watcher closed") 315 } 316 modelConfig, err := w.config.Environ.ModelConfig() 317 if err != nil { 318 return errors.Annotate(err, "getting model config") 319 } 320 if ctx.modelConfig == nil { 321 // We've received the initial model config, 322 // so we can begin provisioning storage. 323 if err := startWatchers(); err != nil { 324 return err 325 } 326 } 327 ctx.modelConfig = modelConfig 328 case changes, ok := <-volumesChanges: 329 if !ok { 330 return errors.New("volumes watcher closed") 331 } 332 if err := volumesChanged(&ctx, changes); err != nil { 333 return errors.Trace(err) 334 } 335 case changes, ok := <-volumeAttachmentsChanges: 336 if !ok { 337 return errors.New("volume attachments watcher closed") 338 } 339 if err := volumeAttachmentsChanged(&ctx, changes); err != nil { 340 return errors.Trace(err) 341 } 342 case changes, ok := <-filesystemsChanges: 343 if !ok { 344 return errors.New("filesystems watcher closed") 345 } 346 if err := filesystemsChanged(&ctx, changes); err != nil { 347 return errors.Trace(err) 348 } 349 case changes, ok := <-filesystemAttachmentsChanges: 350 if !ok { 351 return errors.New("filesystem attachments watcher closed") 352 } 353 if err := filesystemAttachmentsChanged(&ctx, changes); err != nil { 354 return errors.Trace(err) 355 } 356 case _, ok := <-machineBlockDevicesChanges: 357 if !ok { 358 return errors.New("machine block devices watcher closed") 359 } 360 if err := machineBlockDevicesChanged(&ctx); err != nil { 361 return errors.Trace(err) 362 } 363 case machineTag := <-machineChanges: 364 if err := refreshMachine(&ctx, machineTag); err != nil { 365 return errors.Trace(err) 366 } 367 case <-ctx.schedule.Next(): 368 // Ready to pick something(s) off the pending queue. 369 if err := processSchedule(&ctx); err != nil { 370 return errors.Trace(err) 371 } 372 } 373 } 374 } 375 376 // processSchedule executes scheduled operations. 377 func processSchedule(ctx *context) error { 378 ready := ctx.schedule.Ready(ctx.config.Clock.Now()) 379 createVolumeOps := make(map[names.VolumeTag]*createVolumeOp) 380 destroyVolumeOps := make(map[names.VolumeTag]*destroyVolumeOp) 381 attachVolumeOps := make(map[params.MachineStorageId]*attachVolumeOp) 382 detachVolumeOps := make(map[params.MachineStorageId]*detachVolumeOp) 383 createFilesystemOps := make(map[names.FilesystemTag]*createFilesystemOp) 384 destroyFilesystemOps := make(map[names.FilesystemTag]*destroyFilesystemOp) 385 attachFilesystemOps := make(map[params.MachineStorageId]*attachFilesystemOp) 386 detachFilesystemOps := make(map[params.MachineStorageId]*detachFilesystemOp) 387 for _, item := range ready { 388 op := item.(scheduleOp) 389 key := op.key() 390 switch op := op.(type) { 391 case *createVolumeOp: 392 createVolumeOps[key.(names.VolumeTag)] = op 393 case *destroyVolumeOp: 394 destroyVolumeOps[key.(names.VolumeTag)] = op 395 case *attachVolumeOp: 396 attachVolumeOps[key.(params.MachineStorageId)] = op 397 case *detachVolumeOp: 398 detachVolumeOps[key.(params.MachineStorageId)] = op 399 case *createFilesystemOp: 400 createFilesystemOps[key.(names.FilesystemTag)] = op 401 case *destroyFilesystemOp: 402 destroyFilesystemOps[key.(names.FilesystemTag)] = op 403 case *attachFilesystemOp: 404 attachFilesystemOps[key.(params.MachineStorageId)] = op 405 case *detachFilesystemOp: 406 detachFilesystemOps[key.(params.MachineStorageId)] = op 407 } 408 } 409 if len(destroyVolumeOps) > 0 { 410 if err := destroyVolumes(ctx, destroyVolumeOps); err != nil { 411 return errors.Annotate(err, "destroying volumes") 412 } 413 } 414 if len(createVolumeOps) > 0 { 415 if err := createVolumes(ctx, createVolumeOps); err != nil { 416 return errors.Annotate(err, "creating volumes") 417 } 418 } 419 if len(detachVolumeOps) > 0 { 420 if err := detachVolumes(ctx, detachVolumeOps); err != nil { 421 return errors.Annotate(err, "detaching volumes") 422 } 423 } 424 if len(attachVolumeOps) > 0 { 425 if err := attachVolumes(ctx, attachVolumeOps); err != nil { 426 return errors.Annotate(err, "attaching volumes") 427 } 428 } 429 if len(destroyFilesystemOps) > 0 { 430 if err := destroyFilesystems(ctx, destroyFilesystemOps); err != nil { 431 return errors.Annotate(err, "destroying filesystems") 432 } 433 } 434 if len(createFilesystemOps) > 0 { 435 if err := createFilesystems(ctx, createFilesystemOps); err != nil { 436 return errors.Annotate(err, "creating filesystems") 437 } 438 } 439 if len(detachFilesystemOps) > 0 { 440 if err := detachFilesystems(ctx, detachFilesystemOps); err != nil { 441 return errors.Annotate(err, "detaching filesystems") 442 } 443 } 444 if len(attachFilesystemOps) > 0 { 445 if err := attachFilesystems(ctx, attachFilesystemOps); err != nil { 446 return errors.Annotate(err, "attaching filesystems") 447 } 448 } 449 return nil 450 } 451 452 type context struct { 453 kill func(error) 454 addWorker func(worker.Worker) error 455 config Config 456 modelConfig *config.Config 457 458 // volumes contains information about provisioned volumes. 459 volumes map[names.VolumeTag]storage.Volume 460 461 // volumeAttachments contains information about attached volumes. 462 volumeAttachments map[params.MachineStorageId]storage.VolumeAttachment 463 464 // volumeBlockDevices contains information about block devices 465 // corresponding to volumes attached to the scope-machine. This 466 // is only used by the machine-scoped storage provisioner. 467 volumeBlockDevices map[names.VolumeTag]storage.BlockDevice 468 469 // filesystems contains information about provisioned filesystems. 470 filesystems map[names.FilesystemTag]storage.Filesystem 471 472 // filesystemAttachments contains information about attached filesystems. 473 filesystemAttachments map[params.MachineStorageId]storage.FilesystemAttachment 474 475 // machines contains information about machines in the worker's scope. 476 machines map[names.MachineTag]*machineWatcher 477 478 // machineChanges is a channel that machine watchers will send to once 479 // their machine is known to have been provisioned. 480 machineChanges chan<- names.MachineTag 481 482 // schedule is the schedule of storage operations. 483 schedule *schedule.Schedule 484 485 // incompleteVolumeParams contains incomplete parameters for volumes. 486 // 487 // Volume parameters are incomplete when they lack information about 488 // the initial attachment. Once the initial attachment information is 489 // available, the parameters are removed from this map and a volume 490 // creation operation is scheduled. 491 incompleteVolumeParams map[names.VolumeTag]storage.VolumeParams 492 493 // incompleteVolumeAttachmentParams contains incomplete parameters 494 // for volume attachments 495 // 496 // Volume attachment parameters are incomplete when they lack 497 // information about the associated volume or machine. Once this 498 // information is available, the parameters are removed from this 499 // map and a volume attachment operation is scheduled. 500 incompleteVolumeAttachmentParams map[params.MachineStorageId]storage.VolumeAttachmentParams 501 502 // incompleteFilesystemParams contains incomplete parameters for 503 // filesystems. 504 // 505 // Filesystem parameters are incomplete when they lack information 506 // about the initial attachment. Once the initial attachment 507 // information is available, the parameters are removed from this 508 // map and a filesystem creation operation is scheduled. 509 incompleteFilesystemParams map[names.FilesystemTag]storage.FilesystemParams 510 511 // incompleteFilesystemAttachmentParams contains incomplete parameters 512 // for filesystem attachments 513 // 514 // Filesystem attachment parameters are incomplete when they lack 515 // information about the associated filesystem or machine. Once this 516 // information is available, the parameters are removed from this 517 // map and a filesystem attachment operation is scheduled. 518 incompleteFilesystemAttachmentParams map[params.MachineStorageId]storage.FilesystemAttachmentParams 519 520 // pendingVolumeBlockDevices contains the tags of volumes about whose 521 // block devices we wish to enquire. 522 pendingVolumeBlockDevices set.Tags 523 524 // managedFilesystemSource is a storage.FilesystemSource that 525 // manages filesystems backed by volumes attached to the host 526 // machine. 527 managedFilesystemSource storage.FilesystemSource 528 }