opennebula 5.10.5 → 5.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ActionManager.rb +1 -1
  3. data/lib/CommandManager.rb +13 -3
  4. data/lib/DriverExecHelper.rb +19 -18
  5. data/lib/OpenNebulaDriver.rb +38 -62
  6. data/lib/VirtualMachineDriver.rb +1 -1
  7. data/lib/cloud/CloudClient.rb +3 -3
  8. data/lib/opennebula.rb +2 -2
  9. data/lib/opennebula/acl.rb +1 -1
  10. data/lib/opennebula/acl_pool.rb +1 -1
  11. data/lib/opennebula/client.rb +1 -1
  12. data/lib/opennebula/cluster.rb +1 -1
  13. data/lib/opennebula/cluster_pool.rb +1 -1
  14. data/lib/opennebula/datastore.rb +1 -1
  15. data/lib/opennebula/datastore_pool.rb +1 -1
  16. data/lib/opennebula/document.rb +1 -1
  17. data/lib/opennebula/document_json.rb +1 -1
  18. data/lib/opennebula/document_pool.rb +2 -2
  19. data/lib/opennebula/document_pool_json.rb +3 -3
  20. data/lib/opennebula/error.rb +1 -1
  21. data/lib/opennebula/group.rb +1 -1
  22. data/lib/opennebula/group_pool.rb +1 -1
  23. data/lib/opennebula/hook.rb +1 -1
  24. data/lib/opennebula/hook_log.rb +1 -1
  25. data/lib/opennebula/hook_pool.rb +1 -1
  26. data/lib/opennebula/host.rb +13 -5
  27. data/lib/opennebula/host_pool.rb +11 -5
  28. data/lib/opennebula/image.rb +1 -1
  29. data/lib/opennebula/image_pool.rb +1 -1
  30. data/lib/opennebula/ldap_auth.rb +89 -19
  31. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  32. data/lib/opennebula/marketplace.rb +1 -1
  33. data/lib/opennebula/marketplace_pool.rb +1 -1
  34. data/lib/opennebula/marketplaceapp.rb +4 -3
  35. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  36. data/lib/opennebula/oneflow_client.rb +24 -3
  37. data/lib/opennebula/pool.rb +4 -10
  38. data/lib/opennebula/pool_element.rb +9 -37
  39. data/lib/opennebula/security_group.rb +1 -1
  40. data/lib/opennebula/security_group_pool.rb +1 -1
  41. data/lib/opennebula/server_cipher_auth.rb +1 -1
  42. data/lib/opennebula/server_x509_auth.rb +1 -1
  43. data/lib/opennebula/ssh_auth.rb +1 -1
  44. data/lib/opennebula/system.rb +3 -3
  45. data/lib/opennebula/template.rb +1 -1
  46. data/lib/opennebula/template_pool.rb +1 -1
  47. data/lib/opennebula/user.rb +1 -1
  48. data/lib/opennebula/user_pool.rb +1 -1
  49. data/lib/opennebula/utils.rb +1 -1
  50. data/lib/opennebula/vdc.rb +1 -1
  51. data/lib/opennebula/vdc_pool.rb +1 -1
  52. data/lib/opennebula/virtual_machine.rb +8 -7
  53. data/lib/opennebula/virtual_machine_pool.rb +9 -7
  54. data/lib/opennebula/virtual_network.rb +9 -3
  55. data/lib/opennebula/virtual_network_pool.rb +1 -1
  56. data/lib/opennebula/virtual_router.rb +1 -1
  57. data/lib/opennebula/virtual_router_pool.rb +1 -1
  58. data/lib/opennebula/vm_group.rb +1 -1
  59. data/lib/opennebula/vm_group_pool.rb +1 -1
  60. data/lib/opennebula/vntemplate.rb +1 -1
  61. data/lib/opennebula/vntemplate_pool.rb +1 -1
  62. data/lib/opennebula/x509_auth.rb +1 -1
  63. data/lib/opennebula/xml_element.rb +1 -1
  64. data/lib/opennebula/xml_pool.rb +1 -1
  65. data/lib/opennebula/xml_utils.rb +1 -1
  66. data/lib/opennebula/zone.rb +1 -1
  67. data/lib/opennebula/zone_pool.rb +1 -1
  68. data/lib/vcenter_driver.rb +10 -3
  69. metadata +4 -4
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -52,7 +52,7 @@ module OpenNebula
52
52
  #
