Class: Y2Network::UdevRule

Inherits:
Object
  • Object
show all
Defined in:
src/lib/y2network/udev_rule.rb

Overview

Simple udev rule class

This class represents a network udev rule. The current implementation is quite simplistic, featuring a an API which is tailored to our needs.

Basically, udev rules are kept in two different files under /etc/udev/rules.d:

  • 70-persistent-net.rules ('net' group): rules to assign names to interfaces.
  • 79-yast2-drivers.rules ('drivers' group): rules to assign drivers to interfaces.

This class offers a set of constructors to build different kinds of rules. See UdevRule.new_mac_based_rename, UdevRule.new_bus_id_based_rename and UdevRule.new_driver_assignment.

When it comes to write rules to the filesystem, we decided to offer different methods to write to each file. See UdevRule.write_net_rules and UdevRule.write_drivers_rules.

Examples:

Create a rule containing some key/value pairs (rule part)

rule = Y2Network::UdevRule.new(
  Y2Network::UdevRulePart.new("ATTR{address}", "==", "?*31:78:f2"),
  Y2Network::UdevRulePart.new("NAME", "=", "mlx4_ib3")
)
rule.to_s #=> "ACTION==\"add\", SUBSYSTEM==\"net\", ATTR{address}==\"?*31:78:f2\",
               NAME=\"eth0\""

Create a rule from a string

rule = UdevRule.find_for("eth0")
rule.to_s #=> "ACTION==\"add\", SUBSYSTEM==\"net\", ATTR{address}==\"?*31:78:f2\",
               NAME=\"eth0\""

Writing renaming rules

rule = UdevRule.new_mac_based_rename("00:12:34:56:78:ab", "eth0")
UdevRule.write_net_rules([rule])

Writing driver assignment rules

rule = UdevRule.new_driver_assignment("virtio:d00000001v00001AF4", "virtio_net")
UdevRule.write_drivers_rules([rule])

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parts = []) ⇒ UdevRule

Constructor

Parameters:

  • parts (Array<UdevRulePart>) (defaults to: [])

    udev rule parts



210
211
212
# File 'src/lib/y2network/udev_rule.rb', line 210

def initialize(parts = [])
  @parts = parts
end

Instance Attribute Details

#partsArray<UdevRulePart> (readonly)

Returns Parts of the udev rule.

Returns:



205
206
207
# File 'src/lib/y2network/udev_rule.rb', line 205

def parts
  @parts
end

Class Method Details

.allArray<UdevRule>

Returns all persistent network rules

Returns:

  • (Array<UdevRule>)

    Persistent network rules



65
66
67
# File 'src/lib/y2network/udev_rule.rb', line 65

def all
  naming_rules + drivers_rules
end

.drivers_rulesArray<UdevRule>

Returns driver rules

Returns:



79
80
81
# File 'src/lib/y2network/udev_rule.rb', line 79

def drivers_rules
  find_rules(:drivers)
end

.find_for(device) ⇒ UdevRule

Returns the udev rule for a given device

Only the naming rules are considered.

Parameters:

  • device (String)

    Network device name

Returns:



89
90
91
# File 'src/lib/y2network/udev_rule.rb', line 89

def find_for(device)
  naming_rules.find { |r| r.device == device }
end

.naming_rulesArray<UdevRule>

Returns naming rules

Returns:

  • (Array<UdevRule>)

    Naming network rules



72
73
74
# File 'src/lib/y2network/udev_rule.rb', line 72

def naming_rules
  find_rules(:net)
end

.new_bus_id_based_rename(name, bus_id, dev_port = nil) ⇒ Object

Helper method to create a rename rule based on the BUS ID

Parameters:

  • name (String)

    Interface's name

  • bus_id (String)

    BUS ID (e.g., "0000:08:00.0")

  • dev_port (String) (defaults to: nil)

    Device port



117
118
119
120
121
122
# File 'src/lib/y2network/udev_rule.rb', line 117

def new_bus_id_based_rename(name, bus_id, dev_port = nil)
  parts = [UdevRulePart.new("KERNELS", "==", bus_id)]
  parts << UdevRulePart.new("ATTR{dev_port}", "==", dev_port) if dev_port
  parts << UdevRulePart.new("NAME", "=", name)
  new_network_rule(parts)
end

.new_driver_assignment(modalias, driver_name) ⇒ UdevRule

Returns a module assignment rule

Parameters:

  • modalias (String)

    Interface's modalias

  • driver_name (String)

    Module name

Returns:



148
149
150
151
152
153
154
# File 'src/lib/y2network/udev_rule.rb', line 148

