cloudkeeper-one 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +48 -0
  6. data/.travis.yml +22 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +17 -0
  10. data/README.md +98 -0
  11. data/Rakefile +17 -0
  12. data/bin/cloudkeeper-one +5 -0
  13. data/cloudkeeper-one.gemspec +44 -0
  14. data/config/cloudkeeper-one.yml +23 -0
  15. data/config/templates/image.erb +20 -0
  16. data/config/templates/template.erb +19 -0
  17. data/lib/cloudkeeper/one/appliance_actions/list.rb +68 -0
  18. data/lib/cloudkeeper/one/appliance_actions/registration.rb +73 -0
  19. data/lib/cloudkeeper/one/appliance_actions/removal.rb +57 -0
  20. data/lib/cloudkeeper/one/appliance_actions/update.rb +34 -0
  21. data/lib/cloudkeeper/one/appliance_actions/utils/image_download.rb +43 -0
  22. data/lib/cloudkeeper/one/appliance_actions/utils/template_preparation.rb +36 -0
  23. data/lib/cloudkeeper/one/appliance_actions/utils/templates/attributes.erb +23 -0
  24. data/lib/cloudkeeper/one/appliance_actions/utils.rb +10 -0
  25. data/lib/cloudkeeper/one/appliance_actions.rb +11 -0
  26. data/lib/cloudkeeper/one/cli.rb +185 -0
  27. data/lib/cloudkeeper/one/core_connector.rb +106 -0
  28. data/lib/cloudkeeper/one/errors/actions/action_error.rb +9 -0
  29. data/lib/cloudkeeper/one/errors/actions/listing_error.rb +9 -0
  30. data/lib/cloudkeeper/one/errors/actions/registration_error.rb +9 -0
  31. data/lib/cloudkeeper/one/errors/actions/update_error.rb +9 -0
  32. data/lib/cloudkeeper/one/errors/actions.rb +12 -0
  33. data/lib/cloudkeeper/one/errors/argument_error.rb +7 -0
  34. data/lib/cloudkeeper/one/errors/invalid_configuration_error.rb +7 -0
  35. data/lib/cloudkeeper/one/errors/multi_error.rb +25 -0
  36. data/lib/cloudkeeper/one/errors/network_connection_error.rb +7 -0
  37. data/lib/cloudkeeper/one/errors/opennebula/api_call_timeout_error.rb +9 -0
  38. data/lib/cloudkeeper/one/errors/opennebula/authentication_error.rb +9 -0
  39. data/lib/cloudkeeper/one/errors/opennebula/missing_pool_error.rb +9 -0
  40. data/lib/cloudkeeper/one/errors/opennebula/opennebula_error.rb +9 -0
  41. data/lib/cloudkeeper/one/errors/opennebula/resource_not_found_error.rb +9 -0
  42. data/lib/cloudkeeper/one/errors/opennebula/resource_retrieval_error.rb +9 -0
  43. data/lib/cloudkeeper/one/errors/opennebula/resource_state_error.rb +9 -0
  44. data/lib/cloudkeeper/one/errors/opennebula/stub_error.rb +9 -0
  45. data/lib/cloudkeeper/one/errors/opennebula/user_not_authorized_error.rb +9 -0
  46. data/lib/cloudkeeper/one/errors/opennebula.rb +17 -0
  47. data/lib/cloudkeeper/one/errors/standard_error.rb +7 -0
  48. data/lib/cloudkeeper/one/errors.rb +14 -0
  49. data/lib/cloudkeeper/one/opennebula/appliance_handler.rb +91 -0
  50. data/lib/cloudkeeper/one/opennebula/datastore_handler.rb +16 -0
  51. data/lib/cloudkeeper/one/opennebula/group_handler.rb +12 -0
  52. data/lib/cloudkeeper/one/opennebula/handler.rb +59 -0
  53. data/lib/cloudkeeper/one/opennebula/helper.rb +27 -0
  54. data/lib/cloudkeeper/one/opennebula/image_handler.rb +133 -0
  55. data/lib/cloudkeeper/one/opennebula/tags.rb +34 -0
  56. data/lib/cloudkeeper/one/opennebula/template_handler.rb +24 -0
  57. data/lib/cloudkeeper/one/opennebula.rb +14 -0
  58. data/lib/cloudkeeper/one/settings.rb +21 -0
  59. data/lib/cloudkeeper/one/version.rb +5 -0
  60. data/lib/cloudkeeper/one.rb +19 -0
  61. data/lib/cloudkeeper_grpc.rb +5 -0
  62. metadata +385 -0