53
53
  # @param [OpenNebula::Client] client the xml-rpc client
54
54
  # @param [Integer] user_id the filter flag, see
55
- # http://opennebula.org/documentation:rel3.6:api
55
+ # http://docs.opennebula.io/stable/integration/system_interfaces/api.html
56
56
  #
57
57
  # @return [DocumentPool] the new object
58
58
  def initialize(client, user_id=-1)
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -41,7 +41,7 @@ module OpenNebula
41
41
 
42
42
  hash['DOCUMENT_POOL']['DOCUMENT'].each { |doc|
43
43
  body = doc['TEMPLATE']["#{TEMPLATE_TAG}"]
44
- if body
44
+ if body && !body.empty?
45
45
  b_hash = JSON.parse(body)
46
46
  doc['TEMPLATE']["#{TEMPLATE_TAG}"] = b_hash
47
47
  end
@@ -55,4 +55,4 @@ module OpenNebula
55
55
  end
56
56
  end
57
57
  end
58
- end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -125,6 +125,14 @@ module OpenNebula
125
125
  set_status("OFFLINE")
126
126
  end
127
127
 
128
+ #Resets monitoring forcing an update
129
+ def forceupdate()
130
+ rc = offline
131
+ return rc if OpenNebula.is_error?(rc)
132
+
133
+ enable
134
+ end
135
+
128
136
  def flush(action)
129
137
  self.disable
130
138
 
@@ -134,7 +142,7 @@ module OpenNebula
134
142
  rc = vm_pool.info
135
143
  if OpenNebula.is_error?(rc)
136
144
  puts rc.message
137
- exit -1
145
+ exit(-1)
138
146
  end
139
147
 
140
148
  vm_pool.each do |vm|
@@ -184,8 +192,7 @@ module OpenNebula
184
192
  # ["1337266088", "800"]]
185
193
  # }
186
194
  def monitoring(xpath_expressions)
187
- return super(HOST_METHODS[:monitoring], 'HOST',
188
- 'LAST_MON_TIME', xpath_expressions)
195
+ return super(HOST_METHODS[:monitoring], xpath_expressions)
189
196
  end
190
197
 
191
198
  # Retrieves this Host's monitoring data from OpenNebula, in XML
@@ -240,7 +247,8 @@ module OpenNebula
240
247
  vi_client = VCenterDriver::VIClient.new_from_host(self["ID"])
241
248
  importer = VCenterDriver::VmmImporter.new(@client, vi_client)
242
249
 
243
- return importer.import({wild: wild, template: template, one_item: vm, host: self['ID']})
250
+ return importer.import({wild: wild, template: template,
251
+ one_item: vm, host: self['ID']})
244
252
  else
245
253
  rc = vm.allocate(template)
246
254
 
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -94,15 +94,21 @@ module OpenNebula
94
94
  # "HOST_SHARE/FREE_CPU"=>[["1337609673", "800"]],
95
95
  # "HOST_SHARE/RUNNING_VMS"=>[["1337609673", "3"]]}}
96
96
  def monitoring(xpath_expressions)
97
- return super(HOST_POOL_METHODS[:monitoring],
98
- 'HOST', 'LAST_MON_TIME', xpath_expressions)
97
+ return super(HOST_POOL_METHODS[:monitoring], xpath_expressions)
99
98
  end
100
99
 
101
100
  # Retrieves the monitoring data for all the Hosts in the pool, in XML
102
101
  #
102
+ # @param [Integer] num Optional Retrieve monitor records in the last num
103
+ # seconds. 0 just the last record, -1 or nil all records
104
+ #
103
105
  # @return [String] VM monitoring data, in XML
104
- def monitoring_xml()
105
- return @client.call(HOST_POOL_METHODS[:monitoring])
106
+ def monitoring_xml(num = nil)
107
+ return @client.call(HOST_POOL_METHODS[:monitoring]) if num.nil?
108
+
109
+ @client.call(HOST_POOL_METHODS[:monitoring], num.to_i)
106
110
  end
111
+
107
112
  end
113
+
108
114
  end
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # ---------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -50,7 +50,8 @@ class OpenNebula::LdapAuth
50
50
  :mapping_key => 'GROUP_DN',
51
51
  :mapping_default => 1,
52
52
  :attributes => [ "memberOf" ],
53
- :rfc2307bis => true
53
+ :rfc2307bis => true,
54
+ :group_admin_group_dn => nil
54
55
  }.merge(options)