def new_driver_assignment(modalias, driver_name)
  parts = [
    UdevRulePart.new("ENV{MODALIAS}", "==", modalias),
    UdevRulePart.new("ENV{MODALIAS}", "=", driver_name)
  ]
  new(parts)
end

.new_mac_based_rename(name, mac) ⇒ Object

Helper method to create a rename rule based on a MAC address

Parameters:

  • name (String)

    Interface's name

  • mac (String)

    MAC address



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'src/lib/y2network/udev_rule.rb', line 97

def new_mac_based_rename(name, mac)
  new_network_rule(
    [
      # Guard to not try to rename everything with the same MAC address (e.g. vlan devices
      # inherit the MAC address from the underlying device).
      # FIXME: it won't work when using predictable network names (openSUSE)
      # UdevRulePart.new("KERNEL", "==", "eth*"),
      # The port number of a NIC where the ports share the same hardware device.
      UdevRulePart.new("ATTR{dev_id}", "==", "0x0"),
      UdevRulePart.new("ATTR{address}", "==", mac),
      UdevRulePart.new("NAME", "=", name)
    ]
  )
end

.new_network_rule(parts = []) ⇒ UdevRule

Returns a network rule

The network rule includes some parts by default.

Parameters:

  • parts (Array<UdevRulePart] Additional rule parts) (defaults to: [])

    arts [Array<UdevRulePart] Additional rule parts

Returns:



130
131
132
133
134
135
136
137
138
139
140
141
# File 'src/lib/y2network/udev_rule.rb', line 130

def new_network_rule(parts = [])
  base_parts = [
    UdevRulePart.new("SUBSYSTEM", "==", "net"),
    UdevRulePart.new("ACTION", "==", "add"),
    UdevRulePart.new("DRIVERS", "==", "?*"),
    # Ethernet devices
    # https://github.com/torvalds/linux/blob/bb7ba8069de933d69cb45dd0a5806b61033796a3/include/uapi/linux/if_arp.h#L31
    # TODO: what about InfiniBand (it is type 32)?
    UdevRulePart.new("ATTR{type}", "==", "1")
  ]
  new(base_parts.concat(parts))
end

.reset_cacheObject

Clears rules cache map



185
186
187
# File 'src/lib/y2network/udev_rule.rb', line 185

def reset_cache
  @all = nil
end

.write_drivers_rules(udev_rules) ⇒ Object

Writes drivers specific udev rules to the filesystem

Those rules that does not have an MODALIAS part will be ignored.

Parameters:

  • udev_rules (Array<UdevRule>)

    List of udev rules



171
172
173
174
175
176
177
178
179
180
181
182
# File 'src/lib/y2network/udev_rule.rb', line 171

def write_drivers_rules(udev_rules)
  rules_hash = udev_rules.each_with_object({}) do |rule, hash|
    driver = rule.driver
    next unless driver

    hash[driver] = rule.parts.map(&:to_s)
  end
  Yast::SCR.Write(Yast::Path.new(".udev_persistent.drivers"), rules_hash)
  # Writes changes to the rules file
  Yast::SCR.Write(Yast::Path.new(".udev_persistent.nil"), [])
  Yast::SCR.UnmountAgent(Yast::Path.new(".udev_persistent"))
end

.write_net_rules(udev_rules) ⇒ Object

Writes udev rules to the filesystem

Parameters:

  • udev_rules (Array<UdevRule>)

    List of udev rules



159
160
161
162
163
164
# File 'src/lib/y2network/udev_rule.rb', line 159

def write_net_rules(udev_rules)
  Yast::SCR.Write(Yast::Path.new(".udev_persistent.rules"), udev_rules.map(&:to_s))
  # Writes changes to the rules file
  Yast::SCR.Write(Yast::Path.new(".udev_persistent.nil"), [])
  Yast::SCR.UnmountAgent(Yast::Path.new(".udev_persistent"))
end

Instance Method Details

#add_part(key, operator, value) ⇒ Object

Adds a part to the rule

Parameters:

  • key (String)

    Key name

  • operator (String)

    Operator

  • value (String)

    Value to match or assign



219
220
221
# File 'src/lib/y2network/udev_rule.rb', line 219

def add_part(key, operator, value)
  @parts << UdevRulePart.new(key, operator, value)
end

#bus_idString?

Returns the BUS ID in the udev rule

Returns:

  • (String, nil)

    BUS ID or nil if not found

See Also:



305
306
307
# File 'src/lib/y2network/udev_rule.rb', line 305

def bus_id
  part_value_for("KERNELS")
end

#dev_portString?

Returns the device port in the udev rule

Returns:

  • (String, nil)

    Device port or nil if not found

See Also:



