opennebula 6.6.3 → 6.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 885bbb7b22fbe7f2652e00b846dbf5881960a9cd041282d789a669d671a5835f
4
- data.tar.gz: e7575d6535512ae13e139249ecfb05cac655cd34013176184bf98ce6fc5215dd
3
+ metadata.gz: eedefbb85b95ea6fab4348f1a05bb71ef05532659c49a388cecf0ba3662afb75
4
+ data.tar.gz: 10a3856b4e5794727d67687446ef420c8b0eb05b9f15ada412467d8e3da8b15a
5
5
  SHA512:
6
- metadata.gz: 138d7785b0d1c4dc84df1a862f6a14de05a4156ee44b1bff15752dcb99daab62e9590a776d4bc3d5dcd8c45924c574b0b1a8da1aa16e37626609780811b2e47a
7
- data.tar.gz: b3ff5aa1bf6ef4ed40f37e77beb6a8e4e11aaa7bbeed6c5643c316cb95f795d0c4792b026478c92298d8e00b584955a9c2970f14eb03257c8316ef9bb886d198
6
+ metadata.gz: ce540f42a07049fd4088692b31b3651dce29db28cee3bd199a3c67f17998aad1677a8aceb4d29a75deb93e9c5d7d985b9246cc7fa6c55721f9e3411e5779dd85
7
+ data.tar.gz: c751b6ad361a43f7d87ef93734f881715b95c1b2c8c385b565c10ae7ce62b8d3d69db42d4ed2365cb4449d1ed1e2ea8a1958a38ab5705e4c755a6ef135e7249c
@@ -51,7 +51,7 @@ end
51
51
  module CloudClient
52
52
 
53
53
  # OpenNebula version
54
- VERSION = '6.6.3'
54
+ VERSION = '6.8.0'
55
55
 
56
56
  # #########################################################################
57
57
  # Default location for the authentication file
@@ -73,7 +73,8 @@ module OpenNebula
73
73
  "MARKETPLACE" => 0x8000000000000,
74
74
  "MARKETPLACEAPP"=> 0x10000000000000,
75
75
  "VMGROUP" => 0x20000000000000,
76
- "VNTEMPLATE" => 0x40000000000000
76
+ "VNTEMPLATE" => 0x40000000000000,
77
+ "BACKUPJOB" =>0x100000000000000
77
78
  }
78
79
 
79
80
  RIGHTS =
