nifty 0.0.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +79 -12
- data/bin/nifty +2 -0
- data/config/nifty.yml +18 -0
- data/config/templates/image.erb +38 -0
- data/config/templates/template.erb +26 -0
- data/lib/nifty/backend.rb +55 -0
- data/lib/nifty/backends/opennebula.rb +109 -0
- data/lib/nifty/backends/utils/opennebula/datastore_handler.rb +36 -0
- data/lib/nifty/backends/utils/opennebula/group_handler.rb +23 -0
- data/lib/nifty/backends/utils/opennebula/handler.rb +60 -0
- data/lib/nifty/backends/utils/opennebula/helper.rb +44 -0
- data/lib/nifty/backends/utils/opennebula/image_handler.rb +209 -0
- data/lib/nifty/backends/utils/opennebula/template_handler.rb +91 -0
- data/lib/nifty/backends/utils/opennebula.rb +9 -0
- data/lib/nifty/backends/utils.rb +2 -0
- data/lib/nifty/backends.rb +5 -0
- data/lib/nifty/command_executioner.rb +202 -0
- data/lib/nifty/errors/api_call_timeout_error.rb +1 -0
- data/lib/nifty/errors/argument_error.rb +1 -0
- data/lib/nifty/errors/backend_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/authentication_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/resource_not_found_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/resource_retrieval_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/resource_state_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/stub_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula/user_not_authorized_error.rb +1 -0
- data/lib/nifty/errors/backends/opennebula.rb +5 -0
- data/lib/nifty/errors/backends/opennebula_error.rb +1 -0
- data/lib/nifty/errors/backends.rb +4 -0
- data/lib/nifty/errors/command_execution_error.rb +1 -0
- data/lib/nifty/errors/event/converter_error.rb +1 -0
- data/lib/nifty/errors/event/loader_error.rb +1 -0
- data/lib/nifty/errors/event.rb +5 -0
- data/lib/nifty/errors/events/event_error.rb +1 -0
- data/lib/nifty/errors/events/expiration_event_error.rb +1 -0
- data/lib/nifty/errors/events/registration_event_error.rb +1 -0
- data/lib/nifty/errors/events.rb +4 -0
- data/lib/nifty/errors/standard_error.rb +1 -0
- data/lib/nifty/errors/transfer_method_error.rb +1 -0
- data/lib/nifty/errors/transfer_methods/destination_not_directory_error.rb +1 -0
- data/lib/nifty/errors/transfer_methods/destination_not_writable_error.rb +1 -0
- data/lib/nifty/errors/transfer_methods/image_file_not_readable_error.rb +1 -0
- data/lib/nifty/errors/transfer_methods.rb +4 -0
- data/lib/nifty/errors.rb +10 -0
- data/lib/nifty/event/converter.rb +29 -0
- data/lib/nifty/event/loader.rb +27 -0
- data/lib/nifty/event/processor.rb +96 -0
- data/lib/nifty/event.rb +6 -0
- data/lib/nifty/events/event.rb +32 -0
- data/lib/nifty/events/opennebula/event.rb +23 -0
- data/lib/nifty/events/opennebula/expiration_event.rb +32 -0
- data/lib/nifty/events/opennebula/metadata_update_event.rb +12 -0
- data/lib/nifty/events/opennebula/registration_event.rb +113 -0
- data/lib/nifty/events/opennebula/utils/events_common.rb +69 -0
- data/lib/nifty/events/opennebula/utils.rb +4 -0
- data/lib/nifty/events/opennebula.rb +6 -0
- data/lib/nifty/events.rb +5 -0
- data/lib/nifty/exit_codes.rb +8 -0
- data/lib/nifty/settings.rb +18 -0
- data/lib/nifty/transfer_method.rb +69 -0
- data/lib/nifty/transfer_methods/opennebula/cp.rb +52 -0
- data/lib/nifty/transfer_methods/opennebula/noop.rb +27 -0
- data/lib/nifty/transfer_methods/opennebula.rb +4 -0
- data/lib/nifty/transfer_methods.rb +4 -0
- data/lib/nifty/version.rb +1 -1
- data/lib/nifty.rb +20 -2
- data/nifty.gemspec +7 -0
- data/schema/appliance.json +295 -0
- data/templates/image.erb +8 -0
- data/templates/template.erb +13 -0
- metadata +166 -3
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'erb'
|
3
|
+
require 'tilt/erb'
|
4
|
+
|
5
|
+
# Handler for OpenNebula ImagePool
|
6
|
+
#
|
7
|
+
# @author Michal Kimle
|
8
|
+
class Nifty::Backends::Utils::Opennebula::ImageHandler < Nifty::Backends::Utils::Opennebula::Handler
|
9
|
+
|
10
|
+
IMAGE_STATE_READY = 'READY'
|
11
|
+
IMAGE_STATE_DISABLED = 'DISABLED'
|
12
|
+
IMAGE_STATE_USED = 'USED'
|
13
|
+
IMAGE_STATE_ERROR = 'ERROR'
|
14
|
+
TIME_FORMAT = '%Y%m%d%H%M%S'
|
15
|
+
ATTRIBUTE_EXPIRATION = 'NIFTY_EXPIRATION'
|
16
|
+
ATTRIBUTE_OUTDATED = 'NIFTY_OUTDATED'
|
17
|
+
ATTRIBUTE_APPLIANCE_VERSION = 'NIFTY_APPLIANCE_VERSION'
|
18
|
+
|
19
|
+
# Constructor
|
20
|
+
#
|
21
|
+
# @see Nifty::Backends::Utils::Opennebula::Handler#initialize
|
22
|
+
def initialize(client)
|
23
|
+
super(client)
|
24
|
+
@pool = OpenNebula::ImagePool.new(client)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns all images for given appliance id
|
28
|
+
#
|
29
|
+
# @param [String] appliance_id
|
30
|
+
# @return [Array] array of images
|
31
|
+
def images(appliance_id)
|
32
|
+
reload!
|
33
|
+
|
34
|
+
pool.find_all { |image| image["TEMPLATE/#{ATTRIBUTE_APPLIANCE_ID}"] == appliance_id }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns all expired images (have attribute NIFTY_EXPIRATION)
|
38
|
+
#
|
39
|
+
# @return [Array] array of expired images
|
40
|
+
def expired_images()
|
41
|
+
reload!
|
42
|
+
|
43
|
+
pool.find_all { |image| image["TEMPLATE/#{ATTRIBUTE_EXPIRATION}"] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def images_by_version(version)
|
47
|
+
reload!
|
48
|
+
|
49
|
+
pool.find_all { |image| image["TEMPLATE/#{ATTRIBUTE_APPLIANCE_VERSION}"] == version }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns image with given id if exists
|
53
|
+
#
|
54
|
+
# @param [Fixnum] id
|
55
|
+
# @return [OpenNebula::Image] image with given id
|
56
|
+
def image_exist?(id)
|
57
|
+
reload!
|
58
|
+
|
59
|
+
pool.find { |image| image.id == id }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Deletes image
|
63
|
+
#
|
64
|
+
# @param [OpenNebula::Image] image
|
65
|
+
# @raise [Nifty::Errors::ApiCallTimeoutError] if image isn't deleted within a timeout
|
66
|
+
def delete_image(image)
|
67
|
+
id = image.id
|
68
|
+
|
69
|
+
if image.state_str == IMAGE_STATE_USED
|
70
|
+
logger.warn("Image with id #{id.inspect} cannot be removed, still in use")
|
71
|
+
return
|
72
|
+
end
|
73
|
+
|
74
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
75
|
+
logger.debug("Deleting image with id #{id.inspect}")
|
76
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.delete }
|
77
|
+
|
78
|
+
Timeout::timeout(Nifty::API_CALL_TIMEOUT) do
|
79
|
+
while(image_exist?(id))
|
80
|
+
sleep(Nifty::API_POLLING_WAIT)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Timeout::Error
|
84
|
+
fail Nifty::Errors::ApiCallTimeoutError, "Image with id #{id.inspect} was not deleted within timeout"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Disables image
|
88
|
+
#
|
89
|
+
# @param [OpenNebula::Image] image
|
90
|
+
# @raise [Nifty::Errors::ApiCallTimeoutError] if image isn't disabled within a timeout
|
91
|
+
def disable_image(image)
|
92
|
+
image_state = image.state_str
|
93
|
+
id = image.id
|
94
|
+
|
95
|
+
if image_state == IMAGE_STATE_DISABLED
|
96
|
+
logger.debug("Image with id #{id.inspect} is already disabled, skipping")
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
unless image_state == IMAGE_STATE_READY || image_state == IMAGE_STATE_ERROR
|
101
|
+
logger.warn("Image with id #{id.inspect} cannot be disabled")
|
102
|
+
return
|
103
|
+
end
|
104
|
+
|
105
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.disable }
|
106
|
+
|
107
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
108
|
+
Timeout::timeout(Nifty::API_CALL_TIMEOUT) do
|
109
|
+
until(image.state_str == IMAGE_STATE_DISABLED)
|
110
|
+
sleep(Nifty::API_POLLING_WAIT)
|
111
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Expires image
|
117
|
+
# Renames image and add attribute NIFTY_EXPIRATION to image with timestamp as a value
|
118
|
+
#
|
119
|
+
# @param [OpenNebula::Image] image
|
120
|
+
def expire_image(image)
|
121
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
122
|
+
id = image.id
|
123
|
+
|
124
|
+
if image["TEMPLATE/#{ATTRIBUTE_EXPIRATION}"]
|
125
|
+
logger.debug("Image with id #{id.inspect} is already expired, skipping")
|
126
|
+
return
|
127
|
+
end
|
128
|
+
|
129
|
+
disable_image(image)
|
130
|
+
|
131
|
+
logger.debug("Expiring image with id #{id.inspect}")
|
132
|
+
|
133
|
+
expiration_time = Time.now.strftime(TIME_FORMAT)
|
134
|
+
expiration_attribute = "#{ATTRIBUTE_EXPIRATION} = \"#{expiration_time}\""
|
135
|
+
|
136
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.rename("EXPIRED_#{expiration_time}_#{image.name}") }
|
137
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.update(expiration_attribute, true) }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Outdates image
|
141
|
+
# Adds atribute marking the image is outdated
|
142
|
+
#
|
143
|
+
# @param [OpenNebula::Image] image
|
144
|
+
def outdate_image(image)
|
145
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
146
|
+
|
147
|
+
id = image.id
|
148
|
+
|
149
|
+
if image["TEMPLATE/#{ATTRIBUTE_OUTDATED}"]
|
150
|
+
logger.debug("Image with id #{id.inspect} is already outdated, skipping")
|
151
|
+
return
|
152
|
+
end
|
153
|
+
|
154
|
+
logger.debug("Outdating image with id #{id.inspect}")
|
155
|
+
|
156
|
+
outdate_time = Time.now.strftime(TIME_FORMAT)
|
157
|
+
outdate_attribute = "#{ATTRIBUTE_OUTDATED} = \"#{outdate_time}\""
|
158
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.update(outdate_attribute, true) }
|
159
|
+
end
|
160
|
+
|
161
|
+
# Registers a new image
|
162
|
+
#
|
163
|
+
# @param [String] template template for image
|
164
|
+
# @param [OpenNebula::Datastore] datastore datastore to register image to
|
165
|
+
# @raise [Nifty::Errors::ApiCallTimeoutError] if image isn't ready within a timeout
|
166
|
+
def register_image(template, datastore)
|
167
|
+
image_alloc = ::OpenNebula::Image.build_xml
|
168
|
+
image = ::OpenNebula::Image.new(image_alloc, client)
|
169
|
+
|
170
|
+
logger.debug("Registering image with template\n#{template}")
|
171
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.allocate(template, datastore.id) }
|
172
|
+
|
173
|
+
begin
|
174
|
+
Timeout::timeout(Nifty::API_CALL_TIMEOUT) do
|
175
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
176
|
+
|
177
|
+
until(image.state_str == IMAGE_STATE_READY)
|
178
|
+
sleep(Nifty::API_POLLING_WAIT)
|
179
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { image.info! }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
image
|
184
|
+
rescue Timeout::Error
|
185
|
+
image.delete
|
186
|
+
fail Nifty::Errors::ApiCallTimeoutError, "Image with id #{image.id.inspect} didn't become ready within timeout"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Prepares a template for an image
|
191
|
+
#
|
192
|
+
# @param [String] template_dir directory with templates
|
193
|
+
# @param [Hash] data used while populating a template
|
194
|
+
# @return [String] final template for an image
|
195
|
+
def self.prepare_template(template_dir, data)
|
196
|
+
template_location = File.join(template_dir, "image.erb")
|
197
|
+
fail Nifty::Errors::ArgumentError, "Missing file 'image.erb' in template directory '#{template_dir}'" unless File.exist?(template_location)
|
198
|
+
|
199
|
+
template = Tilt::ERBTemplate.new(template_location)
|
200
|
+
template_content = template.render(Object.new, data)
|
201
|
+
|
202
|
+
template = Tilt::ERBTemplate.new(File.join(Nifty::GEM_DIR, 'templates', 'image.erb'))
|
203
|
+
nifty_template_content = template.render(Object.new, data)
|
204
|
+
|
205
|
+
whole_template_content = template_content + nifty_template_content
|
206
|
+
logger.debug "Populated image template:\n#{whole_template_content}"
|
207
|
+
whole_template_content
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'erb'
|
3
|
+
require 'tilt/erb'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
# Handler for OpenNebula TemplatePool
|
7
|
+
#
|
8
|
+
# @author Michal Kimle
|
9
|
+
class Nifty::Backends::Utils::Opennebula::TemplateHandler < Nifty::Backends::Utils::Opennebula::Handler
|
10
|
+
|
11
|
+
# Constructor
|
12
|
+
#
|
13
|
+
# @see Nifty::Backends::Utils::Opennebula::Handler#initialize
|
14
|
+
def initialize(client)
|
15
|
+
super(client)
|
16
|
+
@pool = OpenNebula::TemplatePool.new(client)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns all templates for given appliance id
|
20
|
+
#
|
21
|
+
# @param [String] appliance_id
|
22
|
+
# @return [Array] array of templates
|
23
|
+
def templates(appliance_id)
|
24
|
+
reload!
|
25
|
+
|
26
|
+
pool.find_all{ |template| template["TEMPLATE/#{ATTRIBUTE_APPLIANCE_ID}"] == appliance_id }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns template with given id if exists
|
30
|
+
#
|
31
|
+
# @param [Fixnum] id
|
32
|
+
# @return [OpenNebula::Template] template with given id
|
33
|
+
def template_exist?(id)
|
34
|
+
reload!
|
35
|
+
|
36
|
+
pool.find { |template| template.id == id }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Deletes template
|
40
|
+
#
|
41
|
+
# @param [OpenNebula::Template] template
|
42
|
+
# @raise [Nifty::Errors::ApiCallTimeoutError] if template isn't deleted within a timeout
|
43
|
+
def delete_template(template)
|
44
|
+
template.info!
|
45
|
+
id = template.id
|
46
|
+
|
47
|
+
logger.debug("Deleting template with id #{id.inspect}")
|
48
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { template.delete }
|
49
|
+
|
50
|
+
Timeout::timeout(Nifty::API_CALL_TIMEOUT) do
|
51
|
+
while(template_exist?(id))
|
52
|
+
sleep(Nifty::API_POLLING_WAIT)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue Timeout::Error
|
56
|
+
fail Nifty::Errors::ApiCallTimeoutError, "Template with id #{template.id.inspect} was not deleted within timeout"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Registers a new template
|
60
|
+
#
|
61
|
+
# @param [String] template_template template for template
|
62
|
+
def register_template(template_template)
|
63
|
+
template_alloc = ::OpenNebula::Template.build_xml
|
64
|
+
template = ::OpenNebula::Template.new(template_alloc, client)
|
65
|
+
|
66
|
+
logger.debug("Registering template with template\n#{template_template}")
|
67
|
+
Nifty::Backends::Utils::Opennebula::Helper.handle_opennebula_error { template.allocate(template_template) }
|
68
|
+
|
69
|
+
template
|
70
|
+
end
|
71
|
+
|
72
|
+
# Prepares a template for a template
|
73
|
+
#
|
74
|
+
# @param [String] template_dir directory with templates
|
75
|
+
# @param [Hash] data used while populating a template
|
76
|
+
# @return [String] final template for a template
|
77
|
+
def self.prepare_template(template_dir, data)
|
78
|
+
template_location = File.join(template_dir, "template.erb")
|
79
|
+
fail Nifty::Errors::ArgumentError, "Missing file 'template.erb' in template directory '#{template_dir}'" unless File.exist?(template_location)
|
80
|
+
|
81
|
+
template = Tilt::ERBTemplate.new(template_location)
|
82
|
+
template_content = template.render(Object.new, data)
|
83
|
+
|
84
|
+
template = Tilt::ERBTemplate.new(File.join(Nifty::GEM_DIR, 'templates', 'template.erb'))
|
85
|
+
nifty_template_content = template.render(Object.new, data)
|
86
|
+
|
87
|
+
whole_template_content = template_content + nifty_template_content
|
88
|
+
logger.debug "Populated template template:\n#{whole_template_content}"
|
89
|
+
whole_template_content
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Namespace for OpenNebula's backend utilities
|
2
|
+
module Nifty::Backends::Utils::Opennebula; end
|
3
|
+
|
4
|
+
require 'nifty/backends/utils/opennebula/helper'
|
5
|
+
require 'nifty/backends/utils/opennebula/handler'
|
6
|
+
require 'nifty/backends/utils/opennebula/group_handler'
|
7
|
+
require 'nifty/backends/utils/opennebula/image_handler'
|
8
|
+
require 'nifty/backends/utils/opennebula/template_handler'
|
9
|
+
require 'nifty/backends/utils/opennebula/datastore_handler'
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# Namespace for all the backends
|
2
|
+
module Nifty::Backends
|
3
|
+
require File.join(File.dirname(__FILE__), self.name.demodulize.underscore, 'utils')
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), self.name.demodulize.underscore, '*.rb')) { |backend_file| require backend_file.chomp('.rb') }
|
5
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yell'
|
3
|
+
|
4
|
+
class Nifty::CommandExecutioner < Thor
|
5
|
+
class << self
|
6
|
+
# Force Thor to exit with a non-zero return value on failure (after `exit` is called).
|
7
|
+
#
|
8
|
+
# @return [TrueClass, FalseClass] true if thor fail, false otherwise
|
9
|
+
def exit_on_failure?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
# List all available transfer methods for the backend
|
14
|
+
#
|
15
|
+
# @param [Nifty::Backend] backend
|
16
|
+
# @return [Array] array of transfer method classes that support the backend
|
17
|
+
def available_transfer_methods(backend)
|
18
|
+
unless Nifty::TransferMethods.constants.include? backend.camelize.to_sym
|
19
|
+
fail Nifty::CommandExecutionError, "No backend with name '#{backend}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
clazz = "Nifty::TransferMethods::#{backend.camelize}".constantize
|
23
|
+
constants = clazz.constants.map { |sym| sym.to_s.downcase }
|
24
|
+
constants.select! do |constant|
|
25
|
+
unknown = clazz.const_get(constant.camelize)
|
26
|
+
|
27
|
+
unknown.respond_to?('transfer_method?') && unknown.transfer_method?
|
28
|
+
end
|
29
|
+
|
30
|
+
constants.sort
|
31
|
+
end
|
32
|
+
|
33
|
+
# List all available backends
|
34
|
+
#
|
35
|
+
# @return [Array] array of all available backend classes
|
36
|
+
def available_backends
|
37
|
+
constants = Nifty::Backends.constants.map { |sym| sym.to_s.downcase }
|
38
|
+
constants.select! do |constant|
|
39
|
+
unknown = Nifty::Backends.const_get(constant.camelize)
|
40
|
+
|
41
|
+
unknown.respond_to?('backend?') && unknown.backend?
|
42
|
+
end
|
43
|
+
|
44
|
+
constants.sort
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# global options
|
49
|
+
class_option :"appliance-dir",
|
50
|
+
:required => true,
|
51
|
+
:default => Nifty::Settings['appliance-dir'],
|
52
|
+
:type => :string,
|
53
|
+
:aliases => '-a',
|
54
|
+
:desc => 'Directory from where appliance descriptors will be loaded'
|
55
|
+
class_option :"transfer-method",
|
56
|
+
:required => true,
|
57
|
+
:default => Nifty::Settings['transfer']['method'],
|
58
|
+
:type => :string,
|
59
|
+
:aliases => '-m',
|
60
|
+
:desc => 'Transfer method for image upload'
|
61
|
+
class_option :"transfer-destination",
|
62
|
+
:default => Nifty::Settings['transfer']['destination'],
|
63
|
+
:type => :string,
|
64
|
+
:aliases => '-t',
|
65
|
+
:desc => 'Image upload destination'
|
66
|
+
class_option :"logging-level",
|
67
|
+
:required => true,
|
68
|
+
:default => Nifty::Settings['logging']['level'],
|
69
|
+
:type => :string,
|
70
|
+
:enum => Yell::Severities
|
71
|
+
class_option :"logging-file",
|
72
|
+
:default => Nifty::Settings['logging']['file'],
|
73
|
+
:type => :string,
|
74
|
+
:desc => 'File to write log to'
|
75
|
+
class_option :debug,
|
76
|
+
:default => Nifty::Settings['debug'],
|
77
|
+
:type => :boolean,
|
78
|
+
:desc => 'Runs nifty in debug mode'
|
79
|
+
|
80
|
+
desc 'version', 'Prints NIFTY\'s version'
|
81
|
+
def version
|
82
|
+
$stdout.puts Nifty::VERSION
|
83
|
+
end
|
84
|
+
|
85
|
+
desc 'backends', 'Lists all available backends with their description'
|
86
|
+
def backends
|
87
|
+
backends = self.class.available_backends
|
88
|
+
longest = backends.map { |backend| backend.length }.sort.last
|
89
|
+
backends.each do |backend|
|
90
|
+
$stdout.puts "%-#{longest + 2}s" % "#{backend}" + "# #{Nifty::Backends.const_get(backend.camelize).description}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
available_backends.each do |backend|
|
95
|
+
backend_clazz = Nifty::Backends.const_get(backend.camelize)
|
96
|
+
options = backend_clazz.options
|
97
|
+
options.each do |name, parameters|
|
98
|
+
parameters[:default] = Nifty::Settings[backend][name.to_s] unless parameters[:default]
|
99
|
+
method_option name, parameters
|
100
|
+
end
|
101
|
+
|
102
|
+
class_eval %Q^
|
103
|
+
desc '#{backend}', 'Runs NIFTY with backend #{backend}'
|
104
|
+
def #{backend}
|
105
|
+
start('#{backend}', options)
|
106
|
+
end
|
107
|
+
|
108
|
+
desc '#{backend}-transfer-methods', 'Lists all available transfer methods with their description for #{backend.inspect} backend'
|
109
|
+
def #{backend}_transfer_methods
|
110
|
+
methods = self.class.available_transfer_methods '#{backend}'
|
111
|
+
longest = methods.map { |method| method.length }.sort.last
|
112
|
+
methods.each do |method|
|
113
|
+
$stdout.puts "%-\#{longest + 2}s" % "\#{method}" + "# \#{Nifty::TransferMethods::#{backend.camelize}.const_get(method.camelize).description}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
^
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Starts NIFTY with selected backend
|
122
|
+
#
|
123
|
+
# @param [String] backend backend's name
|
124
|
+
# @param [Hash] options options from command line
|
125
|
+
def start(backend, options)
|
126
|
+
parameters = options.to_hash.deep_symbolize_keys
|
127
|
+
init_log parameters
|
128
|
+
|
129
|
+
backend = Nifty::Backends.const_get(backend.camelize)
|
130
|
+
logger.debug "Selected backend '#{backend.inspect}'"
|
131
|
+
|
132
|
+
transfer_method = construct_transfer_method(backend, parameters)
|
133
|
+
logger.debug "Selected transfer method '#{transfer_method.inspect}'"
|
134
|
+
|
135
|
+
logger.debug "Parameters: #{parameters}"
|
136
|
+
|
137
|
+
processor = Nifty::Event::Processor.new(backend, transfer_method, parameters)
|
138
|
+
exit processor.process_events
|
139
|
+
end
|
140
|
+
|
141
|
+
# Inits logging according to the settings
|
142
|
+
#
|
143
|
+
# @param [Hash] parameters
|
144
|
+
# @option parameters [String] logging-level
|
145
|
+
# @option parameters [String] logging-file file to log to
|
146
|
+
# @option parameters [TrueClass, FalseClass] debug debug mode
|
147
|
+
# @return [Type] description of returned object
|
148
|
+
def init_log(parameters)
|
149
|
+
parameters[:"logging-level"] = 'DEBUG' if parameters[:debug]
|
150
|
+
|
151
|
+
Yell.new :stdout, :name => Object, :level => parameters[:"logging-level"].downcase, :format => Yell::DefaultFormat
|
152
|
+
Object.send :include, Yell::Loggable
|
153
|
+
|
154
|
+
if parameters[:"logging-file"]
|
155
|
+
unless (File.exist?(parameters[:"logging-file"]) && File.writable?(parameters[:"logging-file"])) || (File.writable?(File.dirname(parameters[:"logging-file"])))
|
156
|
+
logger.error "File #{parameters[:"logging-file"]} isn't writable"
|
157
|
+
return
|
158
|
+
end
|
159
|
+
|
160
|
+
logger.adapter :file, parameters[:"logging-file"]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns class representing transfer method of specified name
|
165
|
+
#
|
166
|
+
# @param [Nifty::Backend] backend
|
167
|
+
# @param [String] transfer_method_name name of the transfer method
|
168
|
+
# @return [Nifty::TransferMethod] transfer method class
|
169
|
+
def transfer_method_constant(backend, transfer_method_name)
|
170
|
+
transfer_methods = "Nifty::TransferMethods::#{backend.name.demodulize}".constantize
|
171
|
+
backend_name = backend.name.demodulize.underscore
|
172
|
+
error_msg = "No such transfer method '#{transfer_method_name}' for backend '#{backend_name}'"
|
173
|
+
unless transfer_methods.constants.include?(transfer_method_name.camelize.to_sym)
|
174
|
+
$stdout.puts error_msg
|
175
|
+
exit Nifty::ExitCodes::NO_TRANSFER_METHOD_ERROR_EXIT_CODE
|
176
|
+
end
|
177
|
+
|
178
|
+
transfer_method = transfer_methods.const_get(transfer_method_name.camelize)
|
179
|
+
unless transfer_method.respond_to?('backend') && transfer_method.backend == backend
|
180
|
+
$stdout.puts error_msg
|
181
|
+
exit
|
182
|
+
end
|
183
|
+
|
184
|
+
transfer_method
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns transfer method according to settings
|
188
|
+
#
|
189
|
+
# @param [Nifty::Backend] backend
|
190
|
+
# @param [Hash] parameters
|
191
|
+
# @option parameters [String] transfer-method name of the transfer method
|
192
|
+
# @option parameters [String] transfer-destination destination fo the transfer method
|
193
|
+
# @return [Nifty::TransferMethod] instance of specified transfer method
|
194
|
+
def construct_transfer_method(backend, parameters)
|
195
|
+
transfer_method_const = transfer_method_constant(backend, parameters[:"transfer-method"])
|
196
|
+
|
197
|
+
transfer_method_const.new(parameters[:"transfer-destination"])
|
198
|
+
rescue Nifty::Errors::TransferMethodError => ex
|
199
|
+
$stdout.puts ex.message
|
200
|
+
exit Nifty::ExitCodes::TRANSFER_METHOD_ERROR_EXIT_CODE
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::ApiCallTimeoutError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::ArgumentError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::BackendError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::AuthenticationError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::ResourceNotFoundError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::ResourceRetrievalError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::ResourceStateError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::StubError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::Opennebula::UserNotAuthorizedError < Nifty::Errors::Backends::OpennebulaError; end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
module Nifty::Errors::Backends::Opennebula
|
2
|
+
require File.join(File.dirname(__FILE__), 'opennebula_error')
|
3
|
+
require File.join(File.dirname(__FILE__), self.name.demodulize.underscore, 'authentication_error')
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), self.name.demodulize.underscore, '*.rb')) { |error_file| require error_file.chomp('.rb') }
|
5
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Backends::OpennebulaError < Nifty::Errors::BackendError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::CommandExecutionError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Event::ConverterError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Event::LoaderError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
module Nifty::Errors::Event
|
2
|
+
require File.join(File.dirname(__FILE__), 'event', 'loader_error')
|
3
|
+
require File.join(File.dirname(__FILE__), 'event', 'converter_error')
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), self.name.demodulize.underscore, '*.rb')) { |error_file| require error_file.chomp('.rb') }
|
5
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Events::EventError < Nifty::Errors::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Events::ExpirationEventError < Nifty::Errors::Events::EventError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::Events::RegistrationEventError < Nifty::Errors::Events::EventError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::StandardError < ::StandardError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::TransferMethodError < Nifty::Errors::ArgumentError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::TransferMethods::DestinationNotDirectoryError < Nifty::Errors::TransferMethodError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::TransferMethods::DestinationNotWritableError < Nifty::Errors::TransferMethodError; end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Nifty::Errors::TransferMethods::ImageFileNotReadableError < Nifty::Errors::TransferMethodError; end
|
data/lib/nifty/errors.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Namespace for all the exceptions
|
2
|
+
module Nifty::Errors
|
3
|
+
require File.join(File.dirname(__FILE__), 'errors', 'standard_error')
|
4
|
+
require File.join(File.dirname(__FILE__), 'errors', 'argument_error')
|
5
|
+
require File.join(File.dirname(__FILE__), 'errors', 'event')
|
6
|
+
require File.join(File.dirname(__FILE__), 'errors', 'backends')
|
7
|
+
require File.join(File.dirname(__FILE__), 'errors', 'transfer_methods')
|
8
|
+
require File.join(File.dirname(__FILE__), 'errors', 'events')
|
9
|
+
Dir.glob(File.join(File.dirname(__FILE__), self.name.demodulize.underscore, '*.rb')) { |error_file| require error_file.chomp('.rb') }
|
10
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Converts appliances into events
|
2
|
+
#
|
3
|
+
# @author Michal Kimle
|
4
|
+
class Nifty::Event::Converter
|
5
|
+
# Converts appliance into event
|
6
|
+
#
|
7
|
+
# @param [Cloud::Appliance::Descriptor::Appliance] appliance
|
8
|
+
# @param [Nifty::Backend] backend
|
9
|
+
# @param [Nifty::TransferMethod] transfer_method
|
10
|
+
# @param [Hash] parameters
|
11
|
+
# @raise [Nifty::Errors::Event::ConverterError] when something goes wrong while conversion
|
12
|
+
# @return [Nifty::Event] description of returned object
|
13
|
+
def self.convert_appliance(appliance, backend, transfer_method, parameters)
|
14
|
+
events_module = "Nifty::Events::#{backend.name.demodulize}".constantize
|
15
|
+
|
16
|
+
logger.debug "Converting appliance #{appliance.inspect}"
|
17
|
+
event_type = "#{appliance.action}_event".camelize
|
18
|
+
|
19
|
+
fail Nifty::Errors::Event::ConverterError, "No such event type for action #{appliance.action.inspect}" unless events_module.constants.include?(event_type.to_sym)
|
20
|
+
event_class = events_module.const_get(event_type)
|
21
|
+
|
22
|
+
fail Nifty::Errors::Event::ConverterError, "Class #{event_class.inspect} doesn't represent an event" unless (event_class.respond_to?('event?') && event_class.event? && event_class.public_instance_methods.include?(:run))
|
23
|
+
|
24
|
+
event = backend.create_event(event_class, appliance, transfer_method, parameters)
|
25
|
+
logger.debug "Converted event: #{event.inspect}"
|
26
|
+
|
27
|
+
event
|
28
|
+
end
|
29
|
+
end
|