313
314
315
# File 'src/lib/y2network/udev_rule.rb', line 313

def dev_port
  part_value_for("ATTR{dev_port}")
end

#deviceString?

Returns the device mentioned in the rule (if any)

Returns:

  • (String, nil)

    Device name or nil if not found



320
321
322
# File 'src/lib/y2network/udev_rule.rb', line 320

def device
  part_value_for("NAME", "=")
end

#driverString?

Returns the modalias

Returns:

  • (String, nil)

    Original modalias or nil if not found



334
335
336
# File 'src/lib/y2network/udev_rule.rb', line 334

def driver
  part_value_for("ENV{MODALIAS}", "=")
end

#driversString?

Returns the drivers mentioned in the rule (if any)

Returns:

  • (String, nil)

    drivers or nil if not found



341
342
343
# File 'src/lib/y2network/udev_rule.rb', line 341

def drivers
  part_value_for("DRIVERS", "==")
end

#macString?

Returns the MAC in the udev rule

Returns:

  • (String, nil)

    MAC address or nil if not found

See Also:



254
255
256
# File 'src/lib/y2network/udev_rule.rb', line 254

def mac
  part_value_for("ATTR{address}")
end

#original_modaliasString?

Returns the original modalias

Returns:

  • (String, nil)

    Original modalias or nil if not found



327
328
329
# File 'src/lib/y2network/udev_rule.rb', line 327

def original_modalias
  part_value_for("ENV{MODALIAS}", "==")
end

#part_by_key(key, operator = nil) ⇒ Object

Returns the part with the given key

Parameters:

  • key (String)

    Key name to match

  • operator (String, nil) (defaults to: nil)

    Operator to match; nil omits matching the operator



234
235
236
# File 'src/lib/y2network/udev_rule.rb', line 234

def part_by_key(key, operator = nil)
  parts.find { |p| p.key == key && (operator.nil? || p.operator == operator) }
end

#part_value_for(key, operator = nil) ⇒ String?

Returns the value for a given part

Parameters:

  • key (String)

    Key name

  • operator (String, nil) (defaults to: nil)

    Operator to match; nil omits matching the operator

Returns:

  • (String, nil)

    Value or nil if not found a part which such a key



243
244
245
246
247
248
# File 'src/lib/y2network/udev_rule.rb', line 243

def part_value_for(key, operator = nil)
  part = part_by_key(key, operator)
  return nil unless part

  part.value
end

#rename_by_bus_id(name, bus_id_value, dev_port_value = nil) ⇒ Object

Convenience method which takes care of modifing the udev rule using the bus_id and the dev_port when needed as the naming mechanism



289
290
291
292
293
294
295
296
297
298
299
# File 'src/lib/y2network/udev_rule.rb', line 289

def rename_by_bus_id(name, bus_id_value, dev_port_value = nil)
  parts.delete_if { |p| (p.dev_port? && dev_port_value.nil?) }
  part = part_by_key("ATTR{address}")
  part.key = "KERNELS" if part

  replace_part("KERNELS", "==", bus_id_value) if bus_id != bus_id_value
  replace_part("ATTR{dev_port}", "==", dev_port_value) if dev_port != dev_port_value
  ## Ensure the name is always at the end of the rule
  parts.delete_if(&:name?)
  add_part("NAME", "=", name)
end

#rename_by_mac(name, address) ⇒ Object

Convenience method which takes care of modifing the udev rule using the MAC address as the naming mechanism



276
277
278
279
280
281
282
283
284
285
# File 'src/lib/y2network/udev_rule.rb', line 276

def rename_by_mac(name, address)
  parts.delete_if(&:dev_port?)
  part = part_by_key("KERNELS")
  part.key = "ATTR{address}" if part

  replace_part("ATTR{address}", "==", address) if mac != address
  ## Ensure the name is always at the end of the rule
  parts.delete_if { |p| p.dev_port? || p.name? }
  add_part("NAME", "=", name)
end

#replace_part(key, operator, value) ⇒ Object

Convenience method to replace a specific part by another one. In case that there is no part to be replaced then a new part is added.

Parameters:

  • key (String)

    Key name

  • operator (String)

    Operator

  • value (String)

    Value to match or assign

See Also:



265
266
267
268
269
270
271
272
# File 'src/lib/y2network/udev_rule.rb', line 265

def replace_part(key, operator, value)
  part = part_by_key(key, operator)
  if part
    part.value = value
  else
    add_part(key, operator, value)
  end
end

#to_sString

Returns an string representation that can be used in a rules file

Returns:

  • (String)


226
227
228
# File 'src/lib/y2network/udev_rule.rb', line 226

def to_s
  parts.map(&:to_s).join(", ")
end