aeolus-image 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ require 'imagefactory'
2
+
3
+ module Aeolus
4
+ module Image
5
+ class BuildCommand < BaseCommand
6
+ attr_accessor :console
7
+ def initialize(opts={}, logger=nil)
8
+ super(opts, logger)
9
+ default = {
10
+ :template_str => '',
11
+ :template => '',
12
+ :target => [],
13
+ :image => '',
14
+ :build => ''
15
+ }
16
+ @options = default.merge(@options)
17
+ @console = ImageFactoryConsole.new()
18
+ @console.start
19
+ end
20
+
21
+ def run
22
+ if combo_implemented?
23
+ @options[:template_str] = read_file(@options[:template])
24
+ if @options[:template_str].nil?
25
+ puts "Cannot find specified file"
26
+ quit(1)
27
+ end
28
+
29
+ # Validate XML against TDL Schema
30
+ errors = validate_xml_document(File.dirname(__FILE__) + "/../examples/tdl.rng", @options[:template_str])
31
+ if errors.length > 0
32
+ puts "ERROR: The given Template does not conform to the TDL Schema, see below for specific details:"
33
+ errors.each do |error|
34
+ puts "- " + error.message
35
+ end
36
+ quit(1)
37
+ end
38
+
39
+ #This is a temporary hack in case the agent doesn't show up on bus immediately
40
+ sleep(5)
41
+ @console.build(@options[:template_str], @options[:target], @options[:image], @options[:build]).each do |adaptor|
42
+ puts ""
43
+ puts "Target Image: #{adaptor.image_id}"
44
+ puts "Image: #{adaptor.image}"
45
+ puts "Build: #{adaptor.build}"
46
+ puts "Status: #{adaptor.status}"
47
+ puts "Percent Complete: #{adaptor.percent_complete}"
48
+ end
49
+ quit(0)
50
+ end
51
+ end
52
+
53
+ def combo_implemented?
54
+ if @options[:template].empty? || @options[:target].empty?
55
+ puts "This combination of parameters is not currently supported"
56
+ quit(1)
57
+ end
58
+ true
59
+ end
60
+
61
+ def quit(code)
62
+ @console.shutdown
63
+ super
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,212 @@
1
+ require 'optparse'
2
+ require 'logger'
3
+ require 'base_command'
4
+ require 'list_command'
5
+ require 'build_command'
6
+ require 'push_command'
7
+ require 'import_command'
8
+ require 'delete_command'
9
+
10
+ module Aeolus
11
+ module Image
12
+ class ConfigParser
13
+ COMMANDS = %w(list build push import delete)
14
+ attr_accessor :options, :command, :args
15
+
16
+ def initialize(argv)
17
+ @args = argv
18
+ # Default options
19
+ @options = {}
20
+ parse
21
+ end
22
+
23
+ def process
24
+ # Check for command, then call appropriate Optionparser and initiate
25
+ # call to that class.
26
+ @command = @args.shift
27
+ # Eventually get the config file from user dir if it exists.
28
+ # File.expand_path("~")
29
+ if COMMANDS.include?(@command)
30
+ self.send(@command.to_sym)
31
+ else
32
+ @args << "-h"
33
+ puts "Valid command required: \n\n"
34
+ parse
35
+ end
36
+ end
37
+
38
+ private
39
+ def parse
40
+ @optparse ||= OptionParser.new do|opts|
41
+ opts.banner = "Usage: aeolus-image [#{COMMANDS.join('|')}] [general options] [command options]"
42
+
43
+ opts.separator ""
44
+ opts.separator "General options:"
45
+ opts.on('-u', '--user USERNAME', 'Conductor username') do |user|
46
+ @options[:user] = user
47
+ end
48
+ opts.on('-w', '--password PASSWORD', 'Conductor password') do |pw|
49
+ @options[:password] = pw
50
+ end
51
+ opts.on('-d', '--id ID', 'id for a given object') do |id|
52
+ @options[:id] = id
53
+ end
54
+ opts.on('-r', '--description NAME', 'description (e.g. "<image><name>MyImage</name></image>" or "/home/user/myImage.xml")') do |description|
55
+ @options[:description] = description
56
+ end
57
+ opts.on('-r', '--provider NAME1,NAME2', Array,'name of specific provider (ie ec2-us-east1)') do |name|
58
+ @options[:provider] = name
59
+ end
60
+ opts.on('-I', '--image ID', 'ID of the base image, can be used in build and push commands, see examples') do |id|
61
+ @options[:image] = id
62
+ end
63
+ opts.on('-T', '--target TARGET1,TARGET2', Array, 'provider type (ec2, rackspace, rhevm, etc)') do |name|
64
+ @options[:target] = name
65
+ end
66
+ opts.on('-d', '--daemon', 'run as a background process') do
67
+ @options[:subcommand] = :images
68
+ end
69
+ opts.on( '-h', '--help', 'Get usage information for this tool') do
70
+ puts opts
71
+ exit(0)
72
+ end
73
+
74
+ opts.separator ""
75
+ opts.separator "List options:"
76
+ opts.on('-i', '--images', 'Retrieve a list of images') do
77
+ @options[:subcommand] = :images
78
+ end
79
+ opts.on('-b', '--builds ID', 'Retrieve the builds of an image') do |id|
80
+ @options[:subcommand] = :builds
81
+ @options[:id] = id
82
+ end
83
+ opts.on('-t', '--targetimages ID', 'Retrieve the target images from a build') do |id|
84
+ @options[:subcommand] = :targetimages
85
+ @options[:id] = id
86
+ end
87
+ opts.on('-P', '--providerimages ID', 'Retrieve the provider images from a target image') do |id|
88
+ @options[:subcommand] = :targetimages
89
+ @options[:id] = id
90
+ end
91
+ opts.on('-g', '--targets', 'Retrieve the values available for the --target parameter') do
92
+ @options[:subcommand] = :targets
93
+ end
94
+ opts.on('-p', '--providers', 'Retrieve the values available for the --provider parameter') do
95
+ @options[:subcommand] = :providers
96
+ end
97
+ opts.on('-a', '--accounts', 'Retrieve the values available for the --account parameter') do
98
+ @options[:subcommand] = :accounts
99
+ end
100
+
101
+ opts.separator ""
102
+ opts.separator "Build options:"
103
+ opts.on('-e', '--template FILE', 'path to file that contains template xml') do |file|
104
+ @options[:template] = file
105
+ end
106
+
107
+ opts.separator ""
108
+ opts.separator "Push options:"
109
+ opts.on('-B', '--build ID', 'push all target images for a build, to same providers as previously') do |id|
110
+ @options[:build] = id
111
+ end
112
+ opts.on('-A', '--account NAME', 'name of specific provider account to use for push') do |name|
113
+ @options[:account] = name
114
+ end
115
+
116
+ opts.separator ""
117
+ opts.separator "Delete options:"
118
+ opts.on('-m', '--targetimage ID', 'delete target image and its provider images') do |id|
119
+ @options[:targetimage] = id
120
+ end
121
+ opts.on('-D', '--providerimage ID', 'delete provider image') do |id|
122
+ @options[:providerimage] = id
123
+ end
124
+
125
+ opts.separator ""
126
+ opts.separator "List Examples:"
127
+ opts.separator "aeolus-image list --images # list available images"
128
+ opts.separator "aeolus-image list --builds $image_id # list the builds of an image"
129
+ opts.separator "aeolus-image list --targetimages $build_id # list the target images from a build"
130
+ opts.separator "aeolus-image list --targets # list the values available for the --target parameter"
131
+ opts.separator "aeolus-image list --providers # list the values available for the --provider parameter"
132
+ opts.separator "aeolus-image list --accounts # list the values available for the --account parameter"
133
+
134
+ opts.separator ""
135
+ opts.separator "Build examples:"
136
+ opts.separator "aeolus-image build --target ec2 --template my.tmpl # build a new image for ec2 from the template"
137
+ opts.separator "aeolus-image build --image $image_id # (NOT IMPLEMENTED) rebuild the image template and targets from latest build"
138
+ opts.separator %q{aeolus-image build --target ec2,rackspace \ # rebuild the image with a new template and set of targets
139
+ --image $image_i \
140
+ --template my.tmpl}
141
+
142
+ opts.separator ""
143
+ opts.separator "Push examples:"
144
+ opts.separator "aeolus-image push --provider ec2-us-east-1 --id $image_id # initial push of an image build via image id to the specified provider"
145
+ opts.separator "aeolus-image push --build $build_id # push an image build via build id to the specified provider"
146
+ opts.separator "aeolus-image push --account $provider_account --build $build_id # (NOT IMPLEMENTED) ditto, using a specific provider account"
147
+ opts.separator "aeolus-image push --image $image_id # (NOT IMPLEMENTED) push all the target images for the latest build"
148
+
149
+ opts.separator ""
150
+ opts.separator "Import examples:"
151
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id # import an AMI from the specified provider"
152
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description '<image><name>My Image</name></image>' # import an AMI from the specified provider"
153
+ opts.separator "aeolus-image import --provider ec2-us-east-1 --target ec2 --id $ami_id --description <path_to_xml_file> # import an AMI from the specified provider"
154
+
155
+ opts.separator ""
156
+ opts.separator "Delete examples: (DELETE CURRENTLY NOT IMPLEMENTED) "
157
+ opts.separator "aeolus-image delete --build $build_id # deletes a build, updating latest/parent references as appropriate"
158
+ opts.separator "aeolus-image delete --targetimage $target_image # deletes a target image and its provider images"
159
+ opts.separator "aeolus-image delete --providerimage $provider_image # deletes a provider image"
160
+ end
161
+
162
+ begin
163
+ @optparse.parse!(@args)
164
+ rescue OptionParser::InvalidOption
165
+ puts "Warning: Invalid option"
166
+ exit(1)
167
+ rescue OptionParser::MissingArgument => e
168
+ puts "Warning, #{e.message}"
169
+ exit(1)
170
+ end
171
+ end
172
+
173
+ # TODO: Remove all this boilerplate and replace with some metaprogramming,
174
+ # perhaps method_missing
175
+ def list
176
+ # TODO: Instantiate and call object matching command type, for example:
177
+ # l = ListCommand.new(@options)
178
+ # Each Command will call it's own internal method depending on the contents of the hash.
179
+ # For the list example above, that object would call a method 'images' based on the item
180
+ # @options[:subcommand] being :images, so internally that class may do something like:
181
+ # self.send(@options[:subcommand])
182
+ if @options[:subcommand].nil?
183
+ # TODO: Pull out Print Usage into seporate method, and print
184
+ puts "Could not find subcommand for list, run `./aeolus-image --help` for usage instructions"
185
+ exit(1)
186
+ else
187
+ list_command = ListCommand.new(@options)
188
+ list_command.send(@options[:subcommand])
189
+ end
190
+ end
191
+
192
+ def build
193
+ b = BuildCommand.new(@options)
194
+ b.run
195
+ end
196
+
197
+ def push
198
+ b = PushCommand.new(@options)
199
+ b.run
200
+ end
201
+
202
+ def import
203
+ import_command = ImportCommand.new(@options)
204
+ import_command.import_image
205
+ end
206
+
207
+ def delete
208
+ "Not implemented"
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,9 @@
1
+ module Aeolus
2
+ module Image
3
+ class DeleteCommand < BaseCommand
4
+ def initialize(opts={}, logger=nil)
5
+ super(opts, logger)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ module Aeolus
2
+ module Image
3
+ class ImportCommand < BaseCommand
4
+ def initialize(opts={}, logger=nil)
5
+ super(opts, logger)
6
+ default = {
7
+ :image => '',
8
+ :build => '',
9
+ :id => '',
10
+ :description => '<image><name>' + @options[:id] + '</name></image>',
11
+ :target => '',
12
+ :provider => ''
13
+ }
14
+ @options = default.merge(@options)
15
+ @console = ImageFactoryConsole.new()
16
+ @console.start
17
+ end
18
+
19
+ def import_image
20
+ description = read_file(@options[:description])
21
+ if !description.nil?
22
+ @options[:description] = description
23
+ end
24
+ # TODO: Validate Description XML
25
+
26
+ #This is a temporary hack in case the agent doesn't show up on bus
27
+ #immediately
28
+ sleep(5)
29
+ import_map = @console.import_image(@options[:image], @options[:build], @options[:id], @options[:description], @options[:target].first, @options[:provider].first)
30
+ puts ""
31
+ puts "Target Image: " + import_map['target_image']
32
+ puts "Image: " + import_map['image']
33
+ puts "Build: " + import_map['build']
34
+ puts "Provider Image: " + import_map['provider_image']
35
+ quit(0)
36
+ end
37
+
38
+ def quit(code)
39
+ @console.shutdown
40
+ super
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,141 @@
1
+ module Aeolus
2
+ module Image
3
+ class ListCommand < BaseCommand
4
+ def initialize(opts={}, logger=nil)
5
+ super(opts, logger)
6
+ end
7
+
8
+ def images
9
+ images = [["IMAGE ID", "LASTEST PUSHED BUILD", "NAME", "TARGET", "OS", "OS VERSION", "ARCH", "DESCRIPTION"]]
10
+ doc = Nokogiri::XML iwhd['/target_images'].get
11
+ # Check for any invalid data in iwhd
12
+ invalid_images = []
13
+ doc.xpath("/objects/object/key").each do |targetimage|
14
+ begin
15
+ build = iwhd["/target_images/" + targetimage.text + "/build"].get
16
+ image = iwhd["/builds/" + build + "/image"].get
17
+
18
+ if template_info = get_template_info(image, targetimage.text)
19
+ images << [image] + [lastest_pushed(image)] + template_info
20
+ else
21
+ images << [image] + [lastest_pushed(image)] +[get_image_name(image), iwhd["/target_images/" + targetimage + "/target"].get, "", "", "", ""]
22
+ end
23
+ rescue
24
+ invalid_images << targetimage.text
25
+ end
26
+ end
27
+ format_print(images)
28
+
29
+ unless invalid_images.empty?
30
+ puts "\nN.B. following images were not listed, aeolus-image encountered some invalid data in iwhd:"
31
+ puts invalid_images.join "\n"
32
+ end
33
+ quit(0)
34
+ end
35
+
36
+ def builds
37
+ doc = Nokogiri::XML iwhd['/builds'].get
38
+ doc.xpath("/objects/object/key").each do |build|
39
+ if iwhd['/builds/' + build.text + "/image"].get == @options[:id]
40
+ puts build.text
41
+ end
42
+ end
43
+ quit(0)
44
+ end
45
+
46
+ def targetimages
47
+ doc = Nokogiri::XML iwhd['/target_images'].get
48
+ doc.xpath("/objects/object/key").each do |target_image|
49
+ begin
50
+ if iwhd['/target_images/' + target_image.text + "/build"].get == @options[:id]
51
+ puts target_image.text
52
+ end
53
+ rescue RestClient::ResourceNotFound
54
+ end
55
+ end
56
+ quit(0)
57
+ end
58
+
59
+ def targets
60
+ targets = [["NAME", "TARGET CODE"]]
61
+ targets << ["Mock", "mock"]
62
+ targets << ["Amazon EC2", "ec2"]
63
+ targets << ["RHEV-M", "rhevm"]
64
+ targets << ["VMware vSphere", "vsphere"]
65
+ targets << ["Condor Cloud", "condor_cloud"]
66
+ format_print(targets)
67
+ quit(0)
68
+ end
69
+
70
+ def providers
71
+ print_values = [["NAME", "TYPE", "URL"]]
72
+
73
+ doc = Nokogiri::XML conductor['/providers'].get
74
+ doc.xpath("/providers/provider").each do |provider|
75
+ print_values << [provider.xpath("name").text, provider.xpath("provider_type").text, provider.xpath("url").text]
76
+ end
77
+
78
+ format_print(print_values)
79
+ quit(0)
80
+ end
81
+
82
+ def accounts
83
+ print_values = [["NAME", "PROVIDER", "PROVIDER TYPE"]]
84
+ doc = Nokogiri::XML conductor['/provider_accounts/'].get
85
+ doc.xpath("/provider_accounts/provider_account").each do |account|
86
+ print_values << [account.xpath("name").text, account.xpath("provider").text, account.xpath("provider_type").text]
87
+ end
88
+
89
+ format_print(print_values)
90
+ quit(0)
91
+ end
92
+
93
+ private
94
+ # Takes a 2D array of strings and neatly prints them to STDOUT
95
+ def format_print(print_values)
96
+ widths = Array.new(print_values[0].size, 0)
97
+ print_values.each do |print_value|
98
+ widths = widths.zip(print_value).map! {|width, value| value.length > width ? value.length : width }
99
+ end
100
+
101
+ print_values.each do |print_value|
102
+ widths.zip(print_value) do |width, value|
103
+ printf("%-#{width + 5}s", value)
104
+ end
105
+ puts ""
106
+ end
107
+ end
108
+
109
+ def get_template_info(image, targetimage)
110
+ begin
111
+ template = Nokogiri::XML iwhd["/templates/" + iwhd["/target_images/" + targetimage + "/template"].get].get
112
+ [template.xpath("/template/name").text,
113
+ iwhd["/target_images/" + targetimage + "/target"].get,
114
+ template.xpath("/template/os/name").text,
115
+ template.xpath("/template/os/version").text,
116
+ template.xpath("/template/os/arch").text,
117
+ template.xpath("/template/description").text]
118
+ rescue
119
+ end
120
+ end
121
+
122
+ def get_image_name(image)
123
+ begin
124
+ template_xml = Nokogiri::XML iwhd["images/" + image].get
125
+ template_xml.xpath("/image/name").text
126
+ rescue
127
+ ""
128
+ end
129
+ end
130
+
131
+ def lastest_pushed(image)
132
+ begin
133
+ build = iwhd["/images/" + image + "/latest_build"].get
134
+ build.nil? ? "" : build
135
+ rescue
136
+ ""
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end