@@ -0,0 +1,250 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2023, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ require 'opennebula/lockable_ext'
18
+ require 'opennebula/pool_element'
19
+
20
+ module OpenNebula
21
+
22
+ # Class for representing a Backup Job object
23
+ class BackupJob < PoolElement
24
+
25
+ #######################################################################
26
+ # Constants and Class Methods
27
+ #######################################################################
28
+
29
+ BACKUPJOB_METHODS = {
30
+ :allocate => 'backupjob.allocate',
31
+ :info => 'backupjob.info',
32
+ :update => 'backupjob.update',
33
+ :delete => 'backupjob.delete',
34
+ :chown => 'backupjob.chown',
35
+ :chmod => 'backupjob.chmod',
36
+ :clone => 'backupjob.clone',
37
+ :rename => 'backupjob.rename',
38
+ :lock => 'backupjob.lock',
39
+ :unlock => 'backupjob.unlock',
40
+ :backup => 'backupjob.backup',
41
+ :cancel => 'backupjob.cancel',
42
+ :retry => 'backupjob.retry',
43
+ :priority => 'backupjob.priority',
44
+ :schedadd => 'backupjob.schedadd',
45
+ :scheddelete => 'backupjob.scheddelete',
46
+ :schedupdate => 'backupjob.schedupdate'
47
+ }
48
+
49
+ # Creates a BackupJob description with just its identifier
50
+ # this method should be used to create plain BackupJob objects.
51
+ # +id+ the id of the user
52
+ #
53
+ # Example:
54
+ # bj = BackupJob.new(BackupJob.build_xml(3),rpc_client)
55
+ #
56
+ def self.build_xml(pe_id = nil)
57
+ if pe_id
58
+ obj_xml = "<BACKUPJOB><ID>#{pe_id}</ID></BACKUPJOB>"
59
+ else
60
+ obj_xml = '<BACKUPJOB></BACKUPJOB>'
61
+ end
62
+
63
+ XMLElement.build_xml(obj_xml, 'BACKUPJOB')
64
+ end
65
+
66
+ # Class constructor
67
+ def initialize(xml, client)
68
+ LockableExt.make_lockable(self, BACKUPJOB_METHODS)
69
+
70
+ super(xml, client)
71
+
72
+ @client = client
73
+ end
74
+
75
+ #######################################################################
76
+ # XML-RPC Methods for the Backup Job Object
77
+ #######################################################################
78
+
79
+ # Retrieves the information of the given Backup Job.
80
+ def info
81
+ return Error.new('ID not defined') unless @pe_id
82
+
83
+ rc = @client.call(BACKUPJOB_METHODS[:info], @pe_id)
84
+
85
+ if !OpenNebula.is_error?(rc)
86
+ initialize_xml(rc, 'BACKUPJOB')
87
+ rc = nil
88
+
89
+ @pe_id = self['ID'].to_i if self['ID']
90
+ @name = self['NAME'] if self['NAME']
91
+ end
92
+
93
+ rc
94
+ end
95
+
96
+ alias info! info
97
+
98
+ # Allocates a new Backup Job in OpenNebula
99
+ #
100
+ # @param description [String] The contents of the BackupJob.
101
+ #
102
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
103
+ # otherwise
104
+ def allocate(description)
105
+ super(BACKUPJOB_METHODS[:allocate], description)
106
+ end
107
+
108
+ # Deletes the BackupJob
109
+ #
110
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
111
+ # otherwise
112
+ def delete
113
+ call(BACKUPJOB_METHODS[:delete], @pe_id)
114
+ end
115
+
116
+ # Replaces the Backup Job contents
117
+ #
118
+ # @param new_template [String] New template contents
119
+ # @param append [true, false] True to append new attributes instead of
120
+ # replace the whole template
121
+ #
122
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
123
+ # otherwise
124
+ def update(new_template, append = false)
125
+ super(BACKUPJOB_METHODS[:update], new_template, append ? 1 : 0)
126
+ end
127
+
128
+ # Changes the owner/group
129
+ # uid:: _Integer_ the new owner id. Set to -1 to leave the current one
130
+ # gid:: _Integer_ the new group id. Set to -1 to leave the current one
131
+ # [return] nil in case of success or an Error object
132
+ def chown(uid, gid)
133
+ super(BACKUPJOB_METHODS[:chown], uid, gid)
134
+ end
135
+
136
+ # Changes the Backup Job permissions.
137
+ #
138
+ # @param octet [String] Permissions octed , e.g. 640
139
+ #
140
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
141
+ # otherwise
142
+ def chmod_octet(octet)
143
+ super(BACKUPJOB_METHODS[:chmod], octet)
144
+ end
145
+
146
+ # Changes the Backup Job permissions.
147
+ # Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change
148
+ #
149
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
150
+ # otherwise
151
+ # rubocop:disable Metrics/ParameterLists
152
+ def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u,
153
+ other_m, other_a)
154
+ call(BACKUPJOB_METHODS[:chmod], @pe_id, owner_u, owner_m, owner_a, group_u,
155
+ group_m, group_a, other_u, other_m, other_a)
156
+ end
157
+ # rubocop:enable Metrics/ParameterLists
158
+
159
+ # Renames this Backup Job
160
+ #
161
+ # @param name [String] New name for the Backup Job.
162
+ #
163
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
164
+ # otherwise
165
+ def rename(name)
166
+ call(BACKUPJOB_METHODS[:rename], @pe_id, name)
167
+ end
168
+
169
+ # Starts the Backup Job
170
+ #
171
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
172
+ # otherwise
173
+ def backup
174
+ call(BACKUPJOB_METHODS[:backup], @pe_id)
175
+ end
176
+
177
+ # Cancel pending Backup Job, removing VMs from waiting list
178
+ #
179
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
180
+ # otherwise
181
+ def cancel
182
+ call(BACKUPJOB_METHODS[:cancel], @pe_id)
183
+ end
184
+
185
+ # Retry backup for VMs in error list
186
+ #
187
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
188
+ # otherwise
189
+ def retry
190
+ call(BACKUPJOB_METHODS[:retry], @pe_id)
191
+ end
192
+
193
+ # Change priority of Backup Job
194
+ #
195
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
196
+ # otherwise
197
+ def priority(pr)
198
+ call(BACKUPJOB_METHODS[:priority], @pe_id, pr)
199
+ end
200
+
201
+ # Add Scheduled action
202
+ #
203
+ # @param sched_template [String] Template with SCHED_ACTIONs
204
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
205
+ # otherwise
206
+ def sched_action_add(sched_template)
207
+ call(BACKUPJOB_METHODS[:schedadd], @pe_id, sched_template)
208
+ end
209
+
210
+ # Delete Scheduled Action
211
+ #
212
+ # @param sched_id [Int] id of the SCHED_ACTION
213
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
214
+ # otherwise
215
+ def sched_action_delete(sched_id)
216
+ call(BACKUPJOB_METHODS[:scheddelete], @pe_id, sched_id.to_i)
217
+ end
218
+
219
+ # Update Scheduled Action
220
+ #
221
+ # @param sched_id [Int] id of the SCHED_ACTION
222
+ # @param sched_template [String] Template containing a SCHED_ACTION
223
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
224
+ # otherwise
225
+ def sched_action_update(sched_id, sched_template)
226
+ call(BACKUPJOB_METHODS[:schedupdate], @pe_id,
227
+ sched_id.to_i, sched_template)
228
+ end
229
+
230
+ #######################################################################
231
+ # Helpers to get Template information
232
+ #######################################################################
233
+
234
+ # Returns the group identifier
235
+ # [return] _Integer_ the element's group ID
236
+ def gid
237
+ self['GID'].to_i
238
+ end
239
+
240
+ def owner_id
241
+ self['UID'].to_i
242
+ end
243
+
244
+ def public?
245
+ self['PERMISSIONS/GROUP_U'] == '1' || self['PERMISSIONS/OTHER_U'] == '1'
246
+ end
247
+
248
+ end
249
+
250
+ end
@@ -0,0 +1,82 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2023, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ require 'opennebula/pool'
18
+
19
+ module OpenNebula
20
+
21
+ # Class representing a Backup Job pool
22
+ class BackupJobPool < Pool
23
+
24
+ #######################################################################
25
+ # Constants and Class attribute accessors
26
+ #######################################################################
27
+
28
+ BACKUPJOB_POOL_METHODS = {
29
+ :info => 'backupjobpool.info'
30
+ }
31
+
32
+ #######################################################################
33
+ # Class constructor & Pool Methods
34
+ #######################################################################
35
+
36
+ # +client+ a Client object that represents an XML-RPC connection
37
+ # +user_id+ used to refer to a Pool with Templates from that user
38
+ def initialize(client, user_id = -1)
39
+ super('BACKUPJOB_POOL', 'BACKUPJOB', client)
40
+
41
+ @user_id = user_id
42
+ end
43
+
44
+ # Factory method to create Backup Job objects
45
+ def factory(element_xml)
46
+ OpenNebula::BackupJob.new(element_xml, @client)
47
+ end
48
+
49
+ #######################################################################
50
+ # XML-RPC Methods for the Template Object
51
+ #######################################################################
52
+
53
+ # Retrieves all or part of the Templates in the pool.
54
+ def info(*args)
55
+ case args.size
56
+ when 0
57
+ info_filter(BACKUPJOB_POOL_METHODS[:info], @user_id, -1, -1)
58
+ when 3
59
+ info_filter(BACKUPJOB_POOL_METHODS[:info], args[0], args[1], args[2])
60
+ end
61
+ end
62
+
63
+ def info_all
64
+ super(BACKUPJOB_POOL_METHODS[:info])
65
+ end
66
+
67
+ def info_mine
68
+ super(BACKUPJOB_POOL_METHODS[:info])
69
+ end
70
+
71
+ def info_group
72
+ super(BACKUPJOB_POOL_METHODS[:info])
73
+ end
74
+
75
+ alias info! info
76
+ alias info_all! info_all
77
+ alias info_mine! info_mine
78
+ alias info_group! info_group
79
+
80
+ end
81
+
82
+ end
@@ -140,9 +140,9 @@ module OpenNebula
140
140
  @one_endpoint = endpoint
