boxgrinder-core 0.0.4 → 0.0.6
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.
- data/lib/boxgrinder-core/defaults.rb +0 -41
- data/lib/boxgrinder-core/helpers/appliance-config-helper.rb +53 -104
- data/lib/boxgrinder-core/helpers/appliance-helper.rb +115 -0
- data/lib/boxgrinder-core/helpers/log-helper.rb +45 -16
- data/lib/boxgrinder-core/helpers/queue-helper.rb +54 -0
- data/lib/boxgrinder-core/models/appliance-config.rb +40 -23
- data/lib/boxgrinder-core/models/config.rb +19 -6
- data/lib/boxgrinder-core/validators/appliance-config-validator.rb +75 -0
- metadata +6 -4
- data/lib/boxgrinder-core/validators/appliance-definition-validator.rb +0 -90
@@ -21,23 +21,9 @@
|
|
21
21
|
module BoxGrinder
|
22
22
|
# here are global variables
|
23
23
|
SUPPORTED_ARCHES = [ "i386", "x86_64" ]
|
24
|
-
SUPPORTED_OSES = {
|
25
|
-
"fedora" => [ "12", "11", "rawhide" ]
|
26
|
-
}
|
27
|
-
|
28
|
-
LATEST_STABLE_RELEASES = {
|
29
|
-
"fedora" => "12",
|
30
|
-
"rhel" => "5"
|
31
|
-
}
|
32
|
-
|
33
|
-
DEVELOPMENT_RELEASES = {
|
34
|
-
"fedora" => "rawhide"
|
35
|
-
}
|
36
24
|
|
37
25
|
APPLIANCE_DEFAULTS = {
|
38
26
|
:os => {
|
39
|
-
:name => "fedora",
|
40
|
-
:version => LATEST_STABLE_RELEASES['fedora'],
|
41
27
|
:password => "boxgrinder"
|
42
28
|
},
|
43
29
|
:hardware => {
|
@@ -50,33 +36,6 @@ module BoxGrinder
|
|
50
36
|
|
51
37
|
SUPPORTED_DESKTOP_TYPES = [ "gnome" ]
|
52
38
|
|
53
|
-
# you can use #ARCH# variable to specify build arch
|
54
|
-
REPOS = {
|
55
|
-
"fedora" => {
|
56
|
-
"12" => {
|
57
|
-
"base" => {
|
58
|
-
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-12&arch=#ARCH#"
|
59
|
-
},
|
60
|
-
"updates" => {
|
61
|
-
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f12&arch=#ARCH#"
|
62
|
-
}
|
63
|
-
},
|
64
|
-
"11" => {
|
65
|
-
"base" => {
|
66
|
-
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-11&arch=#ARCH#"
|
67
|
-
},
|
68
|
-
"updates" => {
|
69
|
-
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f11&arch=#ARCH#"
|
70
|
-
}
|
71
|
-
},
|
72
|
-
"rawhide" => {
|
73
|
-
"base" => {
|
74
|
-
"mirrorlist" => "http://mirrors.fedoraproject.org/mirrorlist?repo=rawhide&arch=#ARCH#"
|
75
|
-
}
|
76
|
-
}
|
77
|
-
}
|
78
|
-
}
|
79
|
-
|
80
39
|
DEFAULT_LOCATION = {
|
81
40
|
:log => 'log/boxgrinder.log'
|
82
41
|
}
|
@@ -23,21 +23,15 @@ require 'boxgrinder-core/validators/errors'
|
|
23
23
|
module BoxGrinder
|
24
24
|
class ApplianceConfigHelper
|
25
25
|
|
26
|
-
def initialize(
|
27
|
-
@
|
26
|
+
def initialize(appliance_configs)
|
27
|
+
@appliance_configs = appliance_configs.values.reverse
|
28
28
|
end
|
29
29
|
|
30
|
-
def merge(
|
30
|
+
def merge(appliance_config)
|
31
31
|
@appliance_config = appliance_config
|
32
32
|
|
33
|
-
# add current appliance definition
|
34
|
-
@appliance_definitions[@appliance_config.name] = @appliance_config.definition unless @appliance_definitions.has_key?( @appliance_config.name )
|
35
|
-
|
36
|
-
@current_appliances = get_appliances( @appliance_config.name ).reverse
|
37
|
-
|
38
33
|
prepare_os
|
39
34
|
prepare_appliances
|
40
|
-
prepare_version_and_release
|
41
35
|
|
42
36
|
merge_hardware
|
43
37
|
merge_repos
|
@@ -56,33 +50,19 @@ module BoxGrinder
|
|
56
50
|
end
|
57
51
|
|
58
52
|
def merge_cpus
|
59
|
-
|
60
|
-
@appliance_config.hardware.cpus = @appliance_config.definition['hardware']['cpus']
|
61
|
-
end
|
62
|
-
|
63
|
-
merge_field('cpus', 'hardware'){ |cpus| @appliance_config.hardware.cpus = cpus if cpus > @appliance_config.hardware.cpus }
|
64
|
-
|
65
|
-
@appliance_config.hardware.cpus = APPLIANCE_DEFAULTS[:hardware][:cpus] if @appliance_config.hardware.cpus == 0
|
53
|
+
merge_field('hardware.cpus') { |cpus| @appliance_config.hardware.cpus = cpus if cpus > @appliance_config.hardware.cpus }
|
66
54
|
end
|
67
55
|
|
68
56
|
# This will merge partitions from multiple appliances.
|
69
57
|
def merge_partitions
|
70
58
|
partitions = {}
|
71
59
|
|
72
|
-
|
73
|
-
|
74
|
-
partitions
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
partitions['/'] = { 'root' => '/', 'size' => APPLIANCE_DEFAULTS[:hardware][:partition] } unless partitions.keys.include?('/')
|
79
|
-
|
80
|
-
merge_field('partitions', 'hardware') do |parts|
|
81
|
-
for partition in parts
|
82
|
-
if partitions.keys.include?(partition['root'])
|
83
|
-
partitions[partition['root']]['size'] = partition['size'] if partitions[partition['root']]['size'] < partition['size']
|
60
|
+
merge_field('hardware.partitions') do |parts|
|
61
|
+
parts.each do |root, partition|
|
62
|
+
if partitions.keys.include?(root)
|
63
|
+
partitions[root]['size'] = partition['size'] if partitions[root]['size'] < partition['size']
|
84
64
|
else
|
85
|
-
partitions[
|
65
|
+
partitions[root] = partition
|
86
66
|
end
|
87
67
|
end
|
88
68
|
end
|
@@ -91,118 +71,87 @@ module BoxGrinder
|
|
91
71
|
end
|
92
72
|
|
93
73
|
def merge_memory
|
94
|
-
|
95
|
-
|
96
|
-
merge_field('memory', 'hardware') { |memory| @appliance_config.hardware.memory = memory if memory > @appliance_config.hardware.memory }
|
97
|
-
|
98
|
-
@appliance_config.hardware.memory = APPLIANCE_DEFAULTS[:hardware][:memory] if @appliance_config.hardware.memory == 0
|
74
|
+
merge_field('hardware.memory') { |memory| @appliance_config.hardware.memory = memory if memory > @appliance_config.hardware.memory }
|
99
75
|
end
|
100
76
|
|
101
77
|
def prepare_os
|
102
|
-
merge_field(
|
103
|
-
merge_field(
|
104
|
-
merge_field(
|
105
|
-
end
|
78
|
+
merge_field('os.name') { |name| @appliance_config.os.name = name.to_s }
|
79
|
+
merge_field('os.version') { |version| @appliance_config.os.version = version.to_s }
|
80
|
+
merge_field('os.password') { |password| @appliance_config.os.password = password.to_s }
|
106
81
|
|
107
|
-
|
108
|
-
for appliance in @current_appliances
|
109
|
-
@appliance_config.appliances << appliance
|
110
|
-
end
|
82
|
+
@appliance_config.os.password = APPLIANCE_DEFAULTS[:os][:password] if @appliance_config.os.password.nil?
|
111
83
|
end
|
112
84
|
|
113
|
-
def
|
114
|
-
|
115
|
-
@appliance_config.version = @appliance_config.definition['version']
|
116
|
-
end
|
85
|
+
def prepare_appliances
|
86
|
+
@appliance_config.appliances.clear
|
117
87
|
|
118
|
-
|
119
|
-
@appliance_config.
|
88
|
+
@appliance_configs.each do |appliance_config|
|
89
|
+
@appliance_config.appliances << appliance_config.name unless appliance_config.name == @appliance_config.name
|
120
90
|
end
|
121
91
|
end
|
122
92
|
|
123
93
|
def merge_repos
|
124
|
-
|
125
|
-
definition = @appliance_definitions[appliance_name]
|
94
|
+
@appliance_config.repos.clear
|
126
95
|
|
127
|
-
|
128
|
-
|
129
|
-
['
|
130
|
-
|
96
|
+
@appliance_configs.each do |appliance_config|
|
97
|
+
for repo in appliance_config.repos
|
98
|
+
repo['name'] = substitute_repo_parameters(repo['name'])
|
99
|
+
['baseurl', 'mirrorlist'].each do |type|
|
100
|
+
repo[type] = substitute_repo_parameters(repo[type]) unless repo[type].nil?
|
131
101
|
end
|
132
102
|
|
133
103
|
@appliance_config.repos << repo
|
134
|
-
end
|
104
|
+
end
|
135
105
|
end
|
136
106
|
end
|
137
107
|
|
138
|
-
def substitute_repo_parameters(
|
108
|
+
def substitute_repo_parameters(str)
|
139
109
|
return if str.nil?
|
140
|
-
str.gsub(
|
110
|
+
str.gsub(/#OS_NAME#/, @appliance_config.os.name).gsub(/#OS_VERSION#/, @appliance_config.os.version).gsub(/#ARCH#/, @appliance_config.hardware.arch)
|
141
111
|
end
|
142
112
|
|
143
113
|
def merge_packages
|
144
|
-
|
145
|
-
|
114
|
+
@appliance_config.packages.includes.clear
|
115
|
+
@appliance_config.packages.excludes.clear
|
146
116
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
117
|
+
@appliance_configs.each do |appliance_config|
|
118
|
+
appliance_config.packages.includes.each do |package|
|
119
|
+
@appliance_config.packages.includes << package
|
120
|
+
end
|
151
121
|
|
152
|
-
|
153
|
-
|
154
|
-
end unless definition['packages']['excludes'].nil?
|
122
|
+
appliance_config.packages.excludes.each do |package|
|
123
|
+
@appliance_config.packages.excludes << package
|
155
124
|
end
|
156
125
|
end
|
157
126
|
end
|
158
127
|
|
128
|
+
# TODO this needs to be plugin independent!
|
159
129
|
def merge_post_operations
|
160
|
-
|
161
|
-
|
130
|
+
@appliance_config.post.base.clear
|
131
|
+
@appliance_config.post.ec2.clear
|
132
|
+
@appliance_config.post.vmware.clear
|
162
133
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
for cmd in definition['post']['ec2']
|
169
|
-
@appliance_config.post.ec2 << cmd
|
170
|
-
end unless definition['post']['ec2'].nil?
|
134
|
+
@appliance_configs.each do |appliance_config|
|
135
|
+
appliance_config.post.base do |cmd|
|
136
|
+
@appliance_config.post.base << cmd
|
137
|
+
end
|
171
138
|
|
172
|
-
|
173
|
-
|
174
|
-
end unless definition['post']['vmware'].nil?
|
139
|
+
appliance_config.post.ec2 do |cmd|
|
140
|
+
@appliance_config.post.ec2 << cmd
|
175
141
|
end
|
176
142
|
|
143
|
+
appliance_config.post.vmware do |cmd|
|
144
|
+
@appliance_config.post.vmware << cmd
|
145
|
+
end
|
177
146
|
end
|
178
147
|
end
|
179
148
|
|
180
|
-
def merge_field(
|
181
|
-
|
182
|
-
|
183
|
-
next if
|
184
|
-
|
185
|
-
yield val
|
186
|
-
end unless @appliance_config.definition['appliances'].nil?
|
187
|
-
end
|
188
|
-
|
189
|
-
def get_appliances( appliance_name )
|
190
|
-
appliances = []
|
191
|
-
|
192
|
-
if @appliance_definitions.has_key?( appliance_name )
|
193
|
-
definition = @appliance_definitions[appliance_name]
|
194
|
-
# add current appliance name
|
195
|
-
appliances << definition['name']
|
196
|
-
|
197
|
-
definition['appliances'].each do |appl|
|
198
|
-
appliances += get_appliances( appl ) unless appliances.include?( appl )
|
199
|
-
end unless definition['appliances'].nil? or definition['appliances'].empty?
|
200
|
-
else
|
201
|
-
raise ApplianceValidationError, "Not valid appliance name: Specified appliance name '#{appliance_name}' could not be found in appliance list. Please correct your definition file."
|
149
|
+
def merge_field(field, force = false)
|
150
|
+
@appliance_configs.each do |appliance_config|
|
151
|
+
value = eval("appliance_config.#{field}")
|
152
|
+
next if value.nil? and !force
|
153
|
+
yield value
|
202
154
|
end
|
203
|
-
|
204
|
-
appliances
|
205
155
|
end
|
206
|
-
|
207
156
|
end
|
208
157
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'yaml'
|
22
|
+
|
23
|
+
module BoxGrinder
|
24
|
+
class ApplianceHelper
|
25
|
+
def initialize(options = {})
|
26
|
+
@log = options[:log] || Logger.new(STDOUT)
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_definitions(definition_file, content_type = nil)
|
30
|
+
@log.debug "Reading definition from '#{definition_file}' file..."
|
31
|
+
|
32
|
+
definition_file_extension = File.extname(definition_file)
|
33
|
+
configs = {}
|
34
|
+
|
35
|
+
appliance_config =
|
36
|
+
case definition_file_extension
|
37
|
+
when '.appl', '.yml'
|
38
|
+
read_yaml(definition_file)
|
39
|
+
when '.xml'
|
40
|
+
read_xml(definition_file)
|
41
|
+
else
|
42
|
+
unless content_type.nil?
|
43
|
+
case content_type
|
44
|
+
when 'application/x-yaml', 'text/yaml'
|
45
|
+
read_yaml(definition_file)
|
46
|
+
when 'application/xml', 'text/xml', 'application/x-xml'
|
47
|
+
read_xml(definition_file)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raise 'Unsupported file format for appliance definition file'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
configs[appliance_config.name] = appliance_config
|
55
|
+
|
56
|
+
appliance_config.appliances.each do |appliance_name|
|
57
|
+
configs.merge!(read_definitions("#{File.dirname(definition_file)}/#{appliance_name}#{definition_file_extension}").first)
|
58
|
+
end unless appliance_config.appliances.nil? or !appliance_config.appliances.is_a?(Array)
|
59
|
+
|
60
|
+
[ configs, appliance_config ]
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_yaml(file)
|
64
|
+
begin
|
65
|
+
definition = YAML.load_file(file)
|
66
|
+
raise if definition.nil?
|
67
|
+
rescue
|
68
|
+
raise "File '#{file}' could not be read."
|
69
|
+
end
|
70
|
+
|
71
|
+
return definition if definition.is_a?(ApplianceConfig)
|
72
|
+
|
73
|
+
appliance_config = ApplianceConfig.new
|
74
|
+
|
75
|
+
appliance_config.name = definition['name'] unless definition['name'].nil?
|
76
|
+
appliance_config.summary = definition['summary'] unless definition['summary'].nil?
|
77
|
+
appliance_config.appliances = definition['appliances'] unless definition['appliances'].nil?
|
78
|
+
appliance_config.repos = definition['repos'] unless definition['repos'].nil?
|
79
|
+
|
80
|
+
appliance_config.version = definition['version'].to_s unless definition['version'].nil?
|
81
|
+
appliance_config.release = definition['release'].to_s unless definition['release'].nil?
|
82
|
+
|
83
|
+
unless definition['packages'].nil?
|
84
|
+
appliance_config.packages.includes = definition['packages']['includes'] unless definition['packages']['includes'].nil?
|
85
|
+
appliance_config.packages.excludes = definition['packages']['excludes'] unless definition['packages']['excludes'].nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
unless definition['os'].nil?
|
89
|
+
appliance_config.os.name = definition['os']['name'].to_s unless definition['os']['name'].nil?
|
90
|
+
appliance_config.os.version = definition['os']['version'].to_s unless definition['os']['version'].nil?
|
91
|
+
appliance_config.os.password = definition['os']['password'].to_s unless definition['os']['password'].nil?
|
92
|
+
end
|
93
|
+
|
94
|
+
unless definition['hardware'].nil?
|
95
|
+
appliance_config.hardware.arch = definition['hardware']['cpus'] unless definition['hardware']['arch'].nil?
|
96
|
+
appliance_config.hardware.cpus = definition['hardware']['cpus'] unless definition['hardware']['cpus'].nil?
|
97
|
+
appliance_config.hardware.memory = definition['hardware']['memory'] unless definition['hardware']['memory'].nil?
|
98
|
+
appliance_config.hardware.network = definition['hardware']['network'] unless definition['hardware']['network'].nil?
|
99
|
+
appliance_config.hardware.partitions = definition['hardware']['partitions'] unless definition['hardware']['partitions'].nil?
|
100
|
+
end
|
101
|
+
|
102
|
+
unless definition['post'].nil?
|
103
|
+
appliance_config.post.base = definition['post']['base'] unless definition['post']['base'].nil?
|
104
|
+
appliance_config.post.ec2 = definition['post']['ec2'] unless definition['post']['ec2'].nil?
|
105
|
+
appliance_config.post.vmware = definition['post']['vmware'] unless definition['post']['vmware'].nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
appliance_config
|
109
|
+
end
|
110
|
+
|
111
|
+
def read_xml(file)
|
112
|
+
raise "Reading XML files is not supported right now. File '#{file}' could not be read"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -21,10 +21,31 @@
|
|
21
21
|
require 'logger'
|
22
22
|
require 'boxgrinder-core/defaults'
|
23
23
|
|
24
|
+
Logger.const_set(:TRACE, 0)
|
25
|
+
Logger.const_set(:DEBUG, 1)
|
26
|
+
Logger.const_set(:INFO, 2)
|
27
|
+
Logger.const_set(:WARN, 3)
|
28
|
+
Logger.const_set(:ERROR, 4)
|
29
|
+
Logger.const_set(:FATAL, 5)
|
30
|
+
Logger.const_set(:UNKNOWN, 6)
|
31
|
+
|
32
|
+
Logger::SEV_LABEL.insert(0, 'TRACE')
|
33
|
+
|
34
|
+
class Logger
|
35
|
+
def trace?
|
36
|
+
@level <= TRACE
|
37
|
+
end
|
38
|
+
|
39
|
+
def trace(progname = nil, &block)
|
40
|
+
add(TRACE, nil, progname, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
24
44
|
module BoxGrinder
|
25
45
|
class LogHelper
|
26
46
|
|
27
47
|
THRESHOLDS = {
|
48
|
+
:trace => Logger::TRACE,
|
28
49
|
:fatal => Logger::FATAL,
|
29
50
|
:debug => Logger::DEBUG,
|
30
51
|
:error => Logger::ERROR,
|
@@ -32,30 +53,38 @@ module BoxGrinder
|
|
32
53
|
:info => Logger::INFO
|
33
54
|
}
|
34
55
|
|
35
|
-
def initialize(
|
36
|
-
|
37
|
-
|
56
|
+
def initialize(options = {})
|
57
|
+
location = options[:location] || DEFAULT_LOCATION[:log]
|
58
|
+
threshold = options[:threshold] || :info
|
59
|
+
type = options[:type] || [:stdout, :file]
|
38
60
|
|
39
|
-
unless
|
40
|
-
|
61
|
+
unless type.is_a?(Array)
|
62
|
+
type = [type.to_s.to_sym]
|
41
63
|
end
|
42
64
|
|
43
65
|
threshold = THRESHOLDS[threshold.to_sym] unless threshold.nil?
|
66
|
+
formatter = Logger::Formatter.new
|
44
67
|
|
45
|
-
|
46
|
-
|
68
|
+
if type.include?(:file)
|
69
|
+
unless File.directory?(File.dirname(location))
|
70
|
+
FileUtils.mkdir_p(File.dirname(location))
|
71
|
+
end
|
47
72
|
|
48
|
-
|
49
|
-
|
50
|
-
|
73
|
+
@file_log = Logger.new(location, 10, 1024000)
|
74
|
+
@file_log.level = Logger::TRACE
|
75
|
+
@file_log.formatter = formatter
|
76
|
+
end
|
51
77
|
|
52
|
-
|
53
|
-
|
54
|
-
@stdout_log.
|
55
|
-
@
|
56
|
-
else
|
57
|
-
raise NoMethodError
|
78
|
+
if type.include?(:stdout)
|
79
|
+
@stdout_log = Logger.new(STDOUT)
|
80
|
+
@stdout_log.level = threshold || Logger::INFO
|
81
|
+
@stdout_log.formatter = formatter
|
58
82
|
end
|
59
83
|
end
|
84
|
+
|
85
|
+
def method_missing(method_name, *args)
|
86
|
+
@stdout_log.send(method_name, *args) unless @stdout_log.nil?
|
87
|
+
@file_log.send(method_name, *args) unless @file_log.nil?
|
88
|
+
end
|
60
89
|
end
|
61
90
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'logger'
|
22
|
+
|
23
|
+
module BoxGrinder
|
24
|
+
class QueueHelper
|
25
|
+
def initialize(options = {})
|
26
|
+
@log = options[:log] || Logger.new(STDOUT)
|
27
|
+
end
|
28
|
+
|
29
|
+
def client(opts = {})
|
30
|
+
begin
|
31
|
+
require 'rubygems'
|
32
|
+
require 'torquebox-messaging-client'
|
33
|
+
rescue
|
34
|
+
@log.error "Couldn't load TorqueBox messaging client."
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
host = opts[:host] || 'localhost'
|
39
|
+
port = opts[:port] || 1099
|
40
|
+
|
41
|
+
naming_provider_url = "jnp://#{host}:#{port}/"
|
42
|
+
|
43
|
+
@log.trace "Creating messaging client..."
|
44
|
+
|
45
|
+
ret_val = TorqueBox::Messaging::Client.connect(:naming_provider_url => naming_provider_url ) do |client|
|
46
|
+
yield client
|
47
|
+
end
|
48
|
+
|
49
|
+
@log.trace "Messaging client closed."
|
50
|
+
|
51
|
+
ret_val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -24,22 +24,22 @@ require 'rbconfig'
|
|
24
24
|
|
25
25
|
module BoxGrinder
|
26
26
|
class ApplianceConfig
|
27
|
-
def initialize( definition )
|
28
|
-
|
27
|
+
def initialize #( definition )
|
28
|
+
#@definition = definition
|
29
29
|
|
30
|
-
@name =
|
31
|
-
@summary =
|
30
|
+
@name = nil #@definition['name']
|
31
|
+
@summary = nil #@definition['summary']
|
32
32
|
|
33
33
|
@os = OpenStruct.new
|
34
34
|
|
35
|
-
@os.name = APPLIANCE_DEFAULTS[:os][:name]
|
36
|
-
@os.version = APPLIANCE_DEFAULTS[:os][:version]
|
37
|
-
@os.password = APPLIANCE_DEFAULTS[:os][:password]
|
35
|
+
@os.name = nil #APPLIANCE_DEFAULTS[:os][:name]
|
36
|
+
@os.version = nil #APPLIANCE_DEFAULTS[:os][:version]
|
37
|
+
@os.password = nil #APPLIANCE_DEFAULTS[:os][:password]
|
38
38
|
|
39
39
|
@hardware = OpenStruct.new
|
40
40
|
|
41
|
-
@hardware.cpus =
|
42
|
-
@hardware.memory =
|
41
|
+
@hardware.cpus = APPLIANCE_DEFAULTS[:hardware][:cpus]
|
42
|
+
@hardware.memory = APPLIANCE_DEFAULTS[:hardware][:memory]
|
43
43
|
@hardware.network = APPLIANCE_DEFAULTS[:hardware][:network]
|
44
44
|
|
45
45
|
@post = OpenStruct.new
|
@@ -48,24 +48,33 @@ module BoxGrinder
|
|
48
48
|
@post.ec2 = []
|
49
49
|
@post.vmware = []
|
50
50
|
|
51
|
+
@packages = OpenStruct.new
|
52
|
+
@packages.includes = []
|
53
|
+
@packages.excludes = []
|
54
|
+
|
51
55
|
@appliances = []
|
52
56
|
@repos = []
|
53
|
-
|
57
|
+
|
54
58
|
@version = 1
|
55
59
|
@release = 0
|
56
60
|
end
|
57
61
|
|
58
|
-
attr_reader :definition
|
59
|
-
attr_reader :name
|
60
|
-
attr_reader :summary
|
61
|
-
attr_reader :appliances
|
62
|
+
#attr_reader :definition
|
63
|
+
#attr_reader :name
|
64
|
+
#attr_reader :summary
|
65
|
+
#attr_reader :appliances
|
62
66
|
attr_reader :os
|
63
67
|
attr_reader :hardware
|
64
|
-
attr_reader :repos
|
65
|
-
attr_reader :packages
|
68
|
+
#attr_reader :repos
|
69
|
+
#attr_reader :packages
|
66
70
|
attr_reader :path
|
67
71
|
attr_reader :file
|
68
72
|
|
73
|
+
attr_accessor :packages
|
74
|
+
attr_accessor :repos
|
75
|
+
attr_accessor :appliances
|
76
|
+
attr_accessor :summary
|
77
|
+
attr_accessor :name
|
69
78
|
attr_accessor :version
|
70
79
|
attr_accessor :release
|
71
80
|
attr_accessor :post
|
@@ -98,6 +107,8 @@ module BoxGrinder
|
|
98
107
|
|
99
108
|
@path.dir.packages = "build/#{appliance_path}/packages"
|
100
109
|
|
110
|
+
@path.dir.build = "build/#{appliance_path}"
|
111
|
+
|
101
112
|
@path.dir.raw.build = "build/#{appliance_path}/raw"
|
102
113
|
@path.dir.raw.build_full = "build/#{appliance_path}/raw/#{@name}"
|
103
114
|
|
@@ -115,15 +126,15 @@ module BoxGrinder
|
|
115
126
|
@path.file.raw.xml = "#{@path.dir.raw.build_full}/#{@name}.xml"
|
116
127
|
|
117
128
|
@path.file.ec2.disk = "#{@path.dir.ec2.build}/#{@name}.ec2"
|
118
|
-
@path.file.ec2.manifest = "#{@path.dir.ec2.bundle}/#{@name}.
|
129
|
+
@path.file.ec2.manifest = "#{@path.dir.ec2.bundle}/#{@name}-sda.raw.manifest.xml"
|
119
130
|
|
120
|
-
@path.file.vmware.disk = "#{@path.dir.vmware.build}/#{@name}
|
131
|
+
@path.file.vmware.disk = "#{@path.dir.vmware.build}/#{@name}.raw"
|
121
132
|
@path.file.vmware.personal.vmx = "#{@path.dir.vmware.personal}/#{@name}.vmx"
|
122
133
|
@path.file.vmware.personal.vmdk = "#{@path.dir.vmware.personal}/#{@name}.vmdk"
|
123
|
-
@path.file.vmware.personal.disk = "#{@path.dir.vmware.personal}/#{@name}
|
134
|
+
@path.file.vmware.personal.disk = "#{@path.dir.vmware.personal}/#{@name}.raw"
|
124
135
|
@path.file.vmware.enterprise.vmx = "#{@path.dir.vmware.enterprise}/#{@name}.vmx"
|
125
136
|
@path.file.vmware.enterprise.vmdk = "#{@path.dir.vmware.enterprise}/#{@name}.vmdk"
|
126
|
-
@path.file.vmware.enterprise.disk = "#{@path.dir.vmware.enterprise}/#{@name}
|
137
|
+
@path.file.vmware.enterprise.disk = "#{@path.dir.vmware.enterprise}/#{@name}.raw"
|
127
138
|
|
128
139
|
@path.file.package = {
|
129
140
|
:raw => {
|
@@ -140,9 +151,10 @@ module BoxGrinder
|
|
140
151
|
|
141
152
|
# used to checking if configuration differs from previous in appliance-kickstart
|
142
153
|
def hash
|
143
|
-
"#{@name}-#{@summary}-#{@version}-#{@release}-#{@os.name}-#{@os.version}-#{@os.password}-#{@hardware.cpus}-#{@hardware.memory}-#{@hardware.partitions}-#{@appliances
|
154
|
+
"#{@name}-#{@summary}-#{@version}-#{@release}-#{@os.name}-#{@os.version}-#{@os.password}-#{@hardware.cpus}-#{@hardware.memory}-#{@hardware.partitions}-#{@appliances}".hash
|
144
155
|
end
|
145
156
|
|
157
|
+
# TODO remove this, leave only :name
|
146
158
|
def simple_name
|
147
159
|
@name
|
148
160
|
end
|
@@ -167,8 +179,13 @@ module BoxGrinder
|
|
167
179
|
@hardware.arch.eql?("x86_64")
|
168
180
|
end
|
169
181
|
|
170
|
-
def
|
171
|
-
|
182
|
+
def clone
|
183
|
+
Marshal::load(Marshal.dump(self))
|
184
|
+
end
|
185
|
+
|
186
|
+
def os_family
|
187
|
+
return :linux if [ 'fedora' ].include?( @os.name )
|
188
|
+
return :unknown
|
172
189
|
end
|
173
190
|
end
|
174
191
|
end
|
@@ -24,14 +24,27 @@ require 'rbconfig'
|
|
24
24
|
|
25
25
|
module BoxGrinder
|
26
26
|
class Config
|
27
|
-
def initialize
|
28
|
-
|
29
|
-
@
|
30
|
-
|
27
|
+
def initialize
|
28
|
+
|
29
|
+
@name = DEFAULT_PROJECT_CONFIG[:name]
|
30
|
+
|
31
|
+
@dir = OpenStruct.new
|
32
|
+
@dir.root = `pwd`.strip
|
33
|
+
@dir.base = "#{File.dirname( __FILE__ )}/../../"
|
34
|
+
@dir.build = DEFAULT_PROJECT_CONFIG[:dir_build]
|
35
|
+
@dir.top = "#{@dir.build}/topdir"
|
36
|
+
@dir.src_cache = DEFAULT_PROJECT_CONFIG[:dir_src_cache]
|
37
|
+
@dir.rpms_cache = DEFAULT_PROJECT_CONFIG[:dir_rpms_cache]
|
38
|
+
@dir.specs = DEFAULT_PROJECT_CONFIG[:dir_specs]
|
39
|
+
@dir.appliances = DEFAULT_PROJECT_CONFIG[:dir_appliances]
|
40
|
+
@dir.src = DEFAULT_PROJECT_CONFIG[:dir_src]
|
41
|
+
@dir.kickstarts = DEFAULT_PROJECT_CONFIG[:dir_kickstarts]
|
42
|
+
|
43
|
+
@config_file = ENV['BG_CONFIG_FILE'] || "#{ENV['HOME']}/.boxgrinder/config"
|
31
44
|
|
32
45
|
@version = OpenStruct.new
|
33
|
-
@version.version = version
|
34
|
-
@version.release = release
|
46
|
+
@version.version = DEFAULT_PROJECT_CONFIG[:version]
|
47
|
+
@version.release = DEFAULT_PROJECT_CONFIG[:release]
|
35
48
|
|
36
49
|
@files = OpenStruct.new
|
37
50
|
@data = {}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# JBoss, Home of Professional Open Source
|
2
|
+
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
+
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
+
# full listing of individual contributors.
|
5
|
+
#
|
6
|
+
# This is free software; you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU Lesser General Public License as
|
8
|
+
# published by the Free Software Foundation; either version 2.1 of
|
9
|
+
# the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This software is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this software; if not, write to the Free
|
18
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
+
|
21
|
+
require 'boxgrinder-core/validators/errors'
|
22
|
+
require 'boxgrinder-core/defaults'
|
23
|
+
|
24
|
+
module BoxGrinder
|
25
|
+
class ApplianceConfigValidator
|
26
|
+
def initialize( appliance_config, options = {} )
|
27
|
+
@appliance_config = appliance_config
|
28
|
+
@options = options
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate
|
32
|
+
check_for_missing_field( 'name' )
|
33
|
+
check_for_missing_field( 'summary' )
|
34
|
+
|
35
|
+
validate_os
|
36
|
+
validate_hardware
|
37
|
+
validate_repos
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def check_for_missing_field( name )
|
43
|
+
raise ApplianceValidationError, "Missing field: appliance definition file should have field '#{name}'" if eval("@appliance_config.#{name}").nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_os
|
47
|
+
raise ApplianceValidationError, "No operating system selected" if @appliance_config.os.name.nil?
|
48
|
+
|
49
|
+
unless @options[:os_plugins].nil?
|
50
|
+
os_plugin = @options[:os_plugins][@appliance_config.os.name.to_sym]
|
51
|
+
|
52
|
+
raise ApplianceValidationError, "Not supported operating system selected: #{@appliance_config.os.name}. Supported OSes are: #{OperatingSystemPluginManager.instance.plugins.keys.join(", ")}" if os_plugin.nil?
|
53
|
+
raise ApplianceValidationError, "Not supported operating system version selected: #{@appliance_config.os.version}. Supported versions are: #{os_plugin.info[:versions].join(", ")}" unless @appliance_config.os.version.nil? or os_plugin.info[:versions].include?( @appliance_config.os.version )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_hardware
|
58
|
+
raise ApplianceValidationError, "Not valid CPU amount: Too many or too less CPU's: '#{@appliance_config.hardware.cpus}'. Please choose from 1-4. Please correct your appliance definition file, thanks." unless @appliance_config.hardware.cpus >= 1 and @appliance_config.hardware.cpus <= 4
|
59
|
+
raise ApplianceValidationError, "Not valid memory amount: '#{@appliance_config.hardware.memory}' is wrong value. Please correct your appliance definition file" if @appliance_config.hardware.memory =~ /\d/
|
60
|
+
raise ApplianceValidationError, "Not valid memory amount: '#{@appliance_config.hardware.memory}' is not allowed here. Memory should be multiplicity of 64. Please correct your appliance definition file" if (@appliance_config.hardware.memory.to_i % 64 > 0)
|
61
|
+
|
62
|
+
raise ApplianceValidationError, "No partitions found. Please correct your appliance definition file" if @appliance_config.hardware.partitions.size == 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_repos
|
66
|
+
return if @appliance_config.repos.size == 0
|
67
|
+
|
68
|
+
@appliance_config.repos.each do |repo|
|
69
|
+
raise ApplianceValidationError, "Not valid repo format: '#{repo}' is wrong value. Please correct your appliance definition file, thanks." unless repo.class.eql?(Hash)
|
70
|
+
raise ApplianceValidationError, "Not valid repo format: Please specify name for repository. Please correct your appliance definition file, thanks." unless repo.keys.include?('name')
|
71
|
+
raise ApplianceValidationError, "Not valid repo format: There is no 'mirrorlist' or 'baseurl' specified for '#{repo['name']}' repository. Please correct your appliance definition file, thanks." unless repo.keys.include?('mirrorlist') or repo.keys.include?('baseurl')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 6
|
9
|
+
version: 0.0.6
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- BoxGrinder Project
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-10 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -33,8 +33,10 @@ files:
|
|
33
33
|
- lib/boxgrinder-core/models/task.rb
|
34
34
|
- lib/boxgrinder-core/defaults.rb
|
35
35
|
- lib/boxgrinder-core/validators/errors.rb
|
36
|
-
- lib/boxgrinder-core/validators/appliance-
|
36
|
+
- lib/boxgrinder-core/validators/appliance-config-validator.rb
|
37
37
|
- lib/boxgrinder-core/helpers/exec-helper.rb
|
38
|
+
- lib/boxgrinder-core/helpers/queue-helper.rb
|
39
|
+
- lib/boxgrinder-core/helpers/appliance-helper.rb
|
38
40
|
- lib/boxgrinder-core/helpers/log-helper.rb
|
39
41
|
- lib/boxgrinder-core/helpers/appliance-config-helper.rb
|
40
42
|
- README
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# JBoss, Home of Professional Open Source
|
2
|
-
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
|
3
|
-
# by the @authors tag. See the copyright.txt in the distribution for a
|
4
|
-
# full listing of individual contributors.
|
5
|
-
#
|
6
|
-
# This is free software; you can redistribute it and/or modify it
|
7
|
-
# under the terms of the GNU Lesser General Public License as
|
8
|
-
# published by the Free Software Foundation; either version 2.1 of
|
9
|
-
# the License, or (at your option) any later version.
|
10
|
-
#
|
11
|
-
# This software is distributed in the hope that it will be useful,
|
12
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
-
# Lesser General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU Lesser General Public
|
17
|
-
# License along with this software; if not, write to the Free
|
18
|
-
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
19
|
-
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
20
|
-
|
21
|
-
require 'boxgrinder-core/validators/errors'
|
22
|
-
require 'boxgrinder-core/defaults'
|
23
|
-
|
24
|
-
module BoxGrinder
|
25
|
-
class ApplianceDefinitionValidator
|
26
|
-
def initialize( appliance_definition )
|
27
|
-
@appliance_definition = appliance_definition
|
28
|
-
end
|
29
|
-
|
30
|
-
def validate
|
31
|
-
check_for_missing_field( 'name' )
|
32
|
-
|
33
|
-
validate_os
|
34
|
-
validate_hardware
|
35
|
-
validate_repos
|
36
|
-
end
|
37
|
-
|
38
|
-
protected
|
39
|
-
|
40
|
-
def check_for_missing_field( name )
|
41
|
-
raise ApplianceValidationError, "Missing field: appliance definition file should have field '#{name}'" if @appliance_definition[name].nil?
|
42
|
-
end
|
43
|
-
|
44
|
-
def validate_os
|
45
|
-
return if @appliance_definition['os'].nil?
|
46
|
-
|
47
|
-
raise ApplianceValidationError, "Unsupported OS: operating system '#{@appliance_definition['os']['name']}' is not supported. Supported OS types: #{SUPPORTED_OSES.keys.join(", ")}. Please correct your appliance definition file, thanks." if !@appliance_definition['os']['name'].nil? and !SUPPORTED_OSES.keys.include?( @appliance_definition['os']['name'] )
|
48
|
-
|
49
|
-
#unless @appliance_definition['os']['version'].nil?
|
50
|
-
#@appliance_definition['os']['version'] = @appliance_definition['os']['version'].to_s
|
51
|
-
#raise ApplianceValidationError, "Not valid OS version: operating system version '#{@appliance_definition['os']['version']}' is not supported for OS type '#{@appliance_definition['os']['name']}'. Supported OS versions for this OS type are: #{SUPPORTED_OSES[@appliance_definition['os']['name']].join(", ")}. Please correct your definition file '#{@appliance_definition_file}', thanks" if !SUPPORTED_OSES[@appliance_definition['os']['name'].nil? ? APPLIANCE_DEFAULTS[:os][:name] : @appliance_definition['os']['name']].include?( @appliance_definition['os']['version'] )
|
52
|
-
#end
|
53
|
-
end
|
54
|
-
|
55
|
-
def validate_hardware
|
56
|
-
return if @appliance_definition['hardware'].nil?
|
57
|
-
|
58
|
-
unless @appliance_definition['hardware']['cpus'].nil?
|
59
|
-
raise ApplianceValidationError, "Not valid CPU amount: '#{@appliance_definition['hardware']['cpus']}' is not allowed here. Please correct your appliance definition file, thanks." if @appliance_definition['hardware']['cpus'] =~ /\d/
|
60
|
-
raise ApplianceValidationError, "Not valid CPU amount: Too many or too less CPU's: '#{@appliance_definition['hardware']['cpus']}'. Please choose from 1-4. Please correct your appliance definition file, thanks." unless @appliance_definition['hardware']['cpus'] >= 1 and @appliance_definition['hardware']['cpus'] <= 4
|
61
|
-
end
|
62
|
-
|
63
|
-
unless @appliance_definition['hardware']['memory'].nil?
|
64
|
-
raise ApplianceValidationError, "Not valid memory amount: '#{@appliance_definition['hardware']['memory']}' is wrong value. Please correct your appliance definition file, thanks." if @appliance_definition['hardware']['memory'] =~ /\d/
|
65
|
-
raise ApplianceValidationError, "Not valid memory amount: '#{@appliance_definition['hardware']['memory']}' is not allowed here. Memory should be a multiplicity of 64. Please correct your appliance definition file, thanks." if (@appliance_definition['hardware']['memory'].to_i % 64 > 0)
|
66
|
-
end
|
67
|
-
|
68
|
-
unless @appliance_definition['hardware']['partitions'].nil?
|
69
|
-
raise ApplianceValidationError, "Not valid partitions format: Please correct your appliance definition file, thanks." unless @appliance_definition['hardware']['partitions'].class.eql?(Array)
|
70
|
-
|
71
|
-
for partition in @appliance_definition['hardware']['partitions']
|
72
|
-
raise ApplianceValidationError, "Not valid partition format: '#{partition}' is wrong value. Please correct your appliance definition file, thanks." unless partition.class.eql?(Hash)
|
73
|
-
raise ApplianceValidationError, "Not valid partition format: Keys 'root' and 'size' should be specified for every partition. Please correct your appliance definition file, thanks." if !partition.keys.include?("root") or !partition.keys.include?("size")
|
74
|
-
raise ApplianceValidationError, "Not valid partition size: '#{partition['size']}' is not a valid value. Please correct your appliance definition file, thanks." if partition['size'] =~ /\d/ or partition['size'].to_i < 1
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def validate_repos
|
80
|
-
return if @appliance_definition['repos'].nil?
|
81
|
-
raise ApplianceValidationError, "Not valid repos format: Please correct your appliance definition file, thanks." unless @appliance_definition['repos'].class.eql?(Array)
|
82
|
-
|
83
|
-
for repo in @appliance_definition['repos']
|
84
|
-
raise ApplianceValidationError, "Not valid repo format: '#{repo}' is wrong value. Please correct your appliance definition file, thanks." unless repo.class.eql?(Hash)
|
85
|
-
raise ApplianceValidationError, "Not valid repo format: Please specify name for repository. Please correct your appliance definition file, thanks." unless repo.keys.include?('name')
|
86
|
-
raise ApplianceValidationError, "Not valid repo format: There is no 'mirrorlist' or 'baseurl' specified for '#{repo['name']}' repository. Please correct your appliance definition file, thanks." unless repo.keys.include?('mirrorlist') or repo.keys.include?('baseurl')
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|