cfa_grub2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 44d755734cf3e0bb54735aafb2eebe4ba46437eb
4
+ data.tar.gz: 875e4f91ca2d0b12f755fc2186ae7c4e007f9099
5
+ SHA512:
6
+ metadata.gz: 3f9ddf0fe7a58edaa0a5f60a6a39a2bbadeed15f260a131d3f4d77754503cbf21248c2c96f3084fc20ac8e4126e5413f34a5d3ada6e6b6e72ca22a0fd293b367
7
+ data.tar.gz: 5e6bd382abf70b158c6b0b2ddcddd0f1d88273669cf7dbb7f0eb8aa4ef428197cfff816d76ac1d4dcc6ddb75f8f0a0dd70793fcc0f96efd202709c1890b659a7
@@ -0,0 +1,230 @@
1
+ require "cfa/base_model"
2
+ require "cfa/augeas_parser"
3
+ require "cfa/placer"
4
+ require "cfa/matcher"
5
+
6
+ module CFA
7
+ module Grub2
8
+ # Represents grub configuration in /etc/default/grub
9
+ # Main features:
10
+ #
11
+ # - Do not overwrite files
12
+ # - When setting value first try to just change value if key already exists
13
+ # - When key is not set, then try to find commented out line with key and
14
+ # replace it with real config
15
+ # - When even commented out code is not there, then append configuration
16
+ # to the end of file
17
+ class Default < BaseModel
18
+ attributes(
19
+ timeout: "GRUB_TIMEOUT",
20
+ distributor: "GRUB_DISTRIBUTOR",
21
+ gfxmode: "GRUB_GFXMODE",
22
+ theme: "GRUB_THEME"
23
+ )
24
+
25
+ PARSER = AugeasParser.new("sysconfig.lns")
26
+ PATH = "/etc/default/grub"
27
+
28
+ def initialize(file_handler: File)
29
+ super(PARSER, PATH, file_handler: file_handler)
30
+ end
31
+
32
+ def save(changes_only: false)
33
+ # serialize kernel params object before save
34
+ kernels = [@kernel_params, @xen_hypervisor_params, @xen_kernel_params,
35
+ @recovery_params]
36
+ kernels.each do |params|
37
+ # FIXME: this empty prevent writing explicit empty kernel params.
38
+ generic_set(params.key, params.serialize) if params && !params.empty?
39
+ end
40
+
41
+ super
42
+ end
43
+
44
+ def load
45
+ super
46
+
47
+ kernels = [kernel_params, xen_hypervisor_params, xen_kernel_params,
48
+ recovery_params]
49
+ kernels.each do |kernel|
50
+ param_line = data[kernel.key]
51
+ kernel.replace(param_line) if param_line
52
+ end
53
+ end
54
+
55
+ def os_prober
56
+ @os_prober ||= BooleanValue.new(
57
+ "GRUB_DISABLE_OS_PROBER", self,
58
+ # grub key is disable, so use reverse logic
59
+ true_value: "false", false_value: "true"
60
+ )
61
+ end
62
+
63
+ def kernel_params
64
+ @kernel_params ||= KernelParams.new(
65
+ data["GRUB_CMDLINE_LINUX_DEFAULT"], "GRUB_CMDLINE_LINUX_DEFAULT"
66
+ )
67
+ end
68
+
69
+ def xen_hypervisor_params
70
+ @xen_hypervisor_params ||= KernelParams.new(
71
+ data["GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT"],
72
+ "GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT"
73
+ )
74
+ end
75
+
76
+ def xen_kernel_params
77
+ @xen_kernel_params ||= KernelParams.new(
78
+ data["GRUB_CMDLINE_LINUX_XEN"], "GRUB_CMDLINE_LINUX_XEN"
79
+ )
80
+ end
81
+
82
+ def recovery_params
83
+ @recovery_params ||= KernelParams.new(
84
+ data["GRUB_CMDLINE_LINUX_RECOVERY"], "GRUB_CMDLINE_LINUX_RECOVERY"
85
+ )
86
+ end
87
+
88
+ def recovery_entry
89
+ @recovery ||= BooleanValue.new(
90
+ "GRUB_DISABLE_RECOVERY", self,
91
+ # grub key is disable, so use reverse logic
92
+ true_value: "false", false_value: "true"
93
+ )
94
+ end
95
+
96
+ def cryptodisk
97
+ @cryptodisk ||= BooleanValue.new("GRUB_ENABLE_CRYPTODISK", self)
98
+ end
99
+
100
+ def terminal
101
+ case data["GRUB_TERMINAL"]
102
+ when "", nil then nil
103
+ when "console" then :console
104
+ when "serial" then :serial
105
+ when "gfxterm" then :gfxterm
106
+ else
107
+ raise "unknown GRUB_TERMINAL option #{data["GRUB_TERMINAL"].inspect}"
108
+ end
109
+ end
110
+
111
+ VALID_TERMINAL_OPTIONS = [:serial, :console, :gfxterm]
112
+ def terminal=(value)
113
+ if !VALID_TERMINAL_OPTIONS.include?(value)
114
+ raise ArgumentError, "invalid value #{value.inspect}"
115
+ end
116
+
117
+ generic_set("GRUB_TERMINAL", value.to_s)
118
+ end
119
+
120
+ def serial_console=(value)
121
+ self.terminal = :serial
122
+ generic_set("GRUB_SERIAL_COMMAND", value)
123
+ end
124
+
125
+ def serial_console
126
+ data["GRUB_SERIAL_COMMAND"]
127
+ end
128
+
129
+ # Represents kernel append line with helpers to easier modification.
130
+ # TODO: handle quoting, maybe have own lense to parse/serialize kernel
131
+ # params?
132
+ class KernelParams
133
+ attr_reader :key
134
+
135
+ def initialize(line, key)
136
+ @tree = ParamTree.new(line)
137
+ @key = key
138
+ end
139
+
140
+ def serialize
141
+ @tree.to_string
142
+ end
143
+
144
+ # replaces kernel params with passed line
145
+ def replace(line)
146
+ @tree = ParamTree.new(line)
147
+ end
148
+
149
+ # checks if there is any parameter
150
+ def empty?
151
+ serialize.empty?
152
+ end
153
+
154
+ # gets value for parameters.
155
+ # @return possible values are `false` when parameter missing,
156
+ # `true` when parameter without value placed, string if single
157
+ # instance with value is there and array if multiple instance with
158
+ # values are there.
159
+ #
160
+ # @example different values
161
+ # line = "quite console=S0 console=S1 vga=0x400"
162
+ # params = KernelParams.new(line)
163
+ # params.parameter("quite") # => true
164
+ # params.parameter("verbose") # => false
165
+ # params.parameter("vga") # => "0x400"
166
+ # params.parameter("console") # => ["S0", "S1"]
167
+ #
168
+ def parameter(key)
169
+ values = @tree.data
170
+ .select { |e| e[:key] == key }
171
+ .map { |e| e[:value] }
172
+
173
+ return false if values.empty?
174
+ return values if values.size > 1
175
+ return true if values.first == true
176
+
177
+ values.first
178
+ end
179
+
180
+ # Adds new parameter to kernel command line. Uses augeas placers.
181
+ # To replace value use {ReplacePlacer}
182
+ def add_parameter(key, value, placer = AppendPlacer.new)
183
+ element = placer.new_element(@tree)
184
+
185
+ element[:key] = key
186
+ element[:value] = value
187
+ end
188
+
189
+ # Removes parameter from kernel command line.
190
+ # @param matcher [Matcher] to find entry to remove
191
+ def remove_parameter(matcher)
192
+ @tree.data.reject!(&matcher)
193
+ end
194
+
195
+ # Represents parsed kernel parameters tree. Parses in initialization
196
+ # and backserilized by `to_string`.
197
+ # TODO: replace it via augeas parser when someone write lense
198
+ class ParamTree
199
+ attr_reader :data
200
+
201
+ def initialize(line)
202
+ line ||= ""
203
+ pairs = line.split(/\s/)
204
+ .reject(&:empty?)
205
+ .map { |e| e.split("=", 2) }
206
+
207
+ @data = pairs.map do |k, v|
208
+ {
209
+ key: k,
210
+ value: v || true, # kernel param without value have true
211
+ }
212
+ end
213
+ end
214
+
215
+ def to_string
216
+ snippets = @data.map do |e|
217
+ if e[:value] == true
218
+ e[:key]
219
+ else
220
+ "#{e[:key]}=#{e[:value]}"
221
+ end
222
+ end
223
+
224
+ snippets.join(" ")
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,67 @@
1
+ require "cfa/base_model"
2
+ require "cfa/augeas_parser"
3
+ require "cfa/placer"
4
+ require "cfa/matcher"
5
+
6
+ module CFA
7
+ module Grub2
8
+ # Represents grub device map in /etc/grub2/device_map
9
+ # for details see https://www.gnu.org/software/grub/manual/html_node/Device-map.html
10
+ # Main features:
11
+ #
12
+ # - Do not overwrite files
13
+ # - When setting value first try to just change value if key already exists
14
+ # - When grub key is not there, then add to file
15
+ # - checks and raise exception if number of mappings exceed limit 8.
16
+ # Limitation is caused by BIOS Int 13 used by grub2 for selecting boot
17
+ # device.
18
+ class DeviceMap < BaseModel
19
+ PARSER = AugeasParser.new("device_map.lns")
20
+ PATH = "/etc/grub2/device.map"
21
+
22
+ def initialize(file_handler: File)
23
+ super(PARSER, PATH, file_handler: file_handler)
24
+ end
25
+
26
+ def save(changes_only: false)
27
+ raise "Too much grub devices. Limit is 8." if grub_devices.size > 8
28
+
29
+ super
30
+ end
31
+
32
+ # @return [String] grub device name for given system device
33
+ def grub_device_for(system_dev)
34
+ matcher = Matcher.new(value_matcher: system_dev)
35
+ entry = data.select(matcher)
36
+
37
+ entry.empty? ? nil : entry.first[:key]
38
+ end
39
+
40
+ # @return [String] system device name for given grub device
41
+ def system_device_for(grub_device)
42
+ data[grub_device]
43
+ end
44
+
45
+ # Appends to configuration mapping between grub_device and system_device
46
+ # @note if mapping for given grub device is already defined, it will be
47
+ # overwritten
48
+ def add_mapping(grub_device, system_device)
49
+ generic_set(grub_device, system_device)
50
+ end
51
+
52
+ # Removes mapping for given grub device
53
+ def remove_mapping(grub_device)
54
+ data.delete(grub_device)
55
+ end
56
+
57
+ # @return [Array<String>] list of all grub devices which have mapping.
58
+ # If there is no mapping, then it return empty list.
59
+ def grub_devices
60
+ matcher = Matcher.new { |k, _v| k !~ /comment/ }
61
+ entries = data.select(matcher)
62
+
63
+ entries.map { |e| e[:key] }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,39 @@
1
+ require "cfa/base_model"
2
+
3
+ module CFA
4
+ module Grub2
5
+ # Represents generated grub configuration at /boot/grub2/grub.cfg
6
+ # Main features:
7
+ #
8
+ # - List of generated sections including translations
9
+ class GrubCfg < BaseModel
10
+ PATH = "/boot/grub2/grub.cfg"
11
+
12
+ # @private only internal parser
13
+ class Parser
14
+ def self.parse(string)
15
+ menu_lines = string.lines.grep(/menuentry\s*'/)
16
+ menu_lines.map { |line| line[/\s*menuentry\s*'([^']+)'.*/, 1] }
17
+ end
18
+
19
+ def self.serialize(_string)
20
+ raise NotImplementedError,
21
+ "Serializing not implemented, use grub2 generator"
22
+ end
23
+
24
+ def self.empty
25
+ []
26
+ end
27
+ end
28
+
29
+ def initialize(file_handler: File)
30
+ super(Parser, PATH, file_handler: file_handler)
31
+ end
32
+
33
+ # @return [Array<String>] sections from grub.cfg in order as they appear
34
+ def sections
35
+ data
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,94 @@
1
+ require "cfa/base_model"
2
+
3
+ module CFA
4
+ module Grub2
5
+ # specific parser for install devices.
6
+ # File format is easy element per line without comments.
7
+ # for better readability special values generic_mbr and activate is at
8
+ # the end of file
9
+ module InstallDeviceParser
10
+ # returns list of non-empty lines
11
+ def self.parse(string)
12
+ string.lines.map(&:strip).delete_if(&:empty?)
13
+ end
14
+
15
+ # gets list of devices and create file content from it
16
+ def self.serialize(data)
17
+ activate = data.delete("activate")
18
+ generic_mbr = data.delete("generic_mbr")
19
+
20
+ res = data.join("\n")
21
+ res << "\n" unless res.empty?
22
+
23
+ res << "activate\n" if activate
24
+ res << "generic_mbr\n" if generic_mbr
25
+
26
+ res
27
+ end
28
+
29
+ def self.empty
30
+ []
31
+ end
32
+ end
33
+
34
+ # Model representing configuration in file /etc/default/grub_installdevice
35
+ class InstallDevice < BaseModel
36
+ PATH = "/etc/default/grub_installdevice"
37
+
38
+ def initialize(file_handler: File)
39
+ super(InstallDeviceParser, PATH, file_handler: file_handler)
40
+ end
41
+
42
+ # Adds new install device. Does nothing if it is already there.
43
+ def add_device(dev)
44
+ data << dev unless data.include?(dev)
45
+ end
46
+
47
+ # Removes install device. Does nothing if already not there.
48
+ def remove_device(dev)
49
+ data.delete(dev)
50
+ end
51
+
52
+ # @return [Array<String>] non-special devices from configuration
53
+ def devices
54
+ res = data.dup
55
+ res.delete("generic_mbr")
56
+ res.delete("activate")
57
+
58
+ res
59
+ end
60
+
61
+ # ask if special entry for generic_mbr is there
62
+ def generic_mbr?
63
+ data.include?("generic_mbr")
64
+ end
65
+
66
+ # sets special entry generic_mbr
67
+ def generic_mbr=(enabled)
68
+ if enabled
69
+ return if generic_mbr?
70
+
71
+ data << "generic_mbr"
72
+ else
73
+ data.delete("generic_mbr")
74
+ end
75
+ end
76
+
77
+ # Ask if special entry for activate is there
78
+ def activate?
79
+ data.include?("activate")
80
+ end
81
+
82
+ # sets special entry activate
83
+ def activate=(enabled)
84
+ if enabled
85
+ return if activate?
86
+
87
+ data << "activate"
88
+ else
89
+ data.delete("activate")
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cfa_grub2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josef Reidinger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cfa ~> 0.2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Models allowing easy read and modification of GRUB2 configuration files.
28
+ It is a plugin for cfa framework.
29
+ email:
30
+ - jreidinger@suse.cz
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/cfa/grub2/default.rb
36
+ - lib/cfa/grub2/device_map.rb
37
+ - lib/cfa/grub2/grub_cfg.rb
38
+ - lib/cfa/grub2/install_device.rb
39
+ homepage: http://github.com/config-files-api/config_files_api_grub2
40
+ licenses: []
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 1.3.6
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.2.2
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Models for GRUB2 configuration files.
62
+ test_files: []
63
+ has_rdoc: