opennebula-cli 7.0.2 → 7.1.80.pre

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/bin/oneform +150 -0
  3. data/bin/onemarketapp +3 -0
  4. data/bin/onetemplate +15 -4
  5. data/bin/oneuser +2 -1
  6. data/bin/onevm +50 -5
  7. data/bin/onevntemplate +2 -1
  8. data/bin/onezone +11 -5
  9. data/lib/cli_helper.rb +152 -0
  10. data/lib/one_helper/oneacct_helper.rb +2 -0
  11. data/lib/one_helper/oneacl_helper.rb +2 -0
  12. data/lib/one_helper/onebackupjob_helper.rb +2 -0
  13. data/lib/one_helper/onecluster_helper.rb +3 -0
  14. data/lib/one_helper/onedatastore_helper.rb +3 -0
  15. data/lib/one_helper/oneform_helper.rb +255 -0
  16. data/lib/one_helper/onegroup_helper.rb +132 -97
  17. data/lib/one_helper/onehook_helper.rb +4 -1
  18. data/lib/one_helper/onehost_helper.rb +5 -2
  19. data/lib/one_helper/oneimage_helper.rb +2 -0
  20. data/lib/one_helper/onemarket_helper.rb +2 -0
  21. data/lib/one_helper/onemarketapp_helper.rb +3 -0
  22. data/lib/one_helper/onequota_helper.rb +104 -21
  23. data/lib/one_helper/onesecgroup_helper.rb +2 -0
  24. data/lib/one_helper/onetemplate_helper.rb +2 -1
  25. data/lib/one_helper/oneuser_helper.rb +164 -155
  26. data/lib/one_helper/onevdc_helper.rb +2 -0
  27. data/lib/one_helper/onevm_helper.rb +5 -1
  28. data/lib/one_helper/onevmgroup_helper.rb +2 -0
  29. data/lib/one_helper/onevnet_helper.rb +15 -4
  30. data/lib/one_helper/onevntemplate_helper.rb +2 -1
  31. data/lib/one_helper/onevrouter_helper.rb +2 -0
  32. data/lib/one_helper/onezone_helper.rb +15 -4
  33. data/lib/one_helper.rb +51 -16
  34. data/share/schemas/xsd/group.xsd +8 -0
  35. data/share/schemas/xsd/group_pool.xsd +8 -0
  36. data/share/schemas/xsd/opennebula_configuration.xsd +4 -8
  37. data/share/schemas/xsd/shared.xsd +8 -0
  38. data/share/schemas/xsd/user.xsd +8 -0
  39. data/share/schemas/xsd/user_pool.xsd +8 -0
  40. data/share/schemas/xsd/vm.xsd +20 -0
  41. data/share/schemas/xsd/vnet.xsd +2 -1
  42. data/share/schemas/xsd/vnet_pool.xsd +1 -0
  43. metadata +9 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50f8ce43747b5160398fa1f2c496d59ac6854d2a3c23d3c8beaf29ed7a0638e0
4
- data.tar.gz: 0d2b1ec37420a46c6832cb0ddbd2e53c3659c6fc4e2b7573e97c3ff095d8a61a
3
+ metadata.gz: 4e0a75e568244920c5ad5f5ef5c91d27a059240c29290bdbcadc12825689f7ff
4
+ data.tar.gz: 8b999e8828e35f872fc7c98b81d4a2643c84e0731a135c5bf76b141a8581aeec
5
5
  SHA512:
6
- metadata.gz: 9d72ac369a7d99c345d06a2a2bc930f025568cb07e5bb5e72dabafe4244ca5820bd956152ac2c58f8abba28f15e2431fc29fe59ac55f38902c4b8fcd6534c1c4
7
- data.tar.gz: 6f034ba5eab33981186ce1088b04456990ef375ad62586998c7fe4bc9099e865fe0263bc7679aefa7427d4714c7dedcc008952756abdc0819f9574ee993a37fe
6
+ metadata.gz: c4713fa611ccaad4a2b733056c749b8ef77a0b6cfddcdae0bc54b3638b9c84e945c44a173114515104f80e2b5cc34de31aba637a870ea7cdb2d5c40b37b2e5fd
7
+ data.tar.gz: 1fca59489877e11016777870efe98bc8d3a58099ec2cb24d1717a4cc8a1c6e16ec2024d7f88760ed5732e49d3f16e508e37d7582f3b230b7f259211c3075c62e
data/bin/oneform ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # -------------------------------------------------------------------------- #
4
+ # Copyright 2002-2025, OpenNebula Project, OpenNebula Systems #
5
+ # #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
7
+ # not use this file except in compliance with the License. You may obtain #
8
+ # a copy of the License at #
9
+ # #
10
+ # http://www.apache.org/licenses/LICENSE-2.0 #
11
+ # #
12
+ # Unless required by applicable law or agreed to in writing, software #
13
+ # distributed under the License is distributed on an "AS IS" BASIS, #
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15
+ # See the License for the specific language governing permissions and #
16
+ # limitations under the License. #
17
+ #--------------------------------------------------------------------------- #
18
+
19
+ ONE_LOCATION = ENV['ONE_LOCATION']
20
+
21
+ if !ONE_LOCATION
22
+ LIB_LOCATION = '/usr/lib/one'
23
+ RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
24
+ GEMS_LOCATION = '/usr/share/one/gems'
25
+ else
26
+ LIB_LOCATION = ONE_LOCATION + '/lib'
27
+ RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
28
+ GEMS_LOCATION = ONE_LOCATION + '/share/gems'
29
+ end
30
+
31
+ require 'load_opennebula_paths'
32
+
33
+ $LOAD_PATH << RUBY_LIB_LOCATION
34
+ $LOAD_PATH << RUBY_LIB_LOCATION + '/cli'
35
+
36
+ USER_AGENT = 'CLI'
37
+
38
+ require 'command_parser'
39
+ require 'one_helper'
40
+ require 'opennebula/oneform_client'
41
+ require 'tempfile'
42
+
43
+ require 'one_helper/oneform_helper'
44
+
45
+ CommandParser::CmdParser.new(ARGV) do
46
+ FORMAT = [OpenNebulaHelper::JSON, OpenNebulaHelper::YAML]
47
+
48
+ usage '`oneform` <command> [<args>] [<options>]'
49
+ version OpenNebulaHelper::ONE_VERSION
50
+
51
+ set :option, OneForm::Client::DEFAULT_OPTIONS
52
+ set :option, CommandParser::VERSION
53
+ set :option, CommandParser::HELP
54
+
55
+ ENABLED = {
56
+ :name => 'enabled',
57
+ :short => '-e',
58
+ :large => '--enabled',
59
+ :description => 'Show only enabled providers',
60
+ :default => false
61
+ }
62
+
63
+ helper = OneFormHelper.new
64
+
65
+ ########################################################################
66
+ # Commands for interacting with oneform drivers
67
+ ########################################################################
68
+
69
+ list_desc = <<-EOT.unindent
70
+ List all drivers installed on the system.
71
+ EOT
72
+
73
+ command :list,
74
+ list_desc,
75
+ :options => FORMAT + CLIHelper::OPTIONS + [OpenNebulaHelper::DESCRIBE, ENABLED] do
76
+ enabled = options[:enabled] || false
77
+ helper.list_driver_pool(helper.client(options), options, :enabled => enabled)
78
+ end
79
+
80
+ top_desc = <<-EOT.unindent
81
+ List the available drivers continuously.
82
+ EOT
83
+
84
+ command :top,
85
+ top_desc,
86
+ :options => FORMAT + [CLIHelper::DELAY, ENABLED] do
87
+ Signal.trap('INT') { exit(-1) }
88
+ enabled = options[:enabled] || false
89
+ helper.top_driver_pool(helper.client(options), options, :enabled => enabled)
90
+
91
+ 0
92
+ end
93
+
94
+ show_desc = <<-EOT.unindent
95
+ Show details of a specific driver.
96
+ EOT
97
+
98
+ command :show, show_desc, :driver_name, :options => FORMAT do
99
+ driver_name = args[0]
100
+
101
+ helper.format_resource(helper.client(options), driver_name, options)
102
+ end
103
+
104
+ sync_desc = <<-EOT.unindent
105
+ Synchronize OneForm drivers, refreshing and update available drivers
106
+ EOT
107
+
108
+ command :sync, sync_desc do
109
+ client = helper.client(options)
110
+
111
+ response = client.sync_drivers
112
+
113
+ return [response[:err_code], response[:message]] \
114
+ if CloudClient.is_error?(response)
115
+
116
+ 0
117
+ end
118
+
119
+ enable_desc = <<-EOT.unindent
120
+ Enable a driver in OneForm to permit the creation of new resources based on it.
121
+ EOT
122
+
123
+ command :enable, enable_desc, :driver_name do
124
+ client = helper.client(options)
125
+ driver_name = args[0]
126
+
127
+ response = client.enable_driver(driver_name)
128
+
129
+ return [response[:err_code], response[:message]] \
130
+ if CloudClient.is_error?(response)
131
+
132
+ 0
133
+ end
134
+
135
+ disable_desc = <<-EOT.unindent
136
+ Disables a driver in OneForm, avoiding creation of new resources based on it.
137
+ EOT
138
+
139
+ command :disable, disable_desc, :driver_name do
140
+ client = helper.client(options)
141
+ driver_name = args[0]
142
+
143
+ response = client.disable_driver(driver_name)
144
+
145
+ return [response[:err_code], response[:message]] \
146
+ if CloudClient.is_error?(response)
147
+
148
+ 0
149
+ end
150
+ end
data/bin/onemarketapp CHANGED
@@ -39,6 +39,9 @@ require 'command_parser'
39
39
  require 'one_helper/onemarketapp_helper'