@@ -0,0 +1,14 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Errors
4
+ autoload :StandardError, 'cloudkeeper/one/errors/standard_error'
5
+ autoload :ArgumentError, 'cloudkeeper/one/errors/argument_error'
6
+ autoload :NetworkConnectionError, 'cloudkeeper/one/errors/network_connection_error'
7
+ autoload :MultiError, 'cloudkeeper/one/errors/multi_error'
8
+ autoload :InvalidConfigurationError, 'cloudkeeper/one/errors/invalid_configuration_error'
9
+
10
+ autoload :Opennebula, 'cloudkeeper/one/errors/opennebula'
11
+ autoload :Actions, 'cloudkeeper/one/errors/actions'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,91 @@
1
+ require 'timeout'
2
+
3
+ module Cloudkeeper
4
+ module One
5
+ module Opennebula
6
+ class ApplianceHandler < Handler
7
+ attr_reader :identifier
8
+
9
+ LEAVE_ID_AS_IS = -1
10
+ ONEADMIN_ID = 0
11
+
12
+ def initialize
13
+ super
14
+
15
+ @identifier = Cloudkeeper::One::Settings[:identifier]
16
+ end
17
+
18
+ def find_by_appliance_id(appliance_id)
19
+ find_all_by "TEMPLATE/#{Tags::APPLIANCE_ID}" => appliance_id
20
+ end
21
+
22
+ def find_by_image_list_id(image_list_id)
23
+ find_all_by "TEMPLATE/#{Tags::APPLIANCE_IMAGE_LIST_ID}" => image_list_id
24
+ end
25
+
26
+ def delete(element)
27
+ raise Cloudkeeper::One::Errors::ArgumentError, 'element cannot be nil' unless element
28
+
29
+ id = element.id
30
+ handle_opennebula_error { element.delete }
31
+
32
+ timeout { sleep(Cloudkeeper::One::Opennebula::Handler::API_POLLING_WAIT) while exist? id }
33
+ end
34
+
35
+ def chmod(element, permissions)
36
+ raise Cloudkeeper::One::Errors::ArgumentError, 'element cannot be nil' unless element
37
+
38
+ handle_opennebula_error do
39
+ element.chmod_octet permissions
40
+ element.info!
41
+ end
42
+ end
43
+
44
+ def update(element, template)
45
+ raise Cloudkeeper::One::Errors::ArgumentError, 'element cannot be nil' unless element
46
+
47
+ handle_opennebula_error do
48
+ element.update template, true
49
+ element.info!
50
+ end
51
+ end
52
+
53
+ def chgrp(element, group)
54
+ raise Cloudkeeper::One::Errors::ArgumentError, 'element cannot be nil' unless element
55
+
56
+ handle_opennebula_error { element.info! }
57
+ return if group.id == element.gid
58
+
59
+ handle_opennebula_error do
60
+ element.chown(LEAVE_ID_AS_IS, group.id)
61
+ element.info!
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def find_all_by(attributes)
68
+ xpaths = attributes.clone
69
+ xpaths['UNAME'] = Cloudkeeper::One::Settings[:'opennebula-users'] \
70
+ if Cloudkeeper::One::Settings[:'opennebula-users'] && !Cloudkeeper::One::Settings[:'opennebula-users'].empty?
71
+
72
+ find_all xpaths
73
+ end
74
+
75
+ def timeout
76
+ raise Cloudkeeper::One::Errors::ArgumentError, 'Timeout called without a block!' unless block_given?
77
+
78
+ Timeout.timeout(Cloudkeeper::One::Settings[:'opennebula-api-call-timeout']) { yield }
79
+ rescue Timeout::Error
80
+ raise Cloudkeeper::One::Errors::Opennebula::ApiCallTimeoutError, 'Operation was not successful within the timeout'
81
+ end
82
+
83
+ def find(method, xpaths = {})
84
+ reload!
85
+
86
+ pool.send(method) { |element| element["TEMPLATE/#{Tags::ID}"] == identifier && evaluate_xpaths(element, xpaths) }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,16 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ class DatastoreHandler < Handler
5
+ def initialize
6
+ super
7
+ @pool = OpenNebula::DatastorePool.new client
8
+ end
9
+
10
+ def find_by_names(names)
11
+ names.map { |name| find_by_name name }.compact
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ class GroupHandler < Handler
5
+ def initialize
6
+ super
7
+ @pool = OpenNebula::GroupPool.new client
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ class Handler
5
+ include Helper
6
+ attr_reader :client
7
+ attr_accessor :pool
8
+
9
+ API_POLLING_WAIT = 5
10
+
11
+ def initialize
12
+ @client = OpenNebula::Client.new Cloudkeeper::One::Settings[:'opennebula-secret'],
13
+ Cloudkeeper::One::Settings[:'opennebula-endpoint']
14
+ end
15
+
16
+ def find_one(xpaths = {})
17
+ find(:find, xpaths)
18
+ end
19
+
20
+ def find_all(xpaths = {})
21
+ find(:find_all, xpaths)
22
+ end
23
+
24
+ def find_by_name(name)
25
+ find_one('NAME' => name)
26
+ end
27
+
28
+ def find_by_id(id)
29
+ find_one('ID' => id.to_s)
30
+ end
31
+
32
+ def exist?(id)
33
+ find_by_id id
34
+ end
35
+
36
+ private
37
+
38
+ def find(method, xpaths = {})
39
+ reload!
40
+
41
+ pool.send(method) { |element| evaluate_xpaths(element, xpaths) }
42
+ end
43
+
44
+ def evaluate_xpaths(element, xpaths)
45
+ xpaths.inject(true) do |red, elem|
46
+ red && (elem.last.is_a?(Array) ? elem.last.include?(element[elem.first]) : element[elem.first] == elem.last)
47
+ end
48
+ end
49
+
50
+ def reload!
51
+ raise Cloudkeeper::One::Errors::Opennebula::MissingPoolError, 'Handler is missing an OpenNebula pool' unless pool
52
+
53
+ method = pool.respond_to?('info_all!') ? 'info_all!' : 'info!'
54
+ handle_opennebula_error { pool.send(method.to_sym) }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,27 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ module Helper
5
+ ERRORS = Hash.new(Cloudkeeper::One::Errors::Opennebula::ResourceRetrievalError)
6
+ .update(::OpenNebula::Error::EAUTHENTICATION => Cloudkeeper::One::Errors::Opennebula::AuthenticationError,
7
+ ::OpenNebula::Error::EAUTHORIZATION => Cloudkeeper::One::Errors::Opennebula::UserNotAuthorizedError,
8
+ ::OpenNebula::Error::ENO_EXISTS => Cloudkeeper::One::Errors::Opennebula::ResourceNotFoundError,
9
+ ::OpenNebula::Error::EACTION => Cloudkeeper::One::Errors::Opennebula::ResourceStateError).freeze
10
+
11
+ def handle_opennebula_error
12
+ raise Cloudkeeper::One::Errors::ArgumentError, 'OpenNebula service-wrapper was called without a block!' \
13
+ unless block_given?
14
+
15
+ return_value = yield
16
+ return return_value unless OpenNebula.is_error?(return_value)
17
+
18
+ raise decode_error(return_value.errno), return_value.message
19
+ end
20
+
21
+ def decode_error(errno)
22
+ ERRORS[errno]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,133 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ class ImageHandler < ApplianceHandler
5
+ IMAGE_STATES = {
6
+ ready: 'READY',
7
+ used: 'USED',
8
+ disabled: 'DISABLED',
9
+ error: 'ERROR'
10
+ }.freeze
11
+
12
+ EXPIRED_PERMISSIONS = '600'.freeze
13
+
14
+ def initialize
15
+ super
16
+ @pool = OpenNebula::ImagePool.new client
17
+ end
18
+
19
+ def expired
20
+ xpaths = { "TEMPLATE/#{Tags::EXPIRED}" => 'yes' }
21
+ xpaths['UNAME'] = Cloudkeeper::One::Settings[:'opennebula-users'] \
22
+ if Cloudkeeper::One::Settings[:'opennebula-users'] && !Cloudkeeper::One::Settings[:'opennebula-users'].empty?
23
+
24
+ find_all xpaths
25
+ end
26
+
27
+ def delete(image)
28
+ raise Cloudkeeper::One::Errors::ArgumentError, 'image cannot be nil' unless image
29
+
30
+ id = image.id
31
+
32
+ if used? image
33
+ logger.warn "Image with id #{id.inspect} cannot be removed, still in use"
34
+ return
35
+ end
36
+
37
+ super image
38
+ end
39
+
40
+ def disable(image)
41
+ raise Cloudkeeper::One::Errors::ArgumentError, 'image cannot be nil' unless image
42
+
43
+ id = image.id
44
+
45
+ if disabled? image
46
+ logger.info "Image with id #{id.inspect} is already disabled, skipping"
47
+ return
48
+ end
49
+
50
+ unless free? image
51
+ logger.warn "Image with id #{id.inspect} cannot be disabled"
52
+ return
53
+ end
54
+
55
+ handle_opennebula_error { image.disable }
56
+
57
+ timeout { sleep(Cloudkeeper::One::Opennebula::Handler::API_POLLING_WAIT) until disabled? image }
58
+ end
59
+
60
+ def expire(image)
61
+ raise Cloudkeeper::One::Errors::ArgumentError, 'image cannot be nil' unless image
62
+
63
+ id = image.id
64
+
65
+ if expired? image
66
+ logger.debug("Image with id #{id.inspect} is already expired, skipping")
67
+ return
68
+ end
69
+
70
+ chmod image, EXPIRED_PERMISSIONS
71
+ disable image
72
+
73
+ expiration_attribute = "#{Tags::EXPIRED} = \"yes\""
74
+
75
+ handle_opennebula_error { image.rename("EXPIRED_#{Time.now.to_i}_#{image.name}") }
76
+ handle_opennebula_error { image.update(expiration_attribute, true) }
77
+ end
78
+
79
+ def register(image_template, datastore, group)
80
+ image_alloc = OpenNebula::Image.build_xml
81
+ image = OpenNebula::Image.new(image_alloc, client)
82
+
83
+ handle_opennebula_error { image.allocate(image_template, datastore.id) }
84
+
85
+ timeout do
86
+ until ready? image
87
+ raise Cloudkeeper::One::Errors::Opennebula::ResourceStateError, image['TEMPLATE/ERROR'] if error? image
88
+ sleep(Cloudkeeper::One::Opennebula::Handler::API_POLLING_WAIT)
89
+ end
90
+ end
91
+
92
+ chmod image, Cloudkeeper::One::Settings[:'appliances-permissions']
93
+ chgrp image, group
94
+
95
+ image
96
+ end
97
+
98
+ def expired?(image)
99
+ is?(image) { image["TEMPLATE/#{Tags::EXPIRED}"] == 'yes' }
100
+ end
101
+
102
+ def disabled?(image)
103
+ is?(image) { image.state_str == IMAGE_STATES[:disabled] }
104
+ end
105
+
106
+ def ready?(image)
107
+ is?(image) { image.state_str == IMAGE_STATES[:ready] }
108
+ end
109
+
110
+ def used?(image)
111
+ is?(image) { image.state_str == IMAGE_STATES[:used] }
112
+ end
113
+
114
+ def free?(image)
115
+ is?(image) { image.state_str == IMAGE_STATES[:ready] || image.state_str == IMAGE_STATES[:error] }
116
+ end
117
+
118
+ def error?(image)
119
+ is?(image) { image.state_str == IMAGE_STATES[:error] }
120
+ end
121
+
122
+ private
123
+
124
+ def is?(image)
125
+ raise Cloudkeeper::One::Errors::ArgumentError, 'image cannot be nil' unless image
126
+
127
+ handle_opennebula_error { image.info! }
128
+ yield
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,34 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ module Tags
5
+ BASE = 'CLOUDKEEPER'.freeze
6
+ APPLIANCE = "#{BASE}_APPLIANCE".freeze
7
+ IMAGE = "#{BASE}_IMAGE".freeze
8
+
9
+ ID = "#{BASE}_ID".freeze
10
+ EXPIRED = "#{BASE}_EXPIRED".freeze
11
+
12
+ APPLIANCE_ID = "#{APPLIANCE}_ID".freeze
13
+ APPLIANCE_TITLE = "#{APPLIANCE}_TITLE".freeze
14
+ APPLIANCE_DESCRIPTION = "#{APPLIANCE}_DESCRIPTION".freeze
15
+ APPLIANCE_MPURI = "#{APPLIANCE}_MPURI".freeze
16
+ APPLIANCE_GROUP = "#{APPLIANCE}_GROUP".freeze
17
+ APPLIANCE_RAM = "#{APPLIANCE}_RAM".freeze
18
+ APPLIANCE_CORE = "#{APPLIANCE}_CORE".freeze
19
+ APPLIANCE_VERSION = "#{APPLIANCE}_VERSION".freeze
20
+ APPLIANCE_ARCHITECTURE = "#{APPLIANCE}_ARCHITECTURE".freeze
21
+ APPLIANCE_OPERATING_SYSTEM = "#{APPLIANCE}_OPERATING_SYSTEM".freeze
22
+ APPLIANCE_VO = "#{APPLIANCE}_VO".freeze
23
+ APPLIANCE_EXPIRATION_DATE = "#{APPLIANCE}_EXPIRATION_DATE".freeze
24
+ APPLIANCE_IMAGE_LIST_ID = "#{APPLIANCE}_IMAGE_LIST_ID".freeze
25
+ APPLIANCE_ATTRIBUTES = "#{APPLIANCE}_ATTRIBUTES".freeze
26
+
27
+ IMAGE_URI = "#{IMAGE}_URI".freeze
28
+ IMAGE_CHECKSUM = "#{IMAGE}_CHECKSUM".freeze
29
+ IMAGE_SIZE = "#{IMAGE}_SIZE".freeze
30
+ IMAGE_FORMAT = "#{IMAGE}_FORMAT".freeze
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ class TemplateHandler < ApplianceHandler
5
+ def initialize
6
+ super
7
+ @pool = OpenNebula::TemplatePool.new client
8
+ end
9
+
10
+ def register(template_template, group)
11
+ template_alloc = OpenNebula::Template.build_xml
12
+ template = OpenNebula::Template.new(template_alloc, client)
13
+
14
+ handle_opennebula_error { template.allocate template_template }
15
+
16
+ chmod template, Cloudkeeper::One::Settings[:'appliances-permissions']
17
+ chgrp template, group
18
+
19
+ template
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module Cloudkeeper
2
+ module One
3
+ module Opennebula
4
+ autoload :Helper, 'cloudkeeper/one/opennebula/helper'
5
+ autoload :Tags, 'cloudkeeper/one/opennebula/tags'
6
+ autoload :Handler, 'cloudkeeper/one/opennebula/handler'
7
+ autoload :DatastoreHandler, 'cloudkeeper/one/opennebula/datastore_handler'
8
+ autoload :GroupHandler, 'cloudkeeper/one/opennebula/group_handler'
9
+ autoload :ApplianceHandler, 'cloudkeeper/one/opennebula/appliance_handler'
10
+ autoload :ImageHandler, 'cloudkeeper/one/opennebula/image_handler'
11
+ autoload :TemplateHandler, 'cloudkeeper/one/opennebula/template_handler'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ require 'settingslogic'
2
+
3
+ module Cloudkeeper
4
+ module One
5
+ class Settings < Settingslogic
6
+ CONFIGURATION = 'cloudkeeper-one.yml'.freeze
7
+
8
+ # three possible configuration file locations in order by preference
9
+ # if configuration file is found rest of the locations are ignored
10
+ source "#{ENV['HOME']}/.cloudkeeper-one/#{CONFIGURATION}"\
11
+ if File.exist?("#{ENV['HOME']}/.cloudkeeper-one/#{CONFIGURATION}")
12
+
13
+ source "/etc/cloudkeeper-one/#{CONFIGURATION}"\
14
+ if File.exist?("/etc/cloudkeeper-one/#{CONFIGURATION}")
15
+
16
+ source "#{File.dirname(__FILE__)}/../../../config/#{CONFIGURATION}"
17
+
18
+ namespace 'cloudkeeper-one'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module Cloudkeeper
2
+ module One
3
+ VERSION = '1.0.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ require 'active_support/all'
2
+ require 'opennebula'
3
+ require 'json'
4
+ require 'base64'
5
+ require 'tempfile'
6
+
7
+ module Cloudkeeper
8
+ module One
9
+ autoload :Errors, 'cloudkeeper/one/errors'
10
+ autoload :Opennebula, 'cloudkeeper/one/opennebula'
11
+ autoload :ApplianceActions, 'cloudkeeper/one/appliance_actions'
12
+
13
+ autoload :CLI, 'cloudkeeper/one/cli'
14
+ autoload :Settings, 'cloudkeeper/one/settings'
15
+ autoload :CoreConnector, 'cloudkeeper/one/core_connector'
16
+ end
17
+ end
18
+
19
+ require 'cloudkeeper/one/version'
@@ -0,0 +1,5 @@
1
+ module CloudkeeperGrpc
2
+ require 'cloudkeeper_grpc/cloudkeeper_pb'
3
+ require 'cloudkeeper_grpc/cloudkeeper_services_pb'
4
+ require 'cloudkeeper_grpc/constants'
5
+ end