55
56
 
56
57
  ops={}
@@ -63,9 +64,8 @@ class OpenNebula::LdapAuth
63
64
  }
64
65
  end
65
66
 
66
- if !@options[:rfc2307bis]
67
- @options[:attributes] << @options[:user_field]
68
- end
67
+ # always fetch user_filed to compare whitespace diff
68
+ @options[:attributes] << @options[:user_field]
69
69
 
70
70
  # fetch the user group field only if we need that
71
71
  if @options[:group] or !@options[:rfc2307bis]
@@ -142,15 +142,20 @@ class OpenNebula::LdapAuth
142
142
 
143
143
  if result && result.first
144
144
  @user = result.first
145
- [@user.dn, @user[@options[:user_group_field]]]
145
+
146
+ [@user.dn,
147
+ @user[@options[:user_field]].first,
148
+ @user[@options[:user_group_field]]]
146
149
  else
147
150
  result=@ldap.search(:base => name)
148
151
 
149
152
  if result && result.first
150
153
  @user = result.first
151
- [name, @user[@options[:user_group_field]]]
154
+ [name,
155
+ @user[@options[:user_field]].first,
156
+ @user[@options[:user_group_field]]]
152
157
  else
153
- [nil, nil]
158
+ [nil, nil, nil]
154
159
  end
155
160
  end
156
161
  end
@@ -190,26 +195,26 @@ class OpenNebula::LdapAuth
190
195
  end
191
196
 
192
197
  def get_groups
193
- groups = []
194
-
195
198
  if @options[:rfc2307bis]
196
- [@user['memberOf']].flatten.each do |group|
197
- if (g = in_hash_ignore_case?(@mapping, group))
198
- groups << @mapping[g]
199
- end
200
- end
199
+ ldap_groups = [@user['memberOf']].flatten
201
200
  else
202
201
  group_base = @options[:group_base] ? @options[:group_base] : @options[:base]
203
202
  filter = Net::LDAP::Filter.equals(@options[:group_field], @user[@options[:user_group_field]].first)
204
- @ldap.search(
203
+ ldap_groups = @ldap.search(
205
204
  :base => group_base,
206
205
  :attributes => [ "dn" ],
207
206
  :filter => filter
208
- ) do |entry|
209
- if (g = in_hash_ignore_case?(@mapping, entry.dn))
207
+ ).map! { |entry| entry.dn }
208
+ end
209
+
210
+ groups = []
211
+ ldap_groups.each do |group|
212
+ if (g = in_hash_ignore_case?(@mapping, group))
213
+ if ldap_groups.include? @options[:group_admin_group_dn]
214
+ groups << "*#{@mapping[g]}"
215
+ else
210
216
  groups << @mapping[g]
211
217
  end
212
-
213
218
  end
214
219
  end
215
220
 
@@ -217,3 +222,68 @@ class OpenNebula::LdapAuth
217
222
  groups.compact.uniq
218
223
  end
219
224
  end
