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