141
141
  elsif ENV["ONE_XMLRPC"]
142
142
  @one_endpoint = ENV["ONE_XMLRPC"]
143
- elsif ENV['HOME'] and File.exists?(ENV['HOME']+"/.one/one_endpoint")
143
+ elsif ENV['HOME'] and File.exist?(ENV['HOME']+"/.one/one_endpoint")
144
144
  @one_endpoint = File.read(ENV['HOME']+"/.one/one_endpoint")
145
- elsif File.exists?("/var/lib/one/.one/one_endpoint")
145
+ elsif File.exist?("/var/lib/one/.one/one_endpoint")
146
146
  @one_endpoint = File.read("/var/lib/one/.one/one_endpoint")
147
147
  else
148
148
  @one_endpoint = "http://localhost:2633/RPC2"
@@ -37,7 +37,7 @@ module OpenNebula
37
37
  SELF = -1
38
38
 
39
39
  # Default resource ACL's for group users (create)
40
- GROUP_DEFAULT_ACLS = "VM+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER+VMGROUP"
40
+ GROUP_DEFAULT_ACLS = "VM+IMAGE+TEMPLATE+DOCUMENT+SECGROUP+VROUTER+VMGROUP+BACKUPJOB"
41
41
 
42
42
  # The default view for group and group admins, must be defined in
43
43
  # sunstone_views.yaml
@@ -89,7 +89,7 @@ class OpenNebula::LdapAuth
89
89
  file=@options[:mapping_file_path]
90
90
  generate = false
91
91
 
92
- if File.exists?(file)
92
+ if File.exist?(file)
93
93
  stat = File.stat(file)
94
94
  age = Time.now.to_i - stat.mtime.to_i
95
95
  generate = true if age > @options[:mapping_timeout]
@@ -124,7 +124,7 @@ class OpenNebula::LdapAuth
124
124
 
125
125
  @mapping = {}
126
126
 
127
- if File.exists?(file)
127
+ if File.exist?(file)
128
128
  @mapping = YAML.load(File.read(file))
129
129
  end
130
130
 
@@ -125,6 +125,7 @@ module OpenNebula::LockableExt
125
125
  def self.lockable?(obj)
126
126
  # Lockable classes
