opennebula 6.6.3 → 6.8.0

Sign up to get free protection for your applications and to get access to all the features.
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