ConfigLMM 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,256 @@
1
+
2
+ require 'addressable/uri'
3
+ require 'http'
4
+ require 'securerandom'
5
+ require 'shellwords'
6
+
7
+ module ConfigLMM
8
+ module LMM
9
+ class Linux < Framework::LinuxApp
10
+
11
+ ISO_LOCATION = '~/.cache/configlmm/images/'
12
+ HOSTS_FILE = '/etc/hosts'
13
+ SSH_CONFIG = '~/.ssh/config'
14
+ SYSCTL_FILE = '/etc/sysctl.d/10-configlmm.conf'
15
+
16
+ def actionLinuxBuild(id, target, activeState, context, options)
17
+ prepareConfig(target)
18
+ buildHostsFile(id, target, options)
19
+ buildSSHConfig(id, target, options)
20
+ buildAutoYaST(id, target, options)
21
+ end
22
+
23
+ def actionLinuxDeploy(id, target, activeState, context, options)
24
+ prepareConfig(target)
25
+ if target['Location'] && target['Location'] != '@me'
26
+ uri = Addressable::URI.parse(target['Location'])
27
+ case uri.scheme
28
+ when 'qemu'
29
+ deployOverLibvirt(id, target, activeState, context, options)
30
+ when 'ssh'
31
+ deployOverSSH(uri, id, target, activeState, context, options)
32
+ else
33
+ raise Framework::PluginProcessError.new("#{id}: Unknown protocol: #{uri.scheme}!")
34
+ end
35
+ else
36
+ deployLocalHostsFile(target, options)
37
+ deployLocalSSHConfig(target, options)
38
+ end
39
+ if target['AlternativeLocation']
40
+ uri = Addressable::URI.parse(target['AlternativeLocation'])
41
+ raise Framework::PluginProcessError.new("#{id}: Unsupported protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
42
+ deployOverSSH(uri, id, target, activeState, context, options)
43
+ end
44
+ end
45
+
46
+ def deployOverSSH(locationUri, id, target, activeState, context, options)
47
+ if target['Domain'] || target['Hosts']
48
+ hostsLines = []
49
+ if target['Domain']
50
+ self.class.sshStart(locationUri) do |ssh|
51
+ envs = self.class.sshExec!(ssh, "env").split("\n")
52
+ envVars = Hash[envs.map { |vars| vars.split('=', 2) }]
53
+ ipAddr = envVars['SSH_CONNECTION'].split[-2]
54
+ hostsLines << ipAddr.ljust(16) + target['Domain'] + ' ' + target['Name'] + "\n"
55
+ end
56
+ end
57
+ target['Hosts'].to_a.each do |ip, entries|
58
+ hostsLines << ip.ljust(16) + entries.join(' ') + "\n"
59
+ end
60
+ updateRemoteFile(locationUri, HOSTS_FILE, options, false) do |fileLines|
61
+ fileLines + hostsLines
62
+ end
63
+ end
64
+ if target['Sysctl']
65
+ updateRemoteFile(locationUri, SYSCTL_FILE, options, false) do |fileLines|
66
+ target['Sysctl'].each do |name, value|
67
+ fileLines << "#{name} = #{value}\n"
68
+ end
69
+ fileLines
70
+ end
71
+ end
72
+ end
73
+
74
+ def deployOverLibvirt(id, target, activeState, context, options)
75
+ location = Libvirt.getLocation(target['Location'])
76
+ iso = installationISO(target['Distro'], location)
77
+ iso = buildISOAutoYaST(id, iso, target, options) if target['Distro'] == SUSE_NAME
78
+ plugins[:Libvirt].createVM(target['Name'], target, target['Location'], iso, activeState)
79
+ end
80
+
81
+ def buildHostsFile(id, target, options)
82
+ if target['Hosts']
83
+ hosts = "#\n"
84
+ hosts += "# /etc/hosts: static lookup table for host names\n"
85
+ hosts += "#\n\n"
86
+ hosts += "#<ip-address> <hostname.domain.org> <hostname>\n"
87
+ hosts += "127.0.0.1 localhost\n"
88
+ hosts += "::1 localhost\n\n"
89
+ hosts += CONFIGLMM_SECTION_BEGIN
90
+ target['Hosts'].each do |ip, entries|
91
+ hosts += ip.ljust(16) + entries.join(' ') + "\n"
92
+ end
93
+ hosts += CONFIGLMM_SECTION_END
94
+
95
+ path = options['output'] + '/' + id
96
+ mkdir(path + '/etc', options[:dry])
97
+ fileWrite(path + HOSTS_FILE, hosts, options[:dry])
98
+ end
99
+ end
100
+
101
+ def buildSSHConfig(id, target, options)
102
+ if !target['SSH']['Config'].empty?
103
+ sshConfig = "\n"
104
+ sshConfig += CONFIGLMM_SECTION_BEGIN
105
+ target['SSH']['Config'].each do |name, info|
106
+ sshConfig += "Host #{name} #{info['HostName']}\n"
107
+ sshConfig += " HostName " + info['HostName'] + "\n" if info['HostName']
108
+ sshConfig += " Port " + info['Port'] + "\n" if info['Port']
109
+ sshConfig += " User " + info['User'] + "\n" if info['User']
110
+ sshConfig += " IdentityFile " + info['IdentityFile'] + "\n" if info['IdentityFile']
111
+ sshConfig += "\n"
112
+ end
113
+ sshConfig += CONFIGLMM_SECTION_END
114
+ sshConfig += "\n"
115
+
116
+ configPath = options['output'] + '/' + id
117
+ mkdir(configPath + '/root/.ssh', options[:dry])
118
+ fileWrite(configPath + SSH_CONFIG.gsub('~', '/root'), sshConfig, options[:dry])
119
+ end
120
+ end
121
+
122
+ def buildAutoYaST(id, target, options)
123
+ if target['Distro'] == SUSE_NAME
124
+ outputFolder = options['output'] + '/' + id + '/'
125
+ template = ERB.new(File.read(__dir__ + '/openSUSE/autoinst.xml.erb'))
126
+ renderTemplate(template, target, outputFolder + 'autoinst.xml', options)
127
+ end
128
+ end
129
+
130
+ def deployLocalHostsFile(target, options)
131
+ if target['Hosts']
132
+ updateLocalFile(HOSTS_FILE, options) do |hostsLines|
133
+ target['Hosts'].each do |ip, entries|
134
+ hostsLines << ip.ljust(16) + entries.join(' ') + "\n"
135
+ end
136
+ hostsLines
137
+ end
138
+ end
139
+ end
140
+
141
+ def deployLocalSSHConfig(target, options)
142
+ if !target['SSH']['Config'].empty?
143
+ updateLocalFile(File.expand_path(SSH_CONFIG), options) do |configLines|
144
+ target['SSH']['Config'].each do |name, info|
145
+ configLines << "Host #{name} #{info['HostName']}\n"
146
+ configLines << " HostName " + info['HostName'] + "\n" if info['HostName']
147
+ configLines << " Port " + info['Port'] + "\n" if info['Port']
148
+ configLines << " User " + info['User'] + "\n" if info['User']
149
+ configLines << " IdentityFile " + info['IdentityFile'] + "\n" if info['IdentityFile']
150
+ end
151
+ configLines
152
+ end
153
+ end
154
+ end
155
+
156
+ def installationISO(distro, location)
157
+ url = nil
158
+ case distro
159
+ when SUSE_NAME
160
+ if location.empty?
161
+ # TODO automatically fetch latest version from website
162
+ url = 'https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-NET-x86_64-Media.iso'
163
+ else
164
+ raise Framework::PluginProcessError.new("#{id}: Unimplemented!")
165
+ end
166
+ else
167
+ raise Framework::PluginProcessError.new("#{id}: Unknown Linux Distro: #{distro}!")
168
+ end
169
+
170
+ filename = File.basename(Addressable::URI.parse(url).path)
171
+ iso = File.expand_path(ISO_LOCATION + filename)
172
+ if !File.exist?(iso)
173
+ mkdir(File.expand_path(ISO_LOCATION), false)
174
+ prompt.say('Downloading... ' + url)
175
+ response = HTTP.follow.get(url)
176
+ raise "Failed to download file: #{response.status}" unless response.status.success?
177
+ File.open(iso, 'wb') do |file|
178
+ response.body.each do |chunk|
179
+ file.write(chunk)
180
+ end
181
+ end
182
+ end
183
+ iso
184
+ end
185
+
186
+ def buildISOAutoYaST(id, iso, target, options)
187
+ outputFolder = options['output'] + '/iso/'
188
+ mkdir(outputFolder, false)
189
+ `xorriso -osirrox on -indev #{iso} -extract / #{outputFolder} 2>&1 >/dev/null`
190
+ FileUtils.chmod_R(0750, outputFolder) # Need to make it writeable so it can be deleted
191
+ copy(options['output'] + '/' + id + '/autoinst.xml', outputFolder, false)
192
+
193
+ cfg = outputFolder + "boot/x86_64/loader/isolinux.cfg"
194
+ `sed -i 's|default harddisk|default linux|' #{cfg}`
195
+ `sed -i 's|append initrd=initrd splash=silent showopts|append initrd=initrd splash=silent autoyast=device://sr0/autoinst.xml|' #{cfg}`
196
+ `sed -i 's|prompt 1|prompt 0|' #{cfg}`
197
+ `sed -i 's|timeout 600|timeout 1|' #{cfg}`
198
+
199
+ patchedIso = File.dirname(iso) + '/patched.iso'
200
+ `xorriso -as mkisofs -no-emul-boot -boot-load-size 4 -boot-info-table -iso-level 4 -b boot/x86_64/loader/isolinux.bin -c boot/x86_64/loader/boot.cat -eltorito-alt-boot -e boot/x86_64/efi -no-emul-boot -o #{patchedIso} #{outputFolder} 2>&1 >/dev/null`
201
+ patchedIso
202
+ end
203
+
204
+ def prepareConfig(target)
205
+ target['SSH'] ||= {}
206
+ target['SSH']['Config'] ||= {}
207
+ target['Users'] ||= {}
208
+ target['HostName'] = target['Name'] unless target['HostName']
209
+
210
+ if ENV['LINUX_ROOT_PASSWORD_HASH']
211
+ target['Users']['root'] ||= {}
212
+ target['Users']['root']['PasswordHash'] = ENV['LINUX_ROOT_PASSWORD_HASH']
213
+ elsif ENV['LINUX_ROOT_PASSWORD']
214
+ target['Users']['root'] ||= {}
215
+ target['Users']['root']['PasswordHash'] = self.class.linuxPasswordHash(ENV['LINUX_ROOT_PASSWORD'])
216
+ elsif target['Users'].key?('root')
217
+ if !target['Users']['root']['Password'] &&
218
+ !target['Users']['root']['PasswordHash']
219
+ rootPassword = SecureRandom.urlsafe_base64(12)
220
+ prompt.say("Root password: #{rootPassword}", :color => :magenta)
221
+ target['Users']['root']['PasswordHash'] = self.class.linuxPasswordHash(rootPassword)
222
+ elsif target['Users']['root']['Password'] == 'no'
223
+ target['Users']['root'].delete('Password')
224
+ end
225
+ end
226
+
227
+ target['Users'].each do |user, info|
228
+ newKeys = []
229
+ info['AuthorizedKeys'].to_a.each do |key|
230
+ if key.start_with?('/') || key.start_with?('~')
231
+ newKeys << File.read(File.expand_path(key)).strip
232
+ else
233
+ newKeys << key
234
+ end
235
+ end
236
+ info['AuthorizedKeys'] = newKeys
237
+ end
238
+
239
+ packages = YAML.load_file(__dir__ + '/Packages.yaml')
240
+ newApps = []
241
+ target['Services'] ||= []
242
+ if target['Apps'].to_a.include?('sshd')
243
+ target['Services'] << 'sshd'
244
+ target['Services'].uniq!
245
+ end
246
+ target['Apps'] = self.class.mapPackages(target['Apps'], target['Distro'])
247
+ end
248
+
249
+ def self.linuxPasswordHash(password)
250
+ salt = SecureRandom.alphanumeric(16)
251
+ password.crypt('$6$' + salt)
252
+ end
253
+
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,13 @@
1
+ Arch Linux:
2
+ sshd: openssh
3
+ Postfix: postfix
4
+ Dovecot: dovecot
5
+ PostgreSQL: postgresql
6
+ Valkey: redis
7
+
8
+ openSUSE Leap:
9
+ sshd: openssh
10
+ Postfix: postfix
11
+ Dovecot: dovecot
12
+ PostgreSQL: postgresql-server
13
+ Valkey: redis
@@ -0,0 +1,87 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE profile>
3
+ <profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
4
+ <general config:type="map">
5
+ <mode config:type="map">
6
+ <confirm config:type="boolean">false</confirm>
7
+ </mode>
8
+ </general>
9
+ <bootloader t="map">
10
+ <global t="map">
11
+ <append>splash=silent preempt=full mitigations=auto quiet security=apparmor console=ttyS0,115200</append>
12
+ </global>
13
+ </bootloader>
14
+ <host t="map">
15
+ <% if !config['Hosts'].to_a.empty? %>
16
+ <hosts t="list">
17
+ <% config['Hosts'].each do |address, names| %>
18
+ <hosts_entry t="map">
19
+ <host_address><%= address %></host_address>
20
+ <names t="list">
21
+ <% names.each do |name| %>
22
+ <name><%= name %></name>
23
+ <% end %>
24
+ </names>
25
+ </hosts_entry>
26
+ <% end %>
27
+ </hosts>
28
+ <% end %>
29
+ </host>
30
+ <networking t="map">
31
+ <dns t="map">
32
+ <hostname><%= config['HostName'] %></hostname>
33
+ <% if config['Domain'] %>
34
+ <domain><%= config['Domain'] %></domain>
35
+ <% end %>
36
+ </dns>
37
+ </networking>
38
+ <software t="map">
39
+ <% if !config['Apps'].to_a.empty? %>
40
+ <packages t="list">
41
+ <% config['Apps'].each do |app| %>
42
+ <package><%= app.downcase %></package>
43
+ <% end %>
44
+ </packages>
45
+ <% end %>
46
+ </software>
47
+ <services-manager t="map">
48
+ <services t="map">
49
+ <% if !config['Services'].to_a.empty? %>
50
+ <enable t="list">
51
+ <% config['Services'].each do |service| %>
52
+ <service><%= service %></service>
53
+ <% end %>
54
+ </enable>
55
+ <% end %>
56
+ </services>
57
+ </services-manager>
58
+ <timezone t="map">
59
+ <timezone>Etc/UTC</timezone>
60
+ </timezone>
61
+ <% if !config['Users'].to_h.empty? %>
62
+ <users config:type="list">
63
+ <% config['Users'].each do |user, info| %>
64
+ <user>
65
+ <username>root</username>
66
+ <% if info['PasswordHash'] %>
67
+ <encrypted config:type="boolean">true</encrypted>
68
+ <user_password><%= info['PasswordHash'] %></user_password>
69
+ <% elsif info['Password'] %>
70
+ <encrypted config:type="boolean">false</encrypted>
71
+ <user_password><%= info['Password'] %></user_password>
72
+ <% end %>
73
+ <% if info['Shell'] %>
74
+ <shell>/usr/bin/<%= info['Shell'] %></shell>
75
+ <% end %>
76
+ <% if !info['AuthorizedKeys'].to_a.empty? %>
77
+ <authorized_keys config:type="list">
78
+ <% info['AuthorizedKeys'].each do |entry| %>
79
+ <listentry><%= entry %></listentry>
80
+ <% end %>
81
+ </authorized_keys>
82
+ <% end %>
83
+ </user>
84
+ <% end %>
85
+ </users>
86
+ <% end %>
87
+ </profile>
@@ -26,7 +26,8 @@ module ConfigLMM
26
26
  data.each do |name, data|
27
27
  self.processDNS(domain, data).each do |type, records|
28
28
  records.each do |record|
29
- shortName = name
29
+ shortName = Addressable::IDNA.to_ascii(name)
30
+ record[:content] = Addressable::IDNA.to_ascii(record[:content]) if record[:type] == 'CNAME'
30
31
  config['Records'] += [shortName, record[:ttl], ' IN ', record[:type], record[:content]].join("\t") + "\n"
31
32
  end
32
33
  end
@@ -0,0 +1,103 @@
1
+
2
+ require 'fog/libvirt'
3
+ require 'filesize'
4
+
5
+ module ConfigLMM
6
+ module LMM
7
+ class Libvirt < Framework::Plugin
8
+
9
+ DEFAULT_IMAGE_PATH = '~/.local/share/libvirt/images/'
10
+ DEFAULT_VRAM = 16 * 1024 # 16 MiB
11
+
12
+ def actionLibvirtDeploy(id, target, activeState, context, options)
13
+ if !target['Location']
14
+ target['Location'] = 'qemu:///session'
15
+ end
16
+ compute = Fog::Compute.new(provider: :libvirt, libvirt_uri: target['Location'])
17
+ createPools(target, compute, self.class.isLocal?(target['Location']))
18
+ end
19
+
20
+ def createPools(target, compute, isLocal)
21
+ if target['Pools']
22
+ allPools = compute.pools.all
23
+ target['Pools'].each do |name, location|
24
+ next if allPools.find { |pool| pool.name == name }
25
+ location = DEFAULT_IMAGE_PATH unless location
26
+ location = File.expand_path(location) if isLocal
27
+ xml = dirPoolXML(name, location)
28
+ pool = compute.pools.create(persistent: true, autostart: true, xml: xml)
29
+ pool.build
30
+ pool.start
31
+ end
32
+ end
33
+ end
34
+
35
+ def createVM(serverName, serverInfo, targetUri, iso, activeState)
36
+ compute = Fog::Compute.new(provider: :libvirt, libvirt_uri: targetUri)
37
+ server = compute.servers.all.find { |server| server.name == serverName }
38
+ if server
39
+ server.start
40
+ return
41
+ end
42
+ settings = {
43
+ name: serverName,
44
+ cpu: {
45
+ mode: 'host-passthrough'
46
+ },
47
+ video: { type: 'qxl', vram: DEFAULT_VRAM }
48
+ }
49
+ if serverInfo['CPU']
50
+ settings[:cpus] = serverInfo['CPU']
51
+ end
52
+ if serverInfo['RAM']
53
+ settings[:memory_size] = Filesize.from(serverInfo['RAM']).to_f('KiB').to_i
54
+ end
55
+ volumeName = serverName + '.img'
56
+ volume = compute.volumes.all.find { |volume| volume.name == volumeName }
57
+ if volume
58
+ settings[:volumes] = [volume]
59
+ elsif serverInfo['Storage']
60
+ storage = Filesize.from(serverInfo['Storage']).to_f('GiB').to_i
61
+ volume = compute.volumes.create(
62
+ name: volumeName,
63
+ pool_name: compute.pools.first.name,
64
+ capacity: storage
65
+ )
66
+ settings[:volumes] = [volume]
67
+ end
68
+ if serverInfo['NetworkBridge']
69
+ nic = {
70
+ bridge: serverInfo['NetworkBridge'],
71
+ }
72
+ settings[:nics] = [nic]
73
+ end
74
+ server = compute.servers.new(**settings)
75
+ if iso
76
+ server.iso_dir = File.dirname(iso)
77
+ server.iso_file = File.basename(iso)
78
+ end
79
+ server.save
80
+ activeState['Status'] = 'CREATED'
81
+ state.save
82
+ server.start
83
+ end
84
+
85
+ def dirPoolXML(name, path)
86
+ xml = '<pool type="dir">'
87
+ xml += " <name>#{name.encode(:xml => :text)}</name>"
88
+ xml += "<target><path>#{path.encode(:xml => :text)}</path></target>"
89
+ xml += '</pool>'
90
+ xml
91
+ end
92
+
93
+ def self.isLocal?(location)
94
+ self.getLocation(location).empty?
95
+ end
96
+
97
+ def self.getLocation(location)
98
+ uri = Addressable::URI.parse(location)
99
+ uri.hostname
100
+ end
101
+ end
102
+ end
103
+ end
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ConfigLMM - Large Configuration Management Manager
2
2
 
3
+ ![Yo Dawg I Heard you like config so I put a config in your Config](/Images/configINconfig.png)
4
+
3
5
  ## Manage The Management with ease!
4
6
 
5
7
  You define how you want your applications/systems/containers/services/servers
@@ -57,6 +59,8 @@ to work without being vendor locked into any particular implementation or provid
57
59
 
58
60
  The true [GitOps](https://en.wikipedia.org/wiki/DevOps#GitOps)/DevOps/DevSecOps/[TestOps](https://en.wikipedia.org/wiki/TestOps)/SysOps/[AIOps](https://en.wikipedia.org/wiki/Artificial_Intelligence_for_IT_Operations)[DataOps](https://en.wikipedia.org/wiki/DataOps) which I call AllTheOps :)
59
61
 
62
+ ![One Config to Rule Them All](/Images/singleConfig.png)
63
+
60
64
  ## Benefits
61
65
 
62
66
  * Compare performance and price among different providers
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConfigLMM
4
+ module Framework
5
+
6
+ class LinuxApp < Framework::Plugin
7
+
8
+ LINUX_FOLDER = __dir__ + '/../../../../Plugins/OS/Linux/'
9
+ SUSE_NAME = 'openSUSE Leap'
10
+ SUSE_ID = 'opensuse-leap'
11
+
12
+ def ensurePackage(name, location)
13
+ self.class.ensurePackage(name, location)
14
+ end
15
+
16
+ def self.ensurePackage(name, location)
17
+ if location && location != '@me'
18
+ uri = Addressable::URI.parse(location)
19
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
20
+ self.ensurePackageOverSSH(name, uri)
21
+ else
22
+ # TODO
23
+ end
24
+ end
25
+
26
+ def self.ensurePackageOverSSH(name, locationOrSSH)
27
+
28
+ closure = Proc.new do |ssh|
29
+ distroInfo = self.distroInfoFromSSH(ssh)
30
+ pkgs = self.mapPackages([name], distroInfo['Name']).first
31
+
32
+ pkgs = [pkgs] unless pkgs.is_a?(Array)
33
+ command = distroInfo['InstallPackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
34
+ self.sshExec!(ssh, command)
35
+ end
36
+
37
+ if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
38
+ self.sshStart(locationOrSSH) do |ssh|
39
+ closure.call(ssh)
40
+ end
41
+ else
42
+ closure.call(locationOrSSH)
43
+ end
44
+
45
+ end
46
+
47
+ def ensureServiceAutoStart(name, location)
48
+ if location && location != '@me'
49
+ uri = Addressable::URI.parse(location)
50
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
51
+ self.class.sshStart(uri) do |ssh|
52
+ distroInfo = self.class.distroInfoFromSSH(ssh)
53
+
54
+ command = distroInfo['AutoStartService'] + ' ' + name.shellescape
55
+ self.class.sshExec!(ssh, command)
56
+ end
57
+ else
58
+ # TODO
59
+ end
60
+ end
61
+
62
+ def startService(name, location)
63
+ if location && location != '@me'
64
+ uri = Addressable::URI.parse(location)
65
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
66
+ self.class.sshStart(uri) do |ssh|
67
+ distroInfo = self.class.distroInfoFromSSH(ssh)
68
+
69
+ command = distroInfo['StartService'] + ' ' + name.shellescape
70
+ self.class.sshExec!(ssh, command)
71
+ end
72
+ else
73
+ # TODO
74
+ end
75
+ end
76
+
77
+ def self.mapPackages(packages, distroName)
78
+ distroPackages = YAML.load_file(LINUX_FOLDER + 'Packages.yaml')
79
+ names = []
80
+ packages.to_a.each do |pkg|
81
+ packageName = distroPackages[distroName][pkg]
82
+ if packageName
83
+ names << packageName
84
+ else
85
+ names << pkg.downcase
86
+ end
87
+ end
88
+ names
89
+ end
90
+
91
+ def self.createSubuidsOverSSH(user, distroInfo, ssh)
92
+ self.sshExec!(ssh, "#{distroInfo['ModifyUser']} --add-subuids 100000-165535 --add-subgids 100000-165535 #{user}")
93
+ end
94
+
95
+ def self.distroID
96
+ `cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
97
+ end
98
+
99
+ def self.distroIDfromSSH(ssh)
100
+ ssh.exec!('cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2').strip.gsub('"', '')
101
+ end
102
+
103
+ def self.distroInfoFromSSH(ssh)
104
+ distroInfo = self.distroInfo(self.distroIDfromSSH(ssh))
105
+ end
106
+
107
+ def self.distroInfo(distroID)
108
+ distributions = YAML.load_file(LINUX_FOLDER + 'Distributions.yaml')
109
+ raise Framework::PluginProcessError.new("Unknown Linux Distro: #{distroID}!") unless distributions.key?(distroID)
110
+ distributions[distroID]
111
+ end
112
+
113
+ end
114
+ end
115
+ end