127
127
  lockable = [
128
+ OpenNebula::BackupJob,
128
129
  OpenNebula::Document,
129
130
  OpenNebula::Hook,
130
131
  OpenNebula::Image,
@@ -21,6 +21,7 @@ require 'cloud/CloudClient'
21
21
  include CloudCLI
22
22
 
23
23
  module Role
24
+
24
25
  # Actions that can be performed on the VMs of a given Role
25
26
  SCHEDULE_ACTIONS = [
26
27
  'terminate',
@@ -78,8 +79,9 @@ module Role
78
79
  # @param [String] state String number representing the state
79
80
  # @return the state string
80
81
  def self.state_str(state_number)
81
- return STATE_STR[state_number.to_i]
82
+ STATE_STR[state_number.to_i]
82
83
  end
84
+
83
85
  end
84
86
 
85
87
  module Service
@@ -126,19 +128,19 @@ module Service
126
128
  # @param [String] state String number representing the state
127
129
  # @return the state string
128
130
  def self.state_str(state_number)
129
- return STATE_STR[state_number.to_i]
131
+ STATE_STR[state_number.to_i]
130
132
  end
131
133
 
132
134
  # Build a json specifying an action
133
135
  # @param [String] perform action to be performed (e.g.: shutdown)
134
136
  # @param [Hash, nil] params contains the params for the action
135
137
  # @return [String] json representing the action
136
- def self.build_json_action(perform, params=nil)
137
- body = Hash.new
138
+ def self.build_json_action(perform, params = nil)
139
+ body = {}
138
140
  body['perform'] = perform
139
141
  body['params'] = params if params
140
142
 
141
- action = Hash.new
143
+ action = {}
142
144
  action['action'] = body
143
145
 
144
146
  JSON.pretty_generate action
@@ -148,83 +150,83 @@ module Service
148
150
 
149
151
  DEFAULT_OPTIONS = [
150
152
  ENDPOINT = {
151
- :name => "server",
152
- :short => "-s url",
153
- :large => "--server url",
153
+ :name => 'server',
154
+ :short => '-s url',
155
+ :large => '--server url',
154
156
  :format => String,
155
- :description => "Service endpoint"
157
+ :description => 'Service endpoint'
156
158
  },
157
159
  USERNAME={
158
- :name => "username",
159
- :short => "-u name",
160
- :large => "--username name",
160
+ :name => 'username',
161
+ :short => '-u name',
162
+ :large => '--username name',
161
163
  :format => String,
162
- :description => "User name"
164
+ :description => 'User name'
163
165
  },
164
166
  PASSWORD={
165
- :name => "password",
166
- :short => "-p pass",
167
- :large => "--password pass",
167
+ :name => 'password',
168
+ :short => '-p pass',
169
+ :large => '--password pass',
168
170
  :format => String,
169
- :description => "User password"
171
+ :description => 'User password'
170
172
  }
171
173
  ]
172
174
 
173
175
  JSON_FORMAT = {
174
- :name => "json",
175
- :short => "-j",
176
- :large => "--json",
177
- :description => "Print the resource in JSON"
176
+ :name => 'json',
177
+ :short => '-j',
178
+ :large => '--json',
179
+ :description => 'Print the resource in JSON'
178
180
  }
179
181
 
180
182
  TOP = {
181
- :name => "top",
182
- :short => "-t",
183
- :large => "--top",
184
- :description => "Top for the command"
183
+ :name => 'top',
184
+ :short => '-t',
185
+ :large => '--top',
186
+ :description => 'Top for the command'
185
187
  }
186
188
 
187
189
  PERIOD = {
188
- :name => "period",
189
- :short => "-p x",
190
- :large => "--period x",
190
+ :name => 'period',
191
+ :short => '-p x',
192
+ :large => '--period x',
191
193
  :format => Integer,
192
- :description => "Seconds between each group of actions"
194
+ :description => 'Seconds between each group of actions'
193
195
  }
194
196
 
195
197
  NUMBER = {
196
- :name => "number",
197
- :short => "-n x",
198
- :large => "--number x",
198
+ :name => 'number',
199
+ :short => '-n x',
200
+ :large => '--number x',
199
201
  :format => Integer,
200
- :description => "Number of VMs to apply the action to each period"
202
+ :description => 'Number of VMs to apply the action to each period'
201
203
  }
202
204
 
203
205
  FORCE = {
204
- :name => "force",
205
- :short => "-f",
206
- :large => "--force",
207
- :description => "Force the new cardinality even if it is outside the limits"
206
+ :name => 'force',
207
+ :short => '-f',
208
+ :large => '--force',
209
+ :description => 'Force the new cardinality even if it is outside the limits'
208
210
  }
209
211
 
210
212
  # Format helpers
211
213
 
212
- # def self.rname_to_id(name, poolname, options)
214
+ # def self.rname_to_id(name, poolname, options)
213
215
  def self.rname_to_id(name, poolname)
214
216
  return 0, name.to_i if name.match(/^[0123456789]+$/)
215
217
 
216
- client = Service::Client.new()
218
+ client = Service::Client.new
217
219
 
218
220
  resource_path = case poolname
219
- when "SERVICE" then "/service"
220
- when "SERVICE TEMPLATE" then "/service_template"
221
- end
221
+ when 'SERVICE' then '/service'
222
+ when 'SERVICE TEMPLATE' then '/service_template'
223
+ end
222
224
 
223
225
  response = client.get(resource_path)
224
226
 
225
- if CloudClient::is_error?(response)
227
+ if CloudClient.is_error?(response)
226
228
  return -1, "OpenNebula #{poolname} name not found," <<
227
- " use the ID instead"
229
+ ' use the ID instead'
228
230
  end
229
231
 
230
232
  pool = JSON.parse(response.body)
@@ -242,38 +244,32 @@ module Service
242
244
 
243
245
  objects = pool['DOCUMENT_POOL']['DOCUMENT'].select {|object| object['NAME'] == name }
244
246
 
245
- if objects.length>0
246
- if objects.length>1
247
- return -1, "There are multiple #{ename}s with name #{name}."
248
- else
249
- result = objects.first['ID']
250
- end
251
- else
252
- return -1, "#{ename} named #{name} not found."
253
- end
247
+ return -1, "#{ename} named #{name} not found." unless objects.length>0
248
+ return -1, "There are multiple #{ename}s with name #{name}." if objects.length>1
249
+
250
+ result = objects.first['ID']
254
251
 
255
- return 0, result
252
+ [0, result]
256
253
  end
257
254
 
258
255
  def self.list_to_id(names, poolname)
259
-
260
- client = Service::Client.new()
256
+ client = Service::Client.new
261
257
 
262
258
  resource_path = case poolname
263
- when "SERVICE" then "/service"
264
- when "SERVICE TEMPLATE" then "/service_template"
265
- end
259
+ when 'SERVICE' then '/service'
260
+ when 'SERVICE TEMPLATE' then '/service_template'
261
+ end
266
262
 
267
263
  response = client.get(resource_path)
268
264
 
269
- if CloudClient::is_error?(response)
265
+ if CloudClient.is_error?(response)
270
266
  return -1, "OpenNebula #{poolname} name not found," <<
271
- " use the ID instead"
267
+ ' use the ID instead'
272
268
  end
273
269
 
274
270
  pool = JSON.parse(response.body)
275
271
 
276
- result = names.split(',').collect { |name|
272
+ result = names.split(',').collect do |name|
277
273
  if name.match(/^[0123456789]+$/)
278
274
  name.to_i
279
275
  else
@@ -285,9 +281,9 @@ module Service
285
281
 
286
282
  rc[1]
287
283
  end
288
- }
284
+ end
289
285
 
290
- return 0, result
286
+ [0, result]
291
287
  end
292
288
 
293
289
  def self.list_to_id_desc(poolname)
@@ -304,8 +300,8 @@ module Service
304
300
  ids.each do |id|
305
301
  response = block.call(id) if block_given?
306
302
 
307
- if CloudClient::is_error?(response)
308
- puts response.to_s
303
+ if CloudClient.is_error?(response)
304
+ puts response
309
305
  exit_code = response.code.to_i
310
306
  end
311
307
  end
@@ -321,16 +317,29 @@ module Service
321
317
  exit_code = 0
322
318
  response = block.call(id) if block_given?
323
319
 
324
- if CloudClient::is_error?(response)
325
- puts response.to_s
320
+ if CloudClient.is_error?(response)
321
+ puts response
326
322
  exit_code = response.code.to_i
327
323
  end
328
324
 
329
325
  exit_code
330
326
  end
331
327
 
328
+ #
329
+ # Interface to OneFlow REST API through a Ruby client
330
+ #
332
331
  class Client
333
- def initialize(opts={})
332
+
333
+ #
334
+ # The options are read from ENV and FS if not passed
335
+ #
336
+ # @param [Hash] opts Required configuration to interact with OneFlow
337
+ # @option opts [String] :url Endpoint where OneFlow is running. Defaults to 'http://localhost:2474'
338
+ # @option opts [String] :username OpenNebula user
339
+ # @option opts [String] :password OpenNebula user password
340
+ # @option opts [String] :user_agent Defaults to Ruby. Oneflow will behave accordingly.
341
+ #
342
+ def initialize(opts = {})
334
343
  endpoint = '/.one/oneflow_endpoint'
335
344
  @username = opts[:username] || ENV['ONEFLOW_USER']
336
345
  @password = opts[:password] || ENV['ONEFLOW_PASSWORD']
@@ -339,23 +348,23 @@ module Service
339
348
  url = opts[:url]
340
349
  elsif ENV['ONEFLOW_URL']
341
350
  url = ENV['ONEFLOW_URL']
342
- elsif ENV['HOME'] && File.exists?(ENV['HOME'] + endpoint)
351
+ elsif ENV['HOME'] && File.exist?(ENV['HOME'] + endpoint)
343
352
  url = File.read(ENV['HOME'] + endpoint).strip
344
- elsif File.exists?('/var/lib/one/.one/oneflow_endpoint')
353
+ elsif File.exist?('/var/lib/one/.one/oneflow_endpoint')
345
354
  url = File.read('/var/lib/one/.one/oneflow_endpoint').strip
346
355
  else
347
356
  url = 'http://localhost:2474'
348
357
  end
349
358
 
350
359
  if @username.nil? && @password.nil?
351
- if ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and File.file?(ENV["ONE_AUTH"])
352
- one_auth = File.read(ENV["ONE_AUTH"])
353
- elsif ENV["HOME"] and File.file?(ENV["HOME"]+"/.one/one_auth")
354
- one_auth = File.read(ENV["HOME"]+"/.one/one_auth")
355
- elsif File.file?("/var/lib/one/.one/one_auth")
356
- one_auth = File.read("/var/lib/one/.one/one_auth")
360
+ if ENV['ONE_AUTH'] and !ENV['ONE_AUTH'].empty? and File.file?(ENV['ONE_AUTH'])
361
+ one_auth = File.read(ENV['ONE_AUTH'])
362
+ elsif ENV['HOME'] and File.file?(ENV['HOME']+'/.one/one_auth')
363
+ one_auth = File.read(ENV['HOME']+'/.one/one_auth')
364
+ elsif File.file?('/var/lib/one/.one/one_auth')
365
+ one_auth = File.read('/var/lib/one/.one/one_auth')
357
366
  else
358
- raise "ONE_AUTH file not present"
367
+ raise 'ONE_AUTH file not present'
359
368
  end
360
369
 
361
370
  one_auth = one_auth.rstrip
@@ -366,37 +375,37 @@ module Service
366
375
  @uri = URI.parse(url)
367
376
 
368
377
  @user_agent = "OpenNebula #{CloudClient::VERSION} " <<
369
- "(#{opts[:user_agent]||"Ruby"})"
378
+ "(#{opts[:user_agent]||'Ruby'})"
370
379
 
371
380
  @host = nil
372
381
  @port = nil
373
382
 
374
- if ENV['http_proxy']
375
- uri_proxy = URI.parse(ENV['http_proxy'])
376
- flag = false
377
-
378
- # Check if we need to bypass the proxy
379
- if ENV['no_proxy']
380
- ENV['no_proxy'].split(',').each do |item|
381
- item = item.rstrip.lstrip
382
-
383
- unless (IPAddress @uri.host rescue nil).nil?
384
- unless (IPAddress item rescue nil).nil?
385
- flag |= IPAddress(item).include? IPAddress(@uri.host)
386
- end
387
- else
388
- if (IPAddress(item) rescue nil).nil?
389
- flag |= (item == @uri.host)
390
- end
383
+ return unless ENV['http_proxy']
384
+
385
+ uri_proxy = URI.parse(ENV['http_proxy'])
386
+ flag = false
387
+
388
+ #  Check if we need to bypass the proxy
389
+ if ENV['no_proxy']
390
+ ENV['no_proxy'].split(',').each do |item|
391
+ item = item.strip
392
+
393
+ if (IPAddress @uri.host rescue nil).nil?
394
+ if (IPAddress(item) rescue nil).nil?
395
+ flag |= (item == @uri.host)
396
+ end
397
+ else
398
+ unless (IPAddress item rescue nil).nil?
399
+ flag |= IPAddress(item).include? IPAddress(@uri.host)
391
400
  end
392
401
  end
393
402
  end
394
-
395
- unless flag
396
- @host = uri_proxy.host
397
- @port = uri_proxy.port
398
- end
399
403
  end
404
+
405
+ return if flag
406
+
407
+ @host = uri_proxy.host
408
+ @port = uri_proxy.port
400
409
  end
401
410
 
402
411
  def set_content_type(content_type)
@@ -420,10 +429,8 @@ module Service
420
429
  req = Net::HTTP::Proxy(@host, @port)::Post.new(path)
421
430
  req.body = body
422
431
 
423
- if path.start_with?('/service_template')
424
- unless @content_type.nil?
425
- req.content_type = @content_type
426
- end
432
+ if path.start_with?('/service_template') && !@content_type.nil?
433
+ req.content_type = @content_type
427
434
  end
428
435
  do_request(req)
429
436
  end
@@ -454,11 +461,11 @@ module Service
454
461
 
455
462
  req['User-Agent'] = @user_agent
456
463
 
457
- res = CloudClient::http_start(@uri, @timeout) do |http|
464
+ CloudClient.http_start(@uri, @timeout) do |http|
458
465
  http.request(req)
459
466
  end
460
-
461
- res
462
467
  end
468
+
463
469
  end
470
+
464
471
  end
@@ -14,104 +14,127 @@
14
14
  # limitations under the License. #
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
-
18
- require 'pp'
19
17
  require 'openssl'
20
18
  require 'base64'
21
19
  require 'fileutils'
20
+ require 'open3'
21
+ require 'tempfile'
22
+
23
+ module OpenNebula
24
+
25
+ # SSH key authentication class. It can be used as a driver for auth_mad
26
+ # as auth method is defined. It also holds some helper methods to be used
27
+ # by oneauth command
28
+ class SshAuth
29
+
30
+ # Initialize SshAuth object
31
+ #
32
+ # @param [Hash] default options for path
33
+ # @option options [String] :public_key public key for the user
34
+ # @option options [String] :private_key key private key for the user.
35
+ def initialize(options = {})
36
+ @private_key = nil
37
+ @public_key = nil
38
+
39
+ # Initialize the private key
40
+ if options[:private_key]
41
+ begin
42
+ @private_key = File.read(options[:private_key])
43
+ rescue StandardError => e
44
+ raise "Cannot read #{options[:private_key]}\n #{e}"
45
+ end
22
46
 
23
- module OpenNebula; end
24
-
25
- # SSH key authentication class. It can be used as a driver for auth_mad
26
- # as auth method is defined. It also holds some helper methods to be used
27
- # by oneauth command
28
- class OpenNebula::SshAuth
29
- # Initialize SshAuth object
30
- #
31
- # @param [Hash] default options for path
32
- # @option options [String] :public_key public key for the user
33
- # @option options [String] :private_key key private key for the user.
34
- def initialize(options={})
35
- @private_key = nil
36
- @public_key = nil
37
-
38
- # Initialize the private key
39
- if options[:private_key]
40
- begin
41
- @private_key = File.read(options[:private_key])
42
- rescue Exception => e
43
- raise "Cannot read #{options[:private_key]}"
47
+ begin
48
+ @private_key_rsa = OpenSSL::PKey::RSA.new(@private_key)
49
+ rescue OpenSSL::PKey::RSAError
50
+ private_key_pem = openssh_to_pem(@private_key)
51
+ @private_key_rsa = OpenSSL::PKey::RSA.new(private_key_pem)
52
+ end
53
+ end
54
+
55
+ # Initialize the public key
56
+ if options[:public_key]
57
+ @public_key = options[:public_key]
58
+ elsif !@private_key.nil?
59
+ # Init ssh keys using private key. public key is extracted in a
60
+ # format compatible with openssl. The public key does not contain
61
+ # "---- BEGIN/END PUBLIC KEY ----" and is in a single line
62
+ @public_key = @private_key_rsa.public_key.to_pem.split("\n")
63
+ @public_key = @public_key.reject {|l| l.match(/PUBLIC KEY/) }.join('')
64
+ end
65
+
66
+ if @private_key.nil? && @public_key.nil?
67
+ raise 'You have to define at least one of the keys'
44
68
  end
45
69
 
46
- @private_key_rsa = OpenSSL::PKey::RSA.new(@private_key)
70
+ @public_key_rsa = OpenSSL::PKey::RSA.new(Base64.decode64(@public_key))
47
71
  end
48
72
 
49
- # Initialize the public key
50
- if options[:public_key]
51
- @public_key = options[:public_key]
52
- elsif @private_key != nil
53
- # Init ssh keys using private key. public key is extracted in a
54
- # format compatible with openssl. The public key does not contain
55
- # "---- BEGIN/END PUBLIC KEY ----" and is in a single line
56
- @public_key = @private_key_rsa.public_key.to_pem.split("\n")
57
- @public_key = @public_key.reject {|l| l.match(/PUBLIC KEY/) }.join('')
73
+ # Creates a login token for ssh authentication.
74
+ # By default it is valid for 1 hour but it can be changed to any number
75
+ # of seconds with expire parameter (in seconds)
76
+ def login_token(user, expire = 3600)
77
+ expire ||= 3600
78
+
79
+ return encrypt("#{user}:#{Time.now.to_i + expire.to_i}")
58
80
  end
59
81
 
60
- if @private_key.nil? && @public_key.nil?
61
- raise "You have to define at least one of the keys"
82
+ # Returns a valid password string to create a user using this auth driver.
83
+ # In this case the ssh public key.
84
+ def password
85
+ @public_key
62
86
  end
63
87
 
64
- @public_key_rsa = OpenSSL::PKey::RSA.new(Base64::decode64(@public_key))
65
- end
88
+ # Checks the proxy created with the login method
89
+ def authenticate(user, token)
90
+ begin
91
+ token_plain = decrypt(token)
92
+ t_user, time = token_plain.split(':')
66
93
 
67
- # Creates a login token for ssh authentication.
68
- # By default it is valid for 1 hour but it can be changed to any number
69
- # of seconds with expire parameter (in seconds)
70
- def login_token(user, expire=3600)
71
- expire ||= 3600
94
+ return 'invalid credentials' unless user == t_user
95
+ return 'ssh proxy expired, login again to renew it' if Time.now.to_i >= time.to_i
72
96
 
73
- return encrypt("#{user}:#{Time.now.to_i + expire.to_i}")
74
- end
97
+ return true
98
+ rescue StandardError
99
+ return 'error'
100
+ end
101
+ end
75
102
 
76
- # Returns a valid password string to create a user using this auth driver.
77
- # In this case the ssh public key.
78
- def password
79
- @public_key
80
- end
103
+ private
81
104
 
82
- # Checks the proxy created with the login method
83
- def authenticate(user, token)
84
- begin
85
- token_plain = decrypt(token)
86
- _user, time = token_plain.split(':')
87
-
88
- if user == _user
89
- if Time.now.to_i >= time.to_i
90
- return "ssh proxy expired, login again to renew it"
91
- else
92
- return true
93
- end
94
- else
95
- return "invalid credentials"
96
- end
97
- rescue
98
- return "error"
105
+ def openssh_to_pem(private_key)
106
+ temp_file = Tempfile.new('private_key')
107
+
108
+ File.write(temp_file.path, private_key)
109
+
110
+ # Use ssh-keygen to convert the key
111
+ command = "ssh-keygen -p -N '' -m PEM -f #{temp_file.path}"
112
+
113
+ _out, err, status = Open3.capture3(command)
114
+
115
+ raise "Failed to convert key: #{err}" unless status.success?
116
+
117
+ pem_key = File.read(temp_file.path)
118
+ return pem_key
119
+ ensure
120
+ temp_file.close
121
+ temp_file.unlink if temp_file
122
+ end
123
+
124
+ ###########################################################################
125
+ # Methods to handle ssh keys
126
+ ###########################################################################
127
+ # Encrypts data with the private key of the user and returns
128
+ # base 64 encoded output in a single line
129
+ def encrypt(data)
130
+ Base64.encode64(@private_key_rsa.private_encrypt(data)).gsub!("\n", '').strip
99
131
  end
100
- end
101
132
 
102
- private
133
+ # Decrypts base 64 encoded data with pub_key (public key)
134
+ def decrypt(data)
135
+ @public_key_rsa.public_decrypt(Base64.decode64(data))
136
+ end
103
137
 
104
- ###########################################################################
105
- # Methods to handle ssh keys
106
- ###########################################################################
107
- # Encrypts data with the private key of the user and returns
108
- # base 64 encoded output in a single line
109
- def encrypt(data)
110
- Base64::encode64(@private_key_rsa.private_encrypt(data)).gsub!(/\n/, '').strip
111
138
  end
112
139
 
113
- # Decrypts base 64 encoded data with pub_key (public key)
114
- def decrypt(data)
115
- @public_key_rsa.public_decrypt(Base64::decode64(data))
116
- end
117
140
  end
@@ -756,7 +756,8 @@ module OpenNebula
756
756
  # can be updated are: INPUT/{TYPE, BUS}; RAW/{TYPE, DATA, DATA_VMX},
757
757
  # OS/{BOOT, BOOTLOADER, ARCH, MACHINE, KERNEL, INITRD},
758
758
  # FEATURES/{ACPI, APIC, PAE, LOCALTIME, HYPERV, GUEST_AGENT},
759
- # and GRAPHICS/{TYPE, LISTEN, PASSWD, KEYMAP}
759
+ # GRAPHICS/{TYPE, LISTEN, PASSWD, KEYMAP},
760
+ # and VIDEO/{TYPE, IOMMU, ATS, VRAM, RESOLUTION}
760
761
  # @param append, append template, do not delete empty attributes
761
762
  # @return [nil, OpenNebula::Error] nil in case of success, Error
762
763
  # otherwise
@@ -31,7 +31,10 @@ module OpenNebula
31
31
  :chmod => "vmgroup.chmod",
32
32
  :rename => "vmgroup.rename",
33
33
  :lock => "vmgroup.lock",
34
- :unlock => "vmgroup.unlock"
34
+ :unlock => "vmgroup.unlock",
35
+ :roleadd => "vmgroup.roleadd",
36
+ :roledelete => "vmgroup.roledelete",
37
+ :roleupdate => "vmgroup.roleupdate"
35
38
  }
36
39
 
37
40
  # Creates a VMGroup description with just its identifier
@@ -134,6 +137,37 @@ module OpenNebula
134
137
  return call(VMGROUP_METHODS[:rename], @pe_id, name)
135
138
  end
136
139
 
140
+ # Add role to VM Group
141
+ #
142
+ # @param template [String] String template for the new role
143
+ #
144
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
145
+ # otherwise
146
+ def role_add(template)
147
+ return call(VMGROUP_METHODS[:roleadd], @pe_id, template)
148
+ end
149
+
150
+ # Delete role from VM Group
151
+ #
152
+ # @param roleid [Integer] ID of the role to remove
153
+ #
154
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
155
+ # otherwise
156
+ def role_delete(roleid)
157
+ return call(VMGROUP_METHODS[:roledelete], @pe_id, roleid)
158
+ end
159
+
160
+ # Update VM Group role
161
+ #
162
+ # @param roleid [Integer] ID of the role to remove
163
+ # @param template [String] String template with updated values
164
+ #
165
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
166
+ # otherwise
167
+ def role_update(roleid, template)
168
+ return call(VMGROUP_METHODS[:roleupdate], @pe_id, roleid, template)
169
+ end
170
+
137
171
  #######################################################################
138
172
  # Helpers to get VMGroup information
139
173
  #######################################################################
data/lib/opennebula.rb CHANGED
@@ -73,9 +73,11 @@ require 'opennebula/hook'
73
73
  require 'opennebula/hook_pool'
74
74
  require 'opennebula/hook_log'
75
75
  require 'opennebula/flow'
76
+ require 'opennebula/backupjob'
77
+ require 'opennebula/backupjob_pool'
76
78
 
77
79
  module OpenNebula
78
80
 
79
81
  # OpenNebula version
80
- VERSION = '6.6.3'
82
+ VERSION = '6.8.0'
81
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opennebula
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.6.3
4
+ version: 6.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenNebula
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-25 00:00:00.000000000 Z
11
+ date: 2023-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -155,6 +155,8 @@ files:
155
155
  - lib/opennebula.rb
156
156
  - lib/opennebula/acl.rb
157
157
  - lib/opennebula/acl_pool.rb
158
+ - lib/opennebula/backupjob.rb
159
+ - lib/opennebula/backupjob_pool.rb
158
160
  - lib/opennebula/client.rb
159
161
  - lib/opennebula/cluster.rb
160
162
  - lib/opennebula/cluster_pool.rb