Class: OsCtld::Devices::Manager
- Inherits:
-
Object
- Object
- OsCtld::Devices::Manager
- Defined in:
- lib/osctld/devices/manager.rb
Direct Known Subclasses
V1::ContainerManager, V1::GroupManager, V2::ContainerManager, V2::GroupManager
Instance Attribute Summary collapse
-
#configurator ⇒ Object
readonly
protected
Returns the value of attribute configurator.
-
#devices ⇒ Object
readonly
protected
Returns the value of attribute devices.
-
#owner ⇒ Object
readonly
protected
Returns the value of attribute owner.
Class Method Summary collapse
-
.class_for(owner) ⇒ Class
Get manager class group/container on cgroup v1/v2.
- .load(owner, cfg) ⇒ Object
- .new_for(owner) ⇒ Devices::Manager
Instance Method Summary collapse
-
#add(device) ⇒ Object
Add new device and ensure that parent groups provide it.
-
#add_new(type, major, minor, mode) ⇒ Object
Add new device.
- #add_to_changeset ⇒ Object protected
-
#apply(parents: false, descendants: false) ⇒ Object
Configure devices in cgroup.
- #assets(add) ⇒ Object
- #changeset_sort_key ⇒ any
-
#check_availability!(device, mode: nil, parent: nil) ⇒ Object
Check whether the device is available in parents.
- #check_descendant_mode!(device, mode) ⇒ Object protected
-
#check_descendants!(device, mode: nil) ⇒ Object
Check whether descendants do not have broader mode requirements.
-
#check_unset_inherit!(device, check_owner: nil) ⇒ Object
protected
Check if the inheritance can be disabled.
- #children ⇒ Array<Devices::Owner>
- #chmod(device, mode, parents: false, promote: false, descendants: false, **_opts) ⇒ Object
- #configurator_class ⇒ Class
- #detect {|| ... } ⇒ Object
- #do_add(device) ⇒ Object protected
-
#dump ⇒ Array<Hash>
Dump device configuration into the config.
- #dup(new_owner) ⇒ Object
- #each {|| ... } ⇒ Object
-
#export ⇒ Array<Hash>
Export devices to clients.
-
#find(type, major, minor) ⇒ Devices::Device?
Find device by ‘type` and `major` and `minor` numbers.
-
#get(device) ⇒ Devices::Device?
Find and return device.
-
#include?(device) ⇒ Boolean
Check if we have a particular device.
-
#inherit(device, **_opts) ⇒ Object
Inherit device from a parent.
-
#inherit_promoted(device) ⇒ Object
Inherit a promoted device.
-
#inherit_recursive(device) ⇒ Object
Add the device to all descendants.
-
#inherited?(device) ⇒ Boolean
Check if device exists and is inherited.
-
#init(**_opts) ⇒ Object
Initialize device list.
-
#initialize(owner, devices: []) ⇒ Manager
constructor
A new instance of Manager.
- #parent ⇒ Devices::Owner?
-
#promote(device) ⇒ Object
Promote device, i.e.
-
#provide(device) ⇒ Object
Add device to make it available to child groups.
-
#remove(device) ⇒ Object
Remove device from self.
-
#replace(new_devices) ⇒ Object
Replace configured devices by a new set.
- #select {|| ... } ⇒ Object
-
#set_inherit(device) ⇒ Object
Mark device as inheritable.
- #sync ⇒ Object protected
-
#uninherit_recursive(device) ⇒ Object
Remove the device from all descendats.
-
#unset_inherit(device) ⇒ Object
Remove inheritable mark.
-
#update_inherited_mode(device, mode, changes) ⇒ Object
Called when the access mode of the device in the parent group changes.
-
#used?(device) ⇒ Boolean
Check if device exists and is used, not just inherited.
- #used_by_descendants?(device) ⇒ Boolean
Constructor Details
#initialize(owner, devices: []) ⇒ Manager
Returns a new instance of Manager.
38 39 40 41 42 |
# File 'lib/osctld/devices/manager.rb', line 38 def initialize(owner, devices: []) @owner = owner @devices = devices @configurator = configurator_class.new(owner) end |
Instance Attribute Details
#configurator ⇒ Object (readonly, protected)
Returns the value of attribute configurator.
562 563 564 |
# File 'lib/osctld/devices/manager.rb', line 562 def configurator @configurator end |
#devices ⇒ Object (readonly, protected)
Returns the value of attribute devices.
562 563 564 |
# File 'lib/osctld/devices/manager.rb', line 562 def devices @devices end |
#owner ⇒ Object (readonly, protected)
Returns the value of attribute owner.
562 563 564 |
# File 'lib/osctld/devices/manager.rb', line 562 def owner @owner end |
Class Method Details
.class_for(owner) ⇒ Class
Get manager class group/container on cgroup v1/v2
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/osctld/devices/manager.rb', line 6 def self.class_for(owner) mod = if CGroup.v1? Devices::V1 else Devices::V2 end case owner when Group mod::GroupManager when Container mod::ContainerManager else raise "unsupported device owner #{owner.inspect}" end end |
.load(owner, cfg) ⇒ Object
32 33 34 |
# File 'lib/osctld/devices/manager.rb', line 32 def self.load(owner, cfg) new_for(owner, devices: cfg.map { |v| Devices::Device.load(v) }) end |
.new_for(owner) ⇒ Devices::Manager
26 27 28 |
# File 'lib/osctld/devices/manager.rb', line 26 def self.new_for(owner, **) class_for(owner).new(owner, **) end |
Instance Method Details
#add(device) ⇒ Object
Add new device and ensure that parent groups provide it
82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/osctld/devices/manager.rb', line 82 def add(device) sync do parent.devices.provide(device) if parent do_add(device) add_to_changeset configurator.add_device(device) if device.inherit? inherit_recursive(device) end end end |
#add_new(type, major, minor, mode) ⇒ Object
Add new device
76 77 78 |
# File 'lib/osctld/devices/manager.rb', line 76 def add_new(type, major, minor, mode, **) add(Devices::Device.new(type, major.to_s, minor.to_s, mode, **)) end |
#add_to_changeset ⇒ Object (protected)
610 611 612 |
# File 'lib/osctld/devices/manager.rb', line 610 def add_to_changeset Devices::ChangeSet.add(owner.pool, self, changeset_sort_key) end |
#apply(parents: false, descendants: false) ⇒ Object
Configure devices in cgroup
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/osctld/devices/manager.rb', line 327 def apply(parents: false, descendants: false) sync do if parents && parent parent.devices.apply(parents: true) end configurator.reconfigure(devices) if descendants children.each do |child| child.devices.apply(descendants: true) end end end end |
#assets(add) ⇒ Object
45 |
# File 'lib/osctld/devices/manager.rb', line 45 def assets(add); end |
#changeset_sort_key ⇒ any
534 535 536 |
# File 'lib/osctld/devices/manager.rb', line 534 def changeset_sort_key raise NotImplementedError end |
#check_availability!(device, mode: nil, parent: nil) ⇒ Object
Check whether the device is available in parents
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/osctld/devices/manager.rb', line 403 def check_availability!(device, mode: nil, parent: nil) sync do p = parent || self.parent loop do break if p.nil? dev = p.devices.detect { |v| v == device } if dev.nil? raise DeviceNotAvailable.new(device, p) elsif !dev.mode.compatible?(mode || device.mode) raise DeviceModeInsufficient.new(device, p, dev.mode) end p = p.devices.parent end end end |
#check_descendant_mode!(device, mode) ⇒ Object (protected)
603 604 605 606 607 608 |
# File 'lib/osctld/devices/manager.rb', line 603 def check_descendant_mode!(device, mode) dev = get(device) return if !dev || mode.compatible?(dev.mode) raise DeviceDescendantRequiresMode.new(owner, dev.mode) end |
#check_descendants!(device, mode: nil) ⇒ Object
Check whether descendants do not have broader mode requirements
424 425 426 427 428 429 430 |
# File 'lib/osctld/devices/manager.rb', line 424 def check_descendants!(device, mode: nil) sync do children.each do |child| child.devices.check_descendant_mode!(device, mode || device.mode) end end end |
#check_unset_inherit!(device, check_owner: nil) ⇒ Object (protected)
Check if the inheritance can be disabled
We have to forbid disabling the inheritance if any grandchild has the device promoted, but its parent does not. Thus, the device cannot be deleted from self, because the grandchild needs it.
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 |
# File 'lib/osctld/devices/manager.rb', line 585 def check_unset_inherit!(device, check_owner: nil) if check_owner.nil? children.each do |child| if child.devices.inherited?(device) # Since our child inherits the device, his descendants must not use it check_unset_inherit!(device, check_owner: child) end end else check_owner.devices.children.each do |child| raise DeviceInheritNeeded, child if child.devices.used?(device) check_unset_inherit!(device, check_owner: child) end end end |
#children ⇒ Array<Devices::Owner>
524 525 526 |
# File 'lib/osctld/devices/manager.rb', line 524 def children raise NotImplementedError end |
#chmod(device, mode, parents: false, promote: false, descendants: false, **_opts) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/osctld/devices/manager.rb', line 178 def chmod(device, mode, parents: false, promote: false, descendants: false, **_opts) sync do if parents && parent dev = device.clone dev.mode = mode parent.devices.provide(dev) end changes = device.chmod(mode) device.inherited = false if promote && device.inherited? owner.save_config add_to_changeset configurator.apply_changes(changes) if descendants children.each do |child| dev = child.devices.get(device) next if dev.nil? child.devices.chmod(dev, mode, descendants: true) end end end end |
#configurator_class ⇒ Class
529 530 531 |
# File 'lib/osctld/devices/manager.rb', line 529 def configurator_class raise NotImplementedError end |
#detect {|| ... } ⇒ Object
509 510 511 |
# File 'lib/osctld/devices/manager.rb', line 509 def detect(&block) sync { devices.detect(&block) } end |
#do_add(device) ⇒ Object (protected)
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/osctld/devices/manager.rb', line 564 def do_add(device) if device.inherited? devices << device else # Add promoted devices before inherited devices, as those are always # added later when read from configuration files. i = devices.index(&:inherited?) if i.nil? devices << device else devices.insert(i, device) end end end |
#dump ⇒ Array<Hash>
Dump device configuration into the config
546 547 548 |
# File 'lib/osctld/devices/manager.rb', line 546 def dump sync { devices.reject(&:inherited?).map(&:dump) } end |
#dup(new_owner) ⇒ Object
550 551 552 553 554 555 556 557 558 |
# File 'lib/osctld/devices/manager.rb', line 550 def dup(new_owner) sync do ret = super() ret.instance_variable_set('@owner', new_owner) ret.instance_variable_set('@devices', devices.map(&:clone)) ret.instance_variable_set('@configurator', configurator.dup(new_owner)) ret end end |
#each {|| ... } ⇒ Object
504 505 506 |
# File 'lib/osctld/devices/manager.rb', line 504 def each(&block) sync { devices.each(&block) } end |
#export ⇒ Array<Hash>
Export devices to clients
540 541 542 |
# File 'lib/osctld/devices/manager.rb', line 540 def export sync { devices.map(&:export) } end |
#find(type, major, minor) ⇒ Devices::Device?
Find device by ‘type` and `major` and `minor` numbers
451 452 453 454 455 456 457 |
# File 'lib/osctld/devices/manager.rb', line 451 def find(type, major, minor) sync do devices.detect do |dev| dev.type == type && dev.major == major && dev.minor == minor end end end |
#get(device) ⇒ Devices::Device?
Find and return device
The point of this method is to return the manager’s own device’s instance. Devices equality is tested by comparing its type and major and minor numbers, but its devnode name or mode can be different.
465 466 467 468 469 470 |
# File 'lib/osctld/devices/manager.rb', line 465 def get(device) sync do i = devices.index(device) i ? devices[i] : nil end end |
#include?(device) ⇒ Boolean
Check if we have a particular device
475 476 477 |
# File 'lib/osctld/devices/manager.rb', line 475 def include?(device) sync { devices.include?(device) } end |
#inherit(device, **_opts) ⇒ Object
Inherit device from a parent
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/osctld/devices/manager.rb', line 99 def inherit(device, **_opts) sync do dev = device.clone dev.inherited = true do_add(dev) add_to_changeset configurator.add_device(dev) children.each do |child| child.devices.inherit(dev) end end end |
#inherit_promoted(device) ⇒ Object
Inherit a promoted device
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/osctld/devices/manager.rb', line 213 def inherit_promoted(device) sync do parent_dev = parent.devices.get(device) if parent if parent_dev && parent_dev.inherit? # We can keep the device and descendants unchanged device.inherited = true # Parent group can have broader access mode, so we need to expand it if device.mode != parent_dev.mode add_to_changeset changes = device.chmod(parent_dev.mode.clone) configurator.apply_changes(changes) # Update descendants that inherit the device as well children.each do |child| child_dev = child.devices.get(device) next unless child_dev.inherited? child.devices.update_inherited_mode(child_dev, parent_dev.mode.clone, changes) end end owner.save_config return end # Parent does not provide the device, remove it if used_by_descendants?(device) raise DeviceInUse, 'the device would be removed, but child groups or containers ' \ 'are using it' end remove(device) end end |
#inherit_recursive(device) ⇒ Object
Add the device to all descendants
299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/osctld/devices/manager.rb', line 299 def inherit_recursive(device) sync do children.each do |child| next if child.devices.include?(device) # Add from the top down child.devices.inherit(device) child.devices.inherit_recursive(device) end end end |
#inherited?(device) ⇒ Boolean
Check if device exists and is inherited
482 483 484 485 486 487 488 489 |
# File 'lib/osctld/devices/manager.rb', line 482 def inherited?(device) sync do dev = devices.detect { |v| v == device } next(false) unless dev dev.inherited? end end |
#init(**_opts) ⇒ Object
Initialize device list
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/osctld/devices/manager.rb', line 49 def init(**_opts) sync do if parent parent.devices.each do |parent_dev| next if !parent_dev.inherit? || include?(parent_dev) dev = parent_dev.clone dev.inherited = true do_add(dev) end end configurator.init(devices) end end |
#parent ⇒ Devices::Owner?
519 520 521 |
# File 'lib/osctld/devices/manager.rb', line 519 def parent raise NotImplementedError end |
#promote(device) ⇒ Object
Promote device, i.e. remove its inherited status and save it in config
206 207 208 209 |
# File 'lib/osctld/devices/manager.rb', line 206 def promote(device) device.inherited = false owner.save_config end |
#provide(device) ⇒ Object
Add device to make it available to child groups
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/osctld/devices/manager.rb', line 116 def provide(device) sync do dev = get(device) if dev # Check if we have compatible modes return if dev.mode.compatible?(device.mode) # Broader access mode is required dev.mode.complement(device.mode) # Propagate the mode change to parent groups parent.devices.provide(device) if parent # Apply cgroup # Since the mode was complemented, we don't have to deny existing # access modes, only allow new ones add_to_changeset configurator.add_device(dev) # Save it owner.save_config return end # Device does not exist, ask the parent to provide it and create it parent.devices.provide(device) if parent dev = device.clone dev.inherit = false do_add(dev) add_to_changeset configurator.add_device(dev) owner.save_config end end |
#remove(device) ⇒ Object
Remove device from self
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/osctld/devices/manager.rb', line 156 def remove(device) sync do children.each do |child| child_dev = child.devices.get(device) next if child_dev.nil? child.devices.remove(child_dev) end devices.delete(device) owner.save_config add_to_changeset configurator.remove_device(device) end end |
#replace(new_devices) ⇒ Object
Replace configured devices by a new set
‘new_devices` has to contain devices that are to be promoted. Devices that were promoted but are no longer in `new_devices` will be removed. Devices that are inherited from parent groups are promoted if they’re in ‘new_devices`, otherwire they’re left alone.
Note that this method does not enforce nor manage proper parent/descendant dependencies. It is possible to add a device which is not provided by parents or to remove a device that is needed by descendants.
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/osctld/devices/manager.rb', line 355 def replace(new_devices) sync do to_add = [] to_inherit = [] to_promote = [] to_chmod = [] # Find devices to promote, chmod and remove/inherit devices.each do |cur_dev| found = new_devices.detect { |new_dev| new_dev == cur_dev } if found.nil? to_inherit << cur_dev unless cur_dev.inherited? elsif found.inherited? if found.mode == cur_dev.mode to_promote << cur_dev else to_chmod << [cur_dev, found.mode] end elsif found.mode != cur_dev.mode to_chmod << [cur_dev, found.mode] end end # Find devices to add new_devices.each do |new_dev| found = devices.detect { |cur_dev| cur_dev == new_dev } to_add << new_dev if found.nil? end # Apply changes to_add.each { |dev| add(dev) } to_promote.each { |dev| promote(dev) } to_chmod.each do |dev, mode| chmod(dev, mode, promote: true, descendants: true) end to_inherit.each { |dev| inherit_promoted(dev) } apply(descendants: true) end end |
#select {|| ... } ⇒ Object
514 515 516 |
# File 'lib/osctld/devices/manager.rb', line 514 def select(&block) sync { devices.select(&block) } end |
#set_inherit(device) ⇒ Object
Mark device as inheritable
278 279 280 281 282 283 284 |
# File 'lib/osctld/devices/manager.rb', line 278 def set_inherit(device) sync do device.inherit = true inherit_recursive(device) owner.save_config end end |
#sync ⇒ Object (protected)
614 615 616 |
# File 'lib/osctld/devices/manager.rb', line 614 def sync(&) Devices::Lock.sync(owner.pool, &) end |
#uninherit_recursive(device) ⇒ Object
Remove the device from all descendats
312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/osctld/devices/manager.rb', line 312 def uninherit_recursive(device) sync do children.each do |child| next if child.devices.used?(device) # Remove from the bottom up child.devices.uninherit_recursive(device) child.devices.remove(device) end end end |
#unset_inherit(device) ⇒ Object
Remove inheritable mark
288 289 290 291 292 293 294 295 296 |
# File 'lib/osctld/devices/manager.rb', line 288 def unset_inherit(device) sync do check_unset_inherit!(device) device.inherit = false uninherit_recursive(device) owner.save_config end end |
#update_inherited_mode(device, mode, changes) ⇒ Object
Called when the access mode of the device in the parent group changes
This method should update the mode and pass the information to its own descendants.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/osctld/devices/manager.rb', line 259 def update_inherited_mode(device, mode, changes) sync do device.mode = mode owner.save_config add_to_changeset configurator.apply_changes(changes) children.each do |child| child_dev = child.devices.get(device) next unless child_dev.inherited? child.devices.update_inherited_mode(child_dev, mode.clone, changes) end end end |
#used?(device) ⇒ Boolean
Check if device exists and is used, not just inherited
494 495 496 497 498 499 500 501 |
# File 'lib/osctld/devices/manager.rb', line 494 def used?(device) sync do dev = devices.detect { |v| v == device } next(false) unless dev !dev.inherited? end end |
#used_by_descendants?(device) ⇒ Boolean
434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/osctld/devices/manager.rb', line 434 def used_by_descendants?(device) sync do children.each do |child| if child.devices.used?(device) || child.devices.used_by_descendants?(device) return true end end end false end |