Class: OsCtld::Group

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::File, Assets::Definition, Lockable, Manipulable
Defined in:
lib/osctld/group.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Assets::Definition

#define_assets

Methods included from Manipulable

#acquire_manipulation_lock, #init_manipulable, #is_being_manipulated?, #manipulate, #manipulated_by, #release_manipulation_lock

Methods included from Lockable

#exclusively, included, #inclusively, #init_lock, #lock, #unlock

Constructor Details

#initialize(pool, name, load: true, config: nil, devices: true, root: false) ⇒ Group

Returns a new instance of Group.



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/osctld/group.rb', line 15

def initialize(pool, name, load: true, config: nil, devices: true, root: false)
  init_lock
  init_manipulable
  @pool = pool
  @name = name
  @root = root
  @cgparams = nil
  @devices = nil
  @attrs = Attributes.new
  load_config(config) if load
  devices.init if load && devices
end

Instance Attribute Details

#attrsObject (readonly)

Returns the value of attribute attrs.



13
14
15
# File 'lib/osctld/group.rb', line 13

def attrs
  @attrs
end

#cgparamsObject (readonly)

Returns the value of attribute cgparams.



13
14
15
# File 'lib/osctld/group.rb', line 13

def cgparams
  @cgparams
end

#devicesObject (readonly)

Returns the value of attribute devices.



13
14
15
# File 'lib/osctld/group.rb', line 13

def devices
  @devices
end

#nameObject (readonly)

Returns the value of attribute name.



13
14
15
# File 'lib/osctld/group.rb', line 13

def name
  @name
end

#poolObject (readonly)

Returns the value of attribute pool.



13
14
15
# File 'lib/osctld/group.rb', line 13

def pool
  @pool
end

Instance Method Details

#abs_cgroup_path(subsystem) ⇒ Object



132
133
134
# File 'lib/osctld/group.rb', line 132

def abs_cgroup_path(subsystem)
  CGroup.abs_cgroup_path(subsystem, cgroup_path)
end

#abs_full_cgroup_path(subsystem, user) ⇒ Object



136
137
138
# File 'lib/osctld/group.rb', line 136

def abs_full_cgroup_path(subsystem, user)
  CGroup.abs_cgroup_path(subsystem, full_cgroup_path(user))
end

#any_container_running?Boolean