40
40
  require 'one_helper/onemarket_helper'
41
41
  require 'one_helper/onedatastore_helper'
42
+ require 'opennebula/template_pool'
43
+ require 'opennebula/virtual_machine_pool'
44
+ require 'opennebula/flow'
42
45
 
43
46
  CommandParser::CmdParser.new(ARGV) do
44
47
  usage '`onemarket` <command> [<args>] [<options>]'
data/bin/onetemplate CHANGED
@@ -203,7 +203,8 @@ CommandParser::CmdParser.new(ARGV) do
203
203
  if !OpenNebula.is_error?(res)
204
204
  puts "ID: #{res}"
205
205
  else
206
- puts res.message
206
+ STDERR.puts res.message
207
+ exit(-1)
207
208
  end
208
209
  end
209
210
  end
@@ -253,6 +254,18 @@ CommandParser::CmdParser.new(ARGV) do
253
254
 
254
255
  number = options[:multiple] || 1
255
256
  user_inputs = options[:user_inputs]
257
+ persistent = !options[:persistent].nil?
258
+ flag = nil
259
+
260
+ if persistent && (number > 1)
261
+ if options[:name] && !options[:name].include?('%i')
262
+ flag = '-%i'
263
+ elsif !options[:name]
264
+ STDERR.puts 'Persistent template cannot be instantiated multiple times ' <<
265
+ "without including a name (use '%i' to chose where to insert the index)."
266
+ next -1
267
+ end
268
+ end
256
269
 
257
270
  number.times.each_with_index do |i, index|
258
271
  exit_code = helper.perform_action(
@@ -260,7 +273,7 @@ CommandParser::CmdParser.new(ARGV) do
260
273
  options,
261
274
  'instantiated'
262
275
  ) do |t|
263
- name = options[:name]
276
+ name = "#{options[:name]}#{flag}" if options[:name]
264
277
  prefix = options[:prefix]
265
278
  c_i = nil
266
279
  p = nil
@@ -325,8 +338,6 @@ CommandParser::CmdParser.new(ARGV) do
325
338
 
326
339
  extra_template.strip!
327
340
 
328
- persistent = !options[:persistent].nil?
329
-
330
341
  res = t.instantiate(name, on_hold, extra_template, persistent)
331
342
 
332
343
  if !OpenNebula.is_error?(res)
data/bin/oneuser CHANGED
@@ -52,7 +52,8 @@ CommandParser::CmdParser.new(ARGV) do
52
52
  rescue StandardError => e
53
53
  STDERR.puts e.message
