Class: OsCtld::Devices::ContainerManager

Inherits:
Manager
  • Object
show all
Includes:
OsCtl::Lib::Utils::Log
Defined in:
lib/osctld/devices/container_manager.rb

Instance Attribute Summary

Attributes inherited from Manager

#devices, #owner

Instance Method Summary collapse

Methods inherited from Manager

#add, #add_new, #check_availability!, #clear, #detect, #do_add, #do_allow_dev, #do_apply_changes, #do_deny_all, #do_deny_dev, #dump, #dup, #each, #export, #find, #get, #include?, #inherit, #inherit_all_from, #initialize, load, #promote, #remove, #replace, #select, #set_inherit, #sync, #unset_inherit, #used?

Constructor Details

This class inherits a constructor from OsCtld::Devices::Manager

Instance Method Details

#abs_all_cgroup_pathsArray (protected)

Returns:

  • (Array)


263
264
265
# File 'lib/osctld/devices/container_manager.rb', line 263

def abs_all_cgroup_paths
  abs_group_cgroup_paths + abs_ct_cgroup_paths
end

#abs_ct_cgroup_pathsArray (protected)

Returns a list of all absolute cgroup paths that need to be configured for this container, from the top down.

Returns:

  • (Array)


241
242
243
# File 'lib/osctld/devices/container_manager.rb', line 241

def abs_ct_cgroup_paths
  to_abs_paths(rel_ct_cgroup_paths)
end

#abs_ct_chowned_cgroup_pathsArray (protected)

Returns a list of the container's absolute cgroup paths that are to be chowned to the user.

Returns:

  • (Array)


248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/osctld/devices/container_manager.rb', line 248

def abs_ct_chowned_cgroup_paths
  to_abs_paths([
    # <group>/<user>/<ct>/user-owned
    [ct.cgroup_path, true],

    # <group>/<user>/<ct>/user-owned/lxc
    [File.join(ct.cgroup_path, 'lxc'), true],

    # <group>/<user>/<ct>/user-owned/lxc/<ct>
    [File.join(ct.cgroup_path, 'lxc', ct.id), false,
     ct.user.ugid, ct.gid_map.ns_to_host(0)],
  ])
end

#abs_group_cgroup_pathsArray (protected)

Returns a list of absolute paths of the container's group cgroups

Returns:

  • (Array)


234
235
236
# File 'lib/osctld/devices/container_manager.rb', line 234

def abs_group_cgroup_paths
  to_abs_paths(rel_group_cgroup_paths)
end

#apply(_opts = {}) ⇒ Object

Apply the container's device cgroup settings



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/osctld/devices/container_manager.rb', line 129

def apply(_opts = {})
  sync do
    # group
    ct.group.devices.apply

    abs_group_cgroup_paths.each do |cgpath, req|
      next unless prepare_cgroup(cgpath, req)
      apply_devices(ct.group.devices, cgpath)
    end

    # container groups
    abs_ct_cgroup_paths.each do |cgpath, req|
      next unless prepare_cgroup(cgpath, req)
      apply_devices(self, cgpath)
    end
  end
end

#apply_devices(devices, path) ⇒ Object (protected)

Parameters:



183
184
185
186
187
# File 'lib/osctld/devices/container_manager.rb', line 183

def apply_devices(devices, path)
  devices.each do |dev|
    CGroup.set_param(File.join(path, 'devices.allow'), [dev.to_s])
  end
end

#check_all_available!(group = nil) ⇒ Object

Check that all devices are provided by parents, or raise an exception

Parameters:

  • group (Group, nil) (defaults to: nil)

    which group to use as the container's parent, defaults to the container's group



150
151
152
153
154
# File 'lib/osctld/devices/container_manager.rb', line 150

def check_all_available!(group = nil)
  sync do
    devices.each { |dev| check_availability!(dev, group || ct.group) }
  end
end

#check_descendants!(*_) ⇒ Object



174
175
176
# File 'lib/osctld/devices/container_manager.rb', line 174

def check_descendants!(*_)
  # Containers do not have any descendants
end

#chmod(device, mode, opts = {}) ⇒ Object

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :parents (Boolean)
  • :group_changes (Hash)


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/osctld/devices/container_manager.rb', line 56

def chmod(device, mode, opts = {})
  sync do
    # Parents
    if opts[:parents]
      dev = device.clone
      dev.mode = mode

      ct.group.devices.provide(dev)
    end

    # <group>/<user>
    if opts[:group_changes] # for recursive chmod from the group down
      abs_group_cgroup_paths.each do |cgpath, req|
        next unless prepare_cgroup(cgpath, req)
        do_apply_changes(opts[:group_changes], path: cgpath)
      end

    else # when chmodding the container itself
      abs_group_cgroup_paths.each do |cgpath, req|
        next unless prepare_cgroup(cgpath, req)
        apply_devices(ct.group.devices, cgpath)
      end
    end

    # Container cgroups
    changes = device.chmod(mode)
    device.inherited = false if opts[:promote] && device.inherited?
    ct.save_config

    abs_ct_cgroup_paths.each do |cgpath, req|
      next unless prepare_cgroup(cgpath, req)
      do_apply_changes(changes, path: cgpath)
    end
  end
end

#clear_devices(path) ⇒ Object (protected)

Parameters:

  • path (String)

    absolute cgroup path



190
191
192
# File 'lib/osctld/devices/container_manager.rb', line 190

def clear_devices(path)
  CGroup.set_param(File.join(path, 'devices.deny'), ['a'])
end

#createObject

Create cgroups and apply device settings



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/osctld/devices/container_manager.rb', line 20