Return ‘true` if any container from this or any descendant group is running.

Returns:

  • (Boolean)


241
242
243
244
245
246
247
248
249
# File 'lib/osctld/group.rb', line 241

def any_container_running?
  groups = [self] + descendants

  DB::Containers.get.each do |ct|
    return true if ct.pool == pool && groups.include?(self) && ct.running?
  end

  false
end

#assetsObject



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/osctld/group.rb', line 52

def assets
  define_assets do |add|
    add.file(
      config_path,
      desc: "osctld's group config",
      user: 0,
      group: 0,
      mode: 0o400
    )

    users.each do |u|
      add.directory(
        userdir(u),
        desc: "LXC path for #{u.name}:#{name}",
        user: 0,
        group: u.ugid,
        mode: 0o751
      )
    end

    devices.assets(add)
  end
end

#cgroup_pathObject



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/osctld/group.rb', line 116

def cgroup_path
  if root?
    path

  else
    File.join(
      DB::Groups.root(pool).path,
      *name.split('/').drop(1).map { |v| "group.#{v}" }
    )
  end
end

#childrenArray<Group>

Return all groups that are direct descendants

Returns:



189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/osctld/group.rb', line 189

def children
  DB::Groups.get.select do |grp|
    next if grp.pool != pool || grp.name == name

    s = if root?
          '/'
        else
          "#{name}/"
        end

    grp.name.start_with?(s) && grp.name[s.size..].index('/').nil?
  end.sort! { |a, b| a.name <=> b.name }
end

#config_dirObject



108
109
110
# File 'lib/osctld/group.rb', line 108

def config_dir
  File.join(pool.conf_path, 'group', id)
end

#config_pathObject



112
113
114
# File 'lib/osctld/group.rb', line 112

def config_path
  File.join(pool.conf_path, 'group', id, 'config.yml')
end

#configure(path: nil, devices: true) ⇒ Object



44
45
46
47
48
49
50
# File 'lib/osctld/group.rb', line 44

def configure(path: nil, devices: true)
  @path = path if root?
  @cgparams = CGroup::Params.new(self)
  @devices = Devices::Manager.new_for(self)
  @devices.init if devices
  save_config
end

#containersObject



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/osctld/group.rb', line 227

def containers
  ret = []

  DB::Containers.get.each do |ct|
    next if ct.pool != pool || ct.group != self || ret.include?(ct)

    ret << ct
  end

  ret
end

#descendantsArray<Group>

Return all groups below the current group’s path

Returns:



205
206
207
208
209
210
211
212
213
214
# File 'lib/osctld/group.rb', line 205

def descendants
  groups = DB::Groups.get.select { |grp| grp.pool == pool }

  if root?
    groups.drop(1) # remove the root group, which is first

  else
    groups.select { |grp| grp.name.start_with?("#{name}/") }
  end.sort! { |a, b| a.name <=> b.name }
end

#exportObject



317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/osctld/group.rb', line 317

def export
  inclusively do
    {
      pool: pool.name,
      name:,
      path:,
      full_path: cgroup_path,
      cpu_limit: find_cpu_limit(parents: false),
      memory_limit: find_memory_limit(parents: false),
      swap_limit: find_swap_limit(parents: false)
    }
  end
end

#find_cpu_limit(parents: true) ⇒ Integer?

Returns CPU limit in percent (100 % for one CPU).

Returns:

  • (Integer, nil)

    CPU limit in percent (100 % for one CPU)



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/osctld/group.rb', line 300

def find_cpu_limit(parents: true)
  limit = cgparams.find_cpu_limit

  if limit
    return limit
  elsif !parents
    return
  end

  self.parents.each do |grp|
    grp_limit = grp.find_cpu_limit(parents: true)
    return grp_limit if grp_limit
  end

  nil
end

#find_memory_limit(parents: true) ⇒ Integer?

Returns memory limit in bytes.

Returns:

  • (Integer, nil)

    memory limit in bytes



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/osctld/group.rb', line 264

def find_memory_limit(parents: true)
  limit = cgparams.find_memory_limit

  if limit
    return limit
  elsif !parents
    return
  end

  self.parents.each do |grp|
    grp_limit = grp.find_memory_limit(parents: true)
    return grp_limit if grp_limit
  end

  nil
end

#find_swap_limit(parents: true) ⇒ Integer?

Returns swap limit in bytes.

Returns:

  • (Integer, nil)

    swap limit in bytes



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/osctld/group.rb', line 282

def find_swap_limit(parents: true)
  limit = cgparams.find_swap_limit

  if limit
    return limit
  elsif !parents
    return
  end

  self.parents.each do |grp|
    grp_limit = grp.find_swap_limit(parents: true)
    return grp_limit if grp_limit
  end

  nil
end

#full_cgroup_path(user) ⇒ Object



128
129
130
# File 'lib/osctld/group.rb', line 128

def full_cgroup_path(user)
  File.join(cgroup_path, "user.#{user.name}")
end

#groups_in_pathArray<Group>

Return all groups leading to this group’s path, i.e. all parents and the group itself.

Returns:



183
184
185
# File 'lib/osctld/group.rb', line 183

def groups_in_path
  parents + [self]
end

#has_containers?(user = nil) ⇒ Boolean

Parameters:

  • user (User, nil) (defaults to: nil)

Returns:

  • (Boolean)


217
218
219
220
221
222
223
224
225
# File 'lib/osctld/group.rb', line 217

def has_containers?(user = nil)
  any_ct = DB::Containers.get.detect do |ct|
    ct.pool.name == pool.name \
      && ct.group.name == name \
      && (user.nil? || ct.user.name == user.name)
  end

  any_ct ? true : false
end

#idObject



28
29
30
# File 'lib/osctld/group.rb', line 28

def id
  @name
end

#identObject



32
33
34
# File 'lib/osctld/group.rb', line 32

def ident
  inclusively { "#{pool.name}:#{id}" }
end

#load_config(config = nil) ⇒ Object (protected)



359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/osctld/group.rb', line 359

def load_config(config = nil)
  cfg = if config
          OsCtl::Lib::ConfigFile.load_yaml(config)
        else
          OsCtl::Lib::ConfigFile.load_yaml_file(config_path)
        end

  @path = cfg['path'] if root?
  @cgparams = CGroup::Params.load(self, cfg['cgparams'])
  @devices = Devices::Manager.load(self, cfg['devices'] || [])
  @attrs = Attributes.load(cfg['attrs'] || {})
end

#log_typeObject



331
332
333
# File 'lib/osctld/group.rb', line 331

def log_type
  "group=#{pool.name}:#{name}"
end

#manipulation_resourceObject



335
336
337
# File 'lib/osctld/group.rb', line 335

def manipulation_resource
  ['group', "#{pool.name}:#{name}"]
end

#parentGroup?

Return the closest parent group

Returns:



174
175
176
177
178
# File 'lib/osctld/group.rb', line 174

def parent
  return if root?

  parents.last
end

#parentsArray<Group>

Return all parent groups, from the root group to the closest parent

Returns:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/osctld/group.rb', line 154

def parents
  return [] if root?

  ret = []
  t = ''

  name.split('/')[0..-2].each do |n|
    t = File.join('/', t, n)

    g = DB::Groups.by_path(pool, t)
    raise GroupNotFound, "group '#{t}' not found" if g.nil?

    ret << g
  end

  ret
end

#pathObject



40
41
42
# File 'lib/osctld/group.rb', line 40

def path
  root? ? @path : File.join(DB::Groups.root(pool).path, name)
end

#root?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/osctld/group.rb', line 36

def root?
  @root
end

#save_configObject



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/osctld/group.rb', line 339

def save_config
  FileUtils.mkdir_p(config_dir)

  cfg = {
    'cgparams' => cgparams.dump,
    'devices' => devices.dump,
    'attrs' => attrs.dump
  }

  cfg['path'] = path if root?

  regenerate_file(config_path, 0o400) do |f|
    f.write(OsCtl::Lib::ConfigFile.dump_yaml(cfg))
  end

  File.chown(0, 0, config_path)
end

#set(opts) ⇒ Object

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :attrs (Hash)


78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/osctld/group.rb', line 78

def set(opts)
  opts.each do |k, v|
    case k
    when :attrs
      attrs.update(v)

    else
      raise "unsupported option '#{k}'"
    end
  end

  save_config
end

#setup_for?(user) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/osctld/group.rb', line 148

def setup_for?(user)
  Dir.exist?(userdir(user))
end

#unset(opts) ⇒ Object

Parameters:

  • opts (Hash)

Options Hash (opts):

  • :attrs (Array<String>)


94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/osctld/group.rb', line 94

def unset(opts)
  opts.each do |k, v|
    case k
    when :attrs
      v.each { |attr| attrs.unset(attr) }

    else
      raise "unsupported option '#{k}'"
    end
  end

  save_config
end

#userdir(user) ⇒ Object



140
141
142
143
144
145
146
# File 'lib/osctld/group.rb', line 140

def userdir(user)
  File.join(
    user.userdir,
    *name.split('/').drop(1).map { |v| "group.#{v}" },
    'cts'
  )
end

#usersObject



251
252
253
254
255
256
257
258
259
260
261
# File 'lib/osctld/group.rb', line 251

def users
  ret = []

  DB::Containers.get.each do |ct|
    next if ct.pool != pool || ct.group != self || ret.include?(ct.user)

    ret << ct.user
  end

  ret
end