54
54
 
55
- if e.message != OpenNebula::Client::NO_ONE_AUTH_ERROR
55
+ if e.message != OpenNebula::Client::NO_ONE_AUTH_ERROR &&
56
+ e.message != OpenNebula::Client::GRPC_NOT_SUPPORTED
56
57
  STDERR.puts e.backtrace
57
58
  end
58
59
 
data/bin/onevm CHANGED
@@ -41,6 +41,8 @@ require 'one_helper/onevm_helper'
41
41
  require 'one_helper/onedatastore_helper'
42
42
  require 'opennebula/virtual_machine_ext'
43
43
 
44
+ require 'base64'
45
+
44
46
  CommandParser::CmdParser.new(ARGV) do
45
47
  usage '`onevm` <command> [<args>] [<options>]'
46
48
  version OpenNebulaHelper::ONE_VERSION
@@ -256,6 +258,13 @@ CommandParser::CmdParser.new(ARGV) do
256
258
  :description => 'Use only selected disk ID'
257
259
  }
258
260
 
261
+ CMD_STDIN = {
262
+ :name => 'stdin',
263
+ :large => '--stdin stdin',
264
+ :format => String,
265
+ :description => 'Data to pass to the command executed on the VM'
266
+ }
267
+
259
268
  OpenNebulaHelper::TEMPLATE_OPTIONS_VM.delete_if do |v|
260
269
  ['as_gid', 'as_uid'].include?(v[:name])
261
270
  end
@@ -647,8 +656,7 @@ CommandParser::CmdParser.new(ARGV) do
647
656
 
648
657
  migrate_desc = <<-EOT.unindent
649
658
  Migrates the given running VM to another Host. If used with --live
650
- parameter the migration is done without downtime. Datastore migration is
651
- not supported for --live flag.
659
+ parameter the migration is done without downtime.
652
660
 
653
661
  States: RUNNING
654
662
  EOT
@@ -1272,7 +1280,7 @@ CommandParser::CmdParser.new(ARGV) do
1272
1280
  )
1273
1281
 
1274
1282
  if !rc.nil?
1275
- puts rc.message
1283
+ STDERR.puts rc.message
1276
1284
  exit(-1)
1277
1285
  end
1278
1286
 
@@ -1517,6 +1525,43 @@ CommandParser::CmdParser.new(ARGV) do
1517
1525
  end
1518
1526
  end
1519
1527
 
1528
+ exec_desc = <<-EOT.unindent
1529
+ Execute a command inside a Virtual Machine.
1530
+
1531
+ States: RUNNING.
1532
+ EOT
1533
+
1534
+ command :exec, exec_desc, [:range, :vmid_list], :cmd,
1535
+ :options => [CMD_STDIN] do
1536
+ helper.perform_actions(args[0], options, 'Executing a command') do |vm|
1537
+ vm.exec(args[1], Base64.strict_encode64(options[:stdin] || ''))
1538
+ end
1539
+ end
1540
+
1541
+ exec_retry_desc = <<-EOT.unindent
1542
+ Retries to execute the last command executed inside a Virtual Machine.
1543
+
1544
+ States: RUNNING.
1545
+ EOT
1546
+
1547
+ command :"exec-retry", exec_retry_desc, [:range, :vmid_list] do
1548
+ helper.perform_actions(args[0], options, 'Retrying last command') do |vm|
1549
+ vm.exec_retry
1550
+ end
1551
+ end
1552
+
1553
+ exec_cancel_desc = <<-EOT.unindent
1554
+ Cancel the command being executed inside a Virtual Machine.
1555
+
1556
+ States: RUNNING.
1557
+ EOT
1558
+
1559
+ command :"exec-cancel", exec_cancel_desc, [:range, :vmid_list] do
1560
+ helper.perform_actions(args[0], options, 'Canceling last command') do |vm|
1561
+ vm.exec_cancel
1562
+ end
1563
+ end
1564
+
1520
1565
  ########################### Charters Management ############################
1521
1566
 
1522
1567
  create_chart_desc = <<-EOT.unindent