225
+
226
+
227
+ # ---------------------------------------------------------------------------- #
228
+ # Helper functions to parse ldap_auth.conf server entries
229
+ # ---------------------------------------------------------------------------- #
230
+ def to_array(name)
231
+ if name.is_a? Array
232
+ name
233
+ elsif name.is_a? Hash
234
+ if name.keys.size == 1
235
+ [name.values].flatten
236
+ else
237
+ STDERR.puts "invalid group configuration: #{name}"
238
+ exit(-1)
239
+ end
240
+ else
241
+ [name]
242
+ end
243
+ end
244
+
245
+ def get_server_order(opts, user)
246
+ order = []
247
+
248
+ if opts[:order] && opts[:match_user_regex]
249
+ STDERR.puts ":order and :match_user_regex are mutually exclusive"
250
+ exit(-1)
251
+ end
252
+
253
+ if opts[:order]
254
+ if opts[:order].class != Array
255
+ STDERR.puts ":order value malformed, must be an Array"
256
+ exit(-1)
257
+ end
258
+
259
+ opts[:order].each do |name|
260
+ order << to_array(name)
261
+ end
262
+
263
+ elsif opts[:match_user_regex]
264
+ if opts[:match_user_regex].class != Hash || opts[:match_user_regex].empty?
265
+ STDERR.puts ":match_user_regex value malformed, must be an Hash"
266
+ exit(-1)
267
+ end
268
+
269
+ opts[:match_user_regex].each do |regex, server|
270
+ if m = user.match(/#{regex}/i)
271
+
272
+ # update user with the capture
273
+ user = m[1] if m[1]
274
+
275
+ order << to_array(server)
276
+ end
277
+ end
278
+
279
+ if order.empty?
280
+ STDERR.puts "User #{user} does not mach any regex"
281
+ end
282
+
283
+ else
284
+ STDERR.puts "missing either :order or :match_user_regex in configuration"
285
+ exit(-1)
286
+ end
287
+
288
+ return [order, user]
289
+ end
@@ -1,5 +1,5 @@
1
1
  # ---------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -192,8 +192,9 @@ module OpenNebula
192
192
  options[:vmtemplate_name] = name unless options[:vmtemplate_name]
193
193
 
194
194
  tmpl << "\n"
195
- tmpl << "NAME=\"" << name << "\"\n"
196
- tmpl << "FROM_APP=\"" << self['ID'] << "\"\n"
195
+ tmpl << 'NAME="' << name << "\"\n"
196
+ tmpl << 'FROM_APP="' << self['ID'] << "\"\n"
197
+ tmpl << 'URL_ARGS="' << options[:url_args] << "\"\n" if options[:url_args]
197
198
 
198
199
  case type_str
199
200
  when 'IMAGE'
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2019, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -36,7 +36,12 @@ module Role
36
36
  'reboot-hard',
37
37
  'poweroff',
38
38
  'poweroff-hard',
39
- 'snapshot-create'
39
+ 'snapshot-create',
40
+ 'snapshot-revert',
41
+ 'snapshot-delete',
42
+ 'disk-snapshot-create',
43
+ 'disk-snapshot-revert',
44
+ 'disk-snapshot-delete'
40
45
  ]
41
46
 
42
47
  STATE = {
@@ -285,7 +290,7 @@ module Service
285
290
  exit_code = 0
286
291
 
287
292
  ids.each do |id|
288
- response = block.call(id)
293
+ response = block.call(id) if block_given?
289
294
 
290
295
  if CloudClient::is_error?(response)
291
296
  puts response.to_s
@@ -296,6 +301,22 @@ module Service
296
301
  exit_code
297
302
  end
298
303
 
304
+ # Perform an action on a resource
305
+ # @param [Integer] id resource id
306
+ # @param [Block] block action to be performed
307
+ # @return [Integer] exit_code
308
+ def self.perform_action(id, &block)
309
+ exit_code = 0
310
+ response = block.call(id) if block_given?
311
+
312
+ if CloudClient::is_error?(response)
313
+ puts response.to_s
314
+ exit_code = response.code.to_i
315
+ end
316
+
317
+ exit_code
318
+ end
319
+
299
320
  class Client
300
321
  def initialize(opts={})
301
322
  @username = opts[:username] || ENV['ONEFLOW_USER']