aeolus-image 0.0.1

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,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