@@ -1685,7 +1730,7 @@ CommandParser::CmdParser.new(ARGV) do
1685
1730
  You can specify the PCI device with --pci (short_address) or
1686
1731
  --pci_device (device ID), --pci_class (class ID) and/or --pci_vendor (vendor ID).
1687
1732
 
1688
- States: POWEROFF
1733
+ States: POWEROFF, UNDEPLOYED
1689
1734
  EOT
1690
1735
 
1691
1736
  command :"pci-attach", pci_attach_desc, :vmid,
@@ -1730,7 +1775,7 @@ CommandParser::CmdParser.new(ARGV) do
1730
1775
  pci_detach_desc = <<-EOT.unindent
1731
1776
  Detaches a PCI device from a VM
1732
1777
 
1733
- States: POWEROFF
1778
+ States: POWEROFF, UNDEPLOYED
1734
1779
  EOT
1735
1780
 
1736
1781
  command :"pci-detach", pci_detach_desc, :vmid, :pciid do
data/bin/onevntemplate CHANGED
@@ -173,7 +173,8 @@ CommandParser::CmdParser.new(ARGV) do
173
173
  if !OpenNebula.is_error?(res)
174
174
  puts "ID: #{res}"
175
175
  else
176
- puts res.message
176
+ STDERR.puts res.message
177
+ exit(-1)
177
178
  end
178
179
  end
179
180
  end
data/bin/onezone CHANGED
@@ -130,15 +130,20 @@ CommandParser::CmdParser.new(ARGV) do
130
130
  EOT
131
131
 
132
132
  command :"server-add", addserver_desc, :zoneid, :options =>
133
- [OneZoneHelper::SERVER_NAME, OneZoneHelper::SERVER_ENDPOINT] do
134
- if options[:server_name].nil? || options[:server_rpc].nil?
133
+ [OneZoneHelper::SERVER_NAME, OneZoneHelper::SERVER_ENDPOINT,
134
+ OneZoneHelper::SERVER_ENDPOINT_GRPC] do
135
+ if options[:server_name].nil? || (options[:server_rpc].nil? && options[:server_grpc].nil?)
135
136
  STDERR.puts 'To add a server set:'
136
137
  STDERR.puts "\t-n <server name>"
137
- STDERR.puts "\t-r <RPC endpoint>"
138
+ STDERR.puts "\t-r <XML-RPC endpoint>"
139
+ STDERR.puts "\t-g <gRPC endpoint>"
138
140
  exit(-1)
139
141
  end
140
142
 
141
- if !%r{^(http|https):\/\/}.match(options[:server_rpc])
143
+ puts "#{options[:server_rpc]}:#{options[:server_grpc]}"
144
+
145
+ if !options[:server_rpc].nil? && !options[:server_rpc].empty? &&
146
+ !%r{^(http|https):\/\/}.match(options[:server_rpc])
142
147
  puts 'Wrong protocol specified. Only http or https allowed!'
143
148
  exit(-1)
144
149
  end
@@ -146,7 +151,8 @@ CommandParser::CmdParser.new(ARGV) do
146
151
  template = <<-EOT
147
152
  SERVER = [
148
153
  NAME="#{options[:server_name]}",
149
- ENDPOINT="#{options[:server_rpc]}"
154
+ ENDPOINT="#{options[:server_rpc]}",
155
+ ENDPOINT_GRPC="#{options[:server_grpc]}"
150
156
  ]
151
157
  EOT
152
158
 
data/lib/cli_helper.rb CHANGED
@@ -265,6 +265,158 @@ module CLIHelper
265
265
  exit(-1)
266
266
  end
267
267
 