def create
  sync do
    rel_group_cgroup_paths.zip(abs_group_cgroup_paths).each do |rel, abs|
      next if !rel[1] || !abs[1]

      rel_path = rel[0]
      abs_path = abs[0]

      if CGroup.mkpath('devices', rel_path.split('/'))
        clear_devices(abs_path)
        apply_devices(ct.group.devices, abs_path)
      end
    end

    rel_ct_cgroup_paths.zip(abs_ct_cgroup_paths).each do |rel, abs|
      next if !rel[1] || !abs[1]

      rel_path = rel[0]
      abs_path = abs[0]

      if CGroup.mkpath('devices', rel_path.split('/'))
        clear_devices(abs_path)
        apply_devices(self, abs_path)
      end
    end

    abs_ct_chowned_cgroup_paths.each do |abs, req, uid, gid|
      next unless prepare_cgroup(abs, req)
      File.chown(uid || ct.user.ugid, gid || ct.user.ugid, abs)
    end
  end
end

#ensure_allObject

Ensure that all required devices are provided by parent groups



157
158
159
160
161
# File 'lib/osctld/devices/container_manager.rb', line 157

def ensure_all
  sync do
    devices.each { |dev| ct.group.devices.provide(dev) }
  end
end

#inherit_promoted(device) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/osctld/devices/container_manager.rb', line 92

def inherit_promoted(device)
  sync do
    pdev = ct.group.devices.get(device)

    if pdev.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 != pdev.mode
        changes = device.chmod(pdev.mode.clone)

        abs_all_cgroup_paths.each do |cgpath, req|
          next unless prepare_cgroup(cgpath, req)
          do_apply_changes(changes, path: cgpath)
        end
      end

      ct.save_config
      return
    end

    # Parent does not provide the device, remove it
    remove(device)
  end
end

#init(opts = {}) ⇒ Object

Parameters:

  • opts (Hash) (defaults to: {})


9
10
11
12
13
14
15
16
17
# File 'lib/osctld/devices/container_manager.rb', line 9

def init(opts = {})
  sync do
    super
    inherit_all_from(ct.group, opts)

    log(:info, ct, "Configuring cgroup #{ct.cgroup_path} for devices")
    create
  end
end

#prepare_cgroup(cgpath, create) ⇒ Boolean (protected)

Returns `true` if the cgroup exists or was created

Parameters:

  • cgpath (String)

    absolute cgroup path

  • create (Boolean)

    create the cgroup or not

Returns:

  • (Boolean)

    `true` if the cgroup exists or was created



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/osctld/devices/container_manager.rb', line 276

def prepare_cgroup(cgpath, create)
  exists = Dir.exist?(cgpath)

  if exists
    true

  elsif create
    begin
      Dir.mkdir(cgpath)

    rescue Errno::EEXIST
      true
    end

    # uid/gid is inherited from the parent cgroup
    st = File.stat(File.dirname(cgpath))
    File.chown(st.uid, st.gid, cgpath)

  else
    false
  end
end

#rel_ct_cgroup_pathsArray (protected)

Returns a list of all relative cgroup paths that need to be configured for this container, from the top down.

The returned array contains pairs: `[String, Boolean]`. The `String` is the path itself, while the `Boolean` determines whether this path should be created. Paths that do not need to be created are configured only if they already exist. This is used only for the `./lxc/<ct>` cgroup, which LXC wants to create by itself.

Returns:

  • (Array)


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/osctld/devices/container_manager.rb', line 216

def rel_ct_cgroup_paths
  [
    # <group>/<user>/<ct>
    [ct.base_cgroup_path, true],

    # <group>/<user>/<ct>/user-owned
    [ct.cgroup_path, true],

    # <group>/<user>/<ct>/user-owned/lxc
    [File.join(ct.cgroup_path, 'lxc'), true],

    # <group>/<user>/<ct>/user-owned/lxc/<ct>
    [File.join(ct.cgroup_path, 'lxc', ct.id), false],
  ]
end

#rel_group_cgroup_pathsArray (protected)

Returns a list of relative paths of the container's group cgroups.

These cgroups share the settings of the container's group.

Returns:

  • (Array)


199
200
201
202
203
204
# File 'lib/osctld/devices/container_manager.rb', line 199

def rel_group_cgroup_paths
  [
    # <group>/<user>
    [ct.group.full_cgroup_path(ct.user), true],
  ]
end

#remove_missingObject

Remove devices that aren't provided by the parent, or have insufficient access mode



165
166
167
168
169
170
171
172
# File 'lib/osctld/devices/container_manager.rb', line 165

def remove_missing
  sync do
    devices.delete_if do |dev|
      pdev = ct.group.devices.get(dev)
      pdev.nil? || !pdev.mode.compatible?(dev.mode)
    end
  end
end

#to_abs_paths(rel_paths) ⇒ Object (protected)



267
268
269
270
271
# File 'lib/osctld/devices/container_manager.rb', line 267

def to_abs_paths(rel_paths)
  rel_paths.map do |path, req, *args|
    [File.join(CGroup::FS, CGroup.real_subsystem('devices'), path), req, *args]
  end
end

#update_inherited_mode(device, mode, changes) ⇒ Object



119
120
121
122
123
124
125
126
# File 'lib/osctld/devices/container_manager.rb', line 119

def update_inherited_mode(device, mode, changes)
  sync do
    abs_all_cgroup_paths.each do |cgpath, req|
      next unless prepare_cgroup(cgpath, req)
      do_apply_changes(changes, path: cgpath)
    end
  end
end