268
+ # Render a small HTML subset for terminal output
269
+ # Supports: p, br, ul/ol/li, strong/b, em/i, code, a[href]
270
+ def self.render_html(text)
271
+ return '' if text.nil? || text.empty?
272
+
273
+ use_ansi = $stdout.tty?
274
+
275
+ bold_on = use_ansi ? "\33[1m" : ''
276
+ faint_on = use_ansi ? "\33[2m" : ''
277
+ reset = use_ansi ? "\33[0m" : ''
278
+ mono_on = use_ansi ? "\33[36m" : ''
279
+ bullet = use_ansi ? '•' : '*'
280
+
281
+ # OSC 8 hyperlink escape (clickable links)
282
+ osc8_link = lambda do |label, url|
283
+ return "#{label} (#{url})" unless use_ansi
284
+
285
+ "\e]8;;#{url}\a#{label}\e]8;;\a"
286
+ end
287
+
288
+ s = text.to_s.gsub("\r\n", "\n")
289
+
290
+ # Block tags -> newlines
291
+ s = s.gsub(%r{<\s*br\s*/?\s*>}i, "\n")
292
+ s = s.gsub(%r{<\s*/\s*p\s*>}i, "\n")
293
+ s = s.gsub(/<\s*p\b[^>]*>/i, '')
294
+ s = s.gsub(%r{<\s*/\s*(div|section|article)\s*>}i, "\n\n")
295
+ s = s.gsub(/<\s*(div|section|article)\b[^>]*>/i, '')
296
+
297
+ # Links: <a href="url">label</a>
298
+ s = s.gsub(%r{<\s*a\b[^>]*href\s*=\s*["']([^"']+)["'][^>]*>(.*?)<\s*/\s*a\s*>}im) do
299
+ url = Regexp.last_match(1).strip
300
+ label = Regexp.last_match(2).to_s
301
+ osc8_link.call(label, url)
302
+ end
303
+
304
+ # Lists
305
+ s = s.gsub(/<\s*li\b[^>]*>/i, "#{bullet} ")
306
+
307
+ # Inline formatting
308
+ if use_ansi
309
+ s = s.gsub(%r{<\s*/\s*(strong|b)\s*>}i, reset)
310
+ s = s.gsub(/<\s*(strong|b)\b[^>]*>/i, bold_on)
311
+
312
+ s = s.gsub(%r{<\s*/\s*(em|i)\s*>}i, reset)
313
+ s = s.gsub(/<\s*(em|i)\b[^>]*>/i, faint_on)
314
+
315
+ # <code>
316
+ s = s.gsub(%r{<\s*code\b[^>]*>(.*?)<\s*/\s*code\s*>}im) do
317
+ "#{faint_on}#{mono_on}#{Regexp.last_match(1)}#{reset}"
318
+ end
319
+ else
320
+ s = s.gsub(%r{<\s*/?\s*(strong|b|em|i|code)\b[^>]*>}i, '')
321
+ end
322
+
323
+ # Remove any remaining tags
324
+ s = s.gsub(/<[^>]+>/, '')
325
+
326
+ # Decode minimal HTML entities
327
+ s = s.gsub('&amp;', '&')
328
+ .gsub('&lt;', '<')
329
+ .gsub('&gt;', '>')
330
+ .gsub('&quot;', '"')
331
+ .gsub('&#39;', "'")
332
+ .gsub('&nbsp;', ' ')
333
+
334
+ # Normalize whitespace
335
+ s = s.lines.map(&:rstrip).join("\n")
336
+ s.gsub(/\n{3,}/, "\n\n").strip
337
+ end
338
+
339
+ # Render a small Markdown subset for terminal output. Supports headings,
340
+ # bold, italic, inline code, and unordered lists.
341
+ def self.render_md(text)
342
+ return '' if text.nil? || text.empty?
343
+
344
+ use_ansi = $stdout.tty?
345
+
346
+ bold_on = use_ansi ? "\33[1m" : ''
347
+ reset = use_ansi ? "\33[0m" : ''
348
+ faint_on = use_ansi ? "\33[2m" : ''
349
+ mono_on = use_ansi ? "\33[36m" : ''
350
+ bullet = use_ansi ? '•' : '*'
351
+
352
+ # OSC 8 hyperlink sequences
353
+ osc8_link = lambda do |label, url|
354
+ return "#{label} (#{url})" unless use_ansi
355
+
356
+ "\e]8;;#{url}\a#{label}\e]8;;\a"
357
+ end
358
+
359
+ lines = text.to_s.gsub("\r\n", "\n").split("\n", -1)
360
+
361
+ out = lines.map do |line|
362
+ # Preserve empty lines
363
+ if line.strip.empty?
364
+ ''
365
+ # Headings (#, ##, ###)
366
+ elsif (m = line.match(/\A\s{0,3}(\#{1,3})\s+(.*)\z/))
367
+ title = m[2].strip
368
+ "#{bold_on}#{title}#{reset}"
369
+ # Unordered list items (*, -, +)
370
+ elsif (m = line.match(/\A(\s*)[*+-]\s+(.*)\z/))
371
+ indent = m[1]
372
+ item = m[2]
373
+ "#{indent}#{bullet} #{item}"
374
+ else
375
+ line
376
+ end
377
+ end.join("\n")
378
+
379
+ # Links [label](url)
380
+ out = out.gsub(/\[([^\]]+)\]\(([^)]+)\)/) do
381
+ label = Regexp.last_match(1)
382
+ url = Regexp.last_match(2).strip
383
+ osc8_link.call(label, url)
384
+ end
385
+
386
+ # Autolinks <https://example.com>
387
+ out = out.gsub(/<((?:https?|mailto):[^>\s]+)>/) do
388
+ url = Regexp.last_match(1)
389
+ osc8_link.call(url, url)
390
+ end
391
+
392
+ # Inline formatting (simple, non-nested)
393
+ if use_ansi
394
+ # Inline code: `code`
395
+ out = out.gsub(/`([^`]+)`/) do
396
+ m = Regexp.last_match(1)
397
+ "#{faint_on}#{mono_on}#{m}#{reset}"
398
+ end
399
+
400
+ # Bold: **text**
401
+ out = out.gsub(/\*\*([^\*]+)\*\*/) do
402
+ m = Regexp.last_match(1)
403
+ "#{bold_on}#{m}#{reset}"
404
+ end
405
+
406
+ # Italic: *text*
407
+ out = out.gsub(/(^|[^*])\*([^*\n]+)\*(?!\*)/) do
408
+ m1 = Regexp.last_match(1)
409
+ m2 = Regexp.last_match(2)
410
+ "#{m1}#{faint_on}#{m2}#{reset}"
411
+ end
412
+ else
413
+ # Non-tty, keep markdown mostly as-is, but normalize list bullets
414
+ out = out.gsub(/\A(\s*)[*+-]\s+/m, "\\1#{bullet} ")
415
+ end
416
+
417
+ out
418
+ end
419
+
268
420
  # Check if value is in base64
269
421
  #
270
422
  # @param value [String] Value to check
@@ -16,6 +16,8 @@
16
16
 
17
17
  require 'one_helper'
18
18
  require 'optparse/time'
19
+ require 'opennebula/virtual_machine'
20
+ require 'opennebula/virtual_machine_pool'
19
21
 
20
22
  class AcctHelper < OpenNebulaHelper::OneHelper
21
23
  TIME_ZONE_CUR = {
@@ -15,6 +15,8 @@
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
17
  require 'one_helper'
18
+ require 'opennebula/acl'
19
+ require 'opennebula/acl_pool'
18
20
 
19
21
  # Helper for oneacl command
20
22
  class OneAclHelper < OpenNebulaHelper::OneHelper
@@ -15,6 +15,8 @@
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
17
  require 'one_helper'
18
+ require 'opennebula/backupjob'
19
+ require 'opennebula/backupjob_pool'
18
20
 
19
21
  # Helper for onebackupjob command
20
22
  class OneBackupJobHelper < OpenNebulaHelper::OneHelper
@@ -15,6 +15,9 @@
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
17
  require 'one_helper'
18
+ require 'opennebula/cluster'
19
+ require 'opennebula/cluster_pool'
20
+ require 'opennebula/host'
18
21
 
19
22
  # OneCluster CLI command helper
20
23
  class OneClusterHelper < OpenNebulaHelper::OneHelper
@@ -15,6 +15,9 @@
15
15
  #--------------------------------------------------------------------------- #
16
16
 
17
17
  require 'one_helper'
18
+ require 'opennebula/datastore'
19
+ require 'opennebula/datastore_pool'
20
+ require 'opennebula/image'
18
21
 
19
22
  class OneDatastoreHelper < OpenNebulaHelper::OneHelper
20
23
  DATASTORE = {