fog-proxmox 1.0.0 → 1.1.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: 2a248dd1c4522b48cddfef2eccd5d07ee1c888fd72460ec333390e71b81803b5
4
- data.tar.gz: 3ba917b2e4ae32138edabc4abc8cd65d5248eb6404f367bb11cd61a4c7395180
3
+ metadata.gz: d571150b56652b8f22fc3172ac26caf7918bdc167c5202b9f0809f138693da0f
4
+ data.tar.gz: f26c7b0f98d625306b573bdc2c848974438b03260ac7814b796d055e9967c88a
5
5
  SHA512:
6
- metadata.gz: 962ebb8637afce2db9c75156efcaf0d296c41b9027f881824bc096046016077126c2efd498caa346fa3837310d42a67ae0dfba7854124b9664c36b520f073044
7
- data.tar.gz: a13abbba5806fd3414a52cfac3fb684c596eb7cd9edec744813274d41ca1afdf4d0e69e8d136ba9407691e60d24de4f2991f93aa2687186dadabdb4b13c6f726
6
+ metadata.gz: df89614d08202bce01cc9f9be6d0fe3bd03a763672016ef363a0666a70e08625c6babed6d5969344496334d1aa5f59ef9584bf4e10b1b8f1b39a60d648d8b3b0
7
+ data.tar.gz: 4fea613dea946a99e5376f6b691eaf6112c1f21d8116497a44e0cc67aba0ddb081a766941b9a1fb8922e40160e451791984a2c06176670fce9f979556fc730bd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0](https://github.com/fog/fog-proxmox/compare/v1.0.0...v1.1.0) (2026-10-21)
4
+
5
+
6
+ ### Features
7
+
8
+ * Add hotplug support ([#158](https://github.com/fog/fog-proxmox/issues/158)) ([0745f60](https://github.com/fog/fog-proxmox/commit/0745f6013003af9d0b1ec7304749a5c52038fa62))
9
+ * Add VirtIO RNG device support ([#160](https://github.com/fog/fog-proxmox/issues/160)) ([a31b9d3](https://github.com/fog/fog-proxmox/commit/a31b9d39927d82b2db9da6c1889b9fd556905b5c))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * ISO upload task polling ([3dc3c31](https://github.com/fog/fog-proxmox/commit/3dc3c31fe8cd72fc719954917dad39afa6b08ebd))
15
+
3
16
  ## [1.0.0](https://github.com/fog/fog-proxmox/compare/v0.16.2...v1.0.0) (2026-09-14)
4
17
 
5
18
 
data/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  # Fog::Proxmox
4
4
 
5
- ![CI](https://github.com/fog/fog-proxmox/workflows/CI/badge.svg)
5
+ [![CI](https://github.com/fog/fog-proxmox/actions/workflows/ci.yml/badge.svg)](https://github.com/fog/fog-proxmox/actions/workflows/ci.yml)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/dfcdcc32f096abf1b2b4/maintainability)](https://codeclimate.com/github/fog/fog-proxmox/maintainability)
7
7
  [![Test Coverage](https://api.codeclimate.com/v1/badges/dfcdcc32f096abf1b2b4/test_coverage)](https://codeclimate.com/github/fog/fog-proxmox/test_coverage)
8
8
  [![Gem Version](https://badge.fury.io/rb/fog-proxmox.svg)](https://badge.fury.io/rb/fog-proxmox)
9
9
 
10
- This is a [FOG](http://fog.io/) (>= 2.1) module gem to support [Proxmox VE](https://www.proxmox.com/en/proxmox-ve)
10
+ This is a [FOG](https://fog.github.io/) (>= 2.1) module gem to support [Proxmox VE](https://www.proxmox.com/en/proxmox-ve)
11
11
 
12
- It is intended to satisfy this [feature](https://github.com/fog/fog/issues/3644), but Proxmox provider only, and above all this [Foreman](http://www.theforeman.org) [feature](https://projects.theforeman.org/issues/2186).
12
+ It is intended to satisfy this [feature](https://github.com/fog/fog/issues/3644), but Proxmox provider only, and above all this [Foreman](https://www.theforeman.org) [feature](https://projects.theforeman.org/issues/2186).
13
13
 
14
14
  It is inspired by the great [fog-openstack](https://github.com/fog/fog-openstack) module.
15
15
 
@@ -102,7 +102,7 @@ bundle exec rake --tasks
102
102
  ## Contributing
103
103
 
104
104
  You can reach the [contributors](.github/CONTRIBUTORS.md).
105
- Bug reports and pull requests are welcome on GitHub at [Fog-Proxmox](https://github.com/fog/fog-proxmox/issues). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
105
+ Bug reports and pull requests are welcome on GitHub at [Fog-Proxmox](https://github.com/fog/fog-proxmox/issues). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org) code of conduct.
106
106
 
107
107
  Please read [how to contribute](.github/CONTRIBUTING.md).
108
108
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'fog/proxmox/helpers/rng_helper'
19
+
20
+ module Fog
21
+ module Proxmox
22
+ class Compute
23
+ # VirtIO RNG device; period is in milliseconds and max_bytes is in bytes.
24
+ class Rng < Fog::Model
25
+ attribute :max_bytes
26
+ attribute :period
27
+ attribute :source
28
+
29
+ def flatten
30
+ Fog::Proxmox::RngHelper.flatten(attributes)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -21,6 +21,7 @@ require 'fog/proxmox/attributes'
21
21
  require 'fog/proxmox/helpers/nic_helper'
22
22
  require 'fog/proxmox/helpers/controller_helper'
23
23
  require 'fog/proxmox/helpers/efidisk_helper'
24
+ require 'fog/proxmox/helpers/rng_helper'
24
25
 
25
26
  module Fog
26
27
  module Proxmox
@@ -41,6 +42,7 @@ module Fog
41
42
  attribute :onboot
42
43
  attribute :boot
43
44
  attribute :agent
45
+ attribute :hotplug
44
46
  attribute :scsihw
45
47
  attribute :sockets
46
48
  attribute :memory
@@ -80,6 +82,7 @@ module Fog
80
82
  attribute :searchdomain
81
83
  attribute :nameserver
82
84
  attribute :efidisk
85
+ attribute :rng
83
86
 
84
87
  def initialize(new_attributes = {})
85
88
  prepare_service_value(new_attributes)
@@ -88,6 +91,7 @@ module Fog
88
91
  initialize_interfaces(new_attributes)
89
92
  initialize_disks(new_attributes)
90
93
  initialize_efidisk(new_attributes)
94
+ initialize_rng(new_attributes)
91
95
  super(new_attributes)
92
96
  end
93
97
 
@@ -103,9 +107,10 @@ module Fog
103
107
  end
104
108
 
105
109
  def flatten
106
- flat_hash = attributes.reject { |attribute| %i[node_id type vmid disks interfaces].include? attribute }
110
+ flat_hash = attributes.reject { |attribute| %i[node_id type vmid disks interfaces rng].include? attribute }
107
111
  flat_hash.merge(interfaces_flatten)
108
112
  flat_hash.merge(disks_flatten)
113
+ flat_hash.merge!(rng.flatten) unless rng.nil?
109
114
  flat_hash
110
115
  end
111
116
 
@@ -166,6 +171,14 @@ module Fog
166
171
  efidisk_hash = Fog::Proxmox::EfidiskHelper.extract(efidisk)
167
172
  attributes[:efidisk] = Fog::Proxmox::Compute::Efidisk.new(efidisk_hash)
168
173
  end
174
+
175
+ def initialize_rng(new_attributes)
176
+ rng = new_attributes[:rng0] || new_attributes['rng0']
177
+ return if rng.nil?
178
+
179
+ rng_hash = Fog::Proxmox::RngHelper.extract(rng)
180
+ attributes[:rng] = Fog::Proxmox::Compute::Rng.new(rng_hash)
181
+ end
169
182
  end
170
183
  end
171
184
  end
@@ -37,6 +37,7 @@ module Fog
37
37
  model :disk
38
38
  collection :disks
39
39
  model :efidisk
40
+ model :rng
40
41
  model :server_config
41
42
  model :volume
42
43
  collection :volumes
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Fog
19
+ module Proxmox
20
+ # Convert guest agent settings to and from Proxmox configuration strings.
21
+ module AgentHelper
22
+ DEFAULTS = {
23
+ 'enabled' => '0',
24
+ 'freeze-fs-on-backup' => '1',
25
+ 'fstrim_cloned_disks' => '0',
26
+ 'type' => 'virtio'
27
+ }.freeze
28
+
29
+ def self.extract(agent)
30
+ settings = DEFAULTS.dup
31
+ agent.to_s.split(',').each do |option|
32
+ key, value = option.split('=', 2)
33
+ if value.nil?
34
+ settings['enabled'] = key
35
+ else
36
+ settings[key] = value
37
+ end
38
+ end
39
+
40
+ settings['freeze-fs-on-backup'] = settings.fetch('freeze-fs', settings['freeze-fs-on-backup'])
41
+ settings
42
+ end
43
+
44
+ def self.flatten(settings)
45
+ "enabled=#{settings['enabled']},freeze-fs-on-backup=#{settings['freeze-fs-on-backup']}," \
46
+ "fstrim_cloned_disks=#{settings['fstrim_cloned_disks']},type=#{settings['type']}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module Fog
19
+ module Proxmox
20
+ # Convert hotplug settings to and from Proxmox configuration strings.
21
+ module HotplugHelper
22
+ DEVICES = %w[disk network usb cpu memory].freeze
23
+ DEFAULTS = %w[disk network usb].freeze
24
+
25
+ def self.extract(hotplug)
26
+ hotplug = hotplug.to_s
27
+ {
28
+ enabled: hotplug != '0',
29
+ devices: hotplug.empty? || hotplug == '1' ? DEFAULTS.dup : hotplug.split(',')
30
+ }
31
+ end
32
+
33
+ def self.flatten(settings)
34
+ return '0' unless settings[:enabled]
35
+
36
+ devices = DEVICES.select { |device| settings[:devices].include?(device) }
37
+ devices.empty? ? '0' : devices.join(',')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'fog/proxmox/hash'
19
+
20
+ module Fog
21
+ module Proxmox
22
+ # Convert RNG devices to and from Proxmox configuration strings
23
+ module RngHelper
24
+ RNG_SOURCES = %w[/dev/urandom /dev/random /dev/hwrng].freeze
25
+
26
+ def self.flatten(rng)
27
+ return {} if rng.nil?
28
+
29
+ options = rng.select do |key, value|
30
+ %i[max_bytes period source].include?(key) && !value.nil? && value != ''
31
+ end
32
+
33
+ validate(options)
34
+
35
+ { rng0: Fog::Proxmox::Hash.stringify(options) }
36
+ end
37
+
38
+ def self.validate(rng)
39
+ raise ArgumentError, 'RNG source is required and must be /dev/urandom, /dev/random, or /dev/hwrng' unless RNG_SOURCES.include?(rng[:source].to_s)
40
+
41
+ %i[max_bytes period].each do |key|
42
+ next if rng[key].nil? || rng[key] == ''
43
+ next if rng[key].to_s.match?(/\A[0-9]+\z/)
44
+
45
+ raise ArgumentError, "RNG #{key} must be a non-negative integer"
46
+ end
47
+
48
+ true
49
+ end
50
+
51
+ def self.extract(rng)
52
+ options = rng.split(',').each_with_object({}) do |option, values|
53
+ key, value = option.split('=', 2)
54
+ values[key.to_sym] = value&.sub(/\A"(.*)"\z/, '\1')
55
+ end
56
+
57
+ options.slice(:max_bytes, :period, :source)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -22,9 +22,10 @@ module Fog
22
22
  )
23
23
  end
24
24
  Fog::Logger.debug("Upload response: #{response.inspect}")
25
- raise Fog::Errors::Error, "Unexpected upload response: #{response.inspect}" unless response.to_s.start_with?('UPID:')
25
+ task_node = response.to_s[/\AUPID:([^:]+):/, 1]
26
+ raise Fog::Errors::Error, "Unexpected upload response: #{response.inspect}" unless task_node
26
27
 
27
- service.nodes.get(node_id).tasks.wait_for(response)
28
+ service.nodes.get(task_node).tasks.wait_for(response)
28
29
  response
29
30
  end
30
31
  end
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Fog
21
21
  module Proxmox
22
- VERSION = '1.0.0'
22
+ VERSION = '1.1.0'
23
23
  end
24
24
  end
@@ -13,6 +13,7 @@ describe Fog::Proxmox::Compute::Storage do # rubocop:disable RSpec/SpecFilePathF
13
13
  let(:upload_service) { Minitest::Mock.new }
14
14
  let(:storage) { storage_class.new(service: service, node_id: 'pve', storage: 'local') }
15
15
  let(:iso) { Tempfile.new(['cloudinit', '.iso']) }
16
+ let(:upload_upid) { 'UPID:pve:000008D8:00009E0D:5FBE5DEC:imgcopy::root@pam:' }
16
17
 
17
18
  attr_accessor :uploaded_file
18
19
 
@@ -28,11 +29,11 @@ describe Fog::Proxmox::Compute::Storage do # rubocop:disable RSpec/SpecFilePathF
28
29
  iso.close!
29
30
  end
30
31
 
31
- def expect_upload(response, error: nil)
32
+ def expect_upload(response, error: nil, node: 'pve')
32
33
  service.expect(:nil?, false)
33
34
  service.expect(:config, :compute_config)
34
35
  upload_service.expect(:upload_iso, response) do |path, body|
35
- _(path).must_equal(node: 'pve', storage: 'local')
36
+ _(path).must_equal(node: node, storage: 'local')
36
37
  _(body[:filename]).must_equal File.basename(iso.path)
37
38
  self.uploaded_file = body[:file]
38
39
  _(uploaded_file.read).must_equal "cloud-init\x00\xFF".b
@@ -54,27 +55,38 @@ describe Fog::Proxmox::Compute::Storage do # rubocop:disable RSpec/SpecFilePathF
54
55
  upload_service.verify
55
56
  end
56
57
 
57
- def expect_wait(error: nil)
58
+ def expect_wait(error: nil, node: 'pve', expected_upid: upload_upid)
58
59
  tasks = Object.new
59
60
  test = self
60
61
  tasks.define_singleton_method(:wait_for) do |upid|
61
- test.assert_equal 'UPID:upload', upid
62
+ test.assert_equal expected_upid, upid
62
63
  test.assert_predicate test.uploaded_file, :closed?
63
64
  raise error if error
64
65
 
65
66
  true
66
67
  end
67
68
  nodes = Minitest::Mock.new
68
- nodes.expect(:get, Struct.new(:tasks).new(tasks), ['pve'])
69
+ nodes.expect(:get, Struct.new(:tasks).new(tasks), [node])
69
70
  service.expect(:nodes, nodes)
70
71
  nodes
71
72
  end
72
73
 
73
74
  it 'uploads to its node and storage, closes the file, and waits before returning the UPID' do
74
- expect_upload('UPID:upload')
75
+ expect_upload(upload_upid)
75
76
  nodes = expect_wait
76
77
 
77
- _(upload_iso).must_equal 'UPID:upload'
78
+ _(upload_iso).must_equal upload_upid
79
+ service.verify
80
+ nodes.verify
81
+ end
82
+
83
+ it 'polls the node in the returned UPID when it differs from the upload node' do
84
+ storage.node_id = 'test-2'
85
+ upid = 'UPID:test-1:000008D8:00009E0D:5FBE5DEC:upload::root@pam:'
86
+ expect_upload(upid, node: 'test-2')
87
+ nodes = expect_wait(node: 'test-1', expected_upid: upid)
88
+
89
+ _(upload_iso).must_equal upid
78
90
  service.verify
79
91
  nodes.verify
80
92
  end
@@ -88,6 +100,15 @@ describe Fog::Proxmox::Compute::Storage do # rubocop:disable RSpec/SpecFilePathF
88
100
  service.verify
89
101
  end
90
102
 
103
+ it 'rejects a UPID with no task owner without polling tasks' do
104
+ expect_upload('UPID::000008D8:00009E0D:5FBE5DEC:imgcopy::root@pam:')
105
+
106
+ error = _(proc { upload_iso }).must_raise Fog::Errors::Error
107
+ _(error.message).must_match(/Unexpected upload response/)
108
+ _(uploaded_file.closed?).must_equal true
109
+ service.verify
110
+ end
111
+
91
112
  it 'closes the file when the upload request fails' do
92
113
  expect_upload(nil, error: Fog::Errors::Error.new('Upload failed'))
93
114
 
@@ -97,7 +118,7 @@ describe Fog::Proxmox::Compute::Storage do # rubocop:disable RSpec/SpecFilePathF
97
118
  end
98
119
 
99
120
  it 'propagates task failures' do
100
- expect_upload('UPID:upload')
121
+ expect_upload(upload_upid)
101
122
  nodes = expect_wait(error: Fog::Errors::Error.new('Task failed'))
102
123
 
103
124
  error = _(proc { upload_iso }).must_raise Fog::Errors::Error
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'spec_helper'
19
+ require 'fog/proxmox/helpers/agent_helper'
20
+
21
+ describe 'AgentHelper' do
22
+ describe '.extract' do
23
+ [nil, '', '0', 0].each do |agent|
24
+ it "uses disabled defaults for #{agent.inspect}" do
25
+ assert_equal Fog::Proxmox::AgentHelper::DEFAULTS, Fog::Proxmox::AgentHelper.extract(agent)
26
+ end
27
+ end
28
+
29
+ ['1', 1, 'enabled=1'].each do |agent|
30
+ it "reads enabled shorthand #{agent.inspect}" do
31
+ expected = Fog::Proxmox::AgentHelper::DEFAULTS.merge('enabled' => '1')
32
+ assert_equal expected, Fog::Proxmox::AgentHelper.extract(agent)
33
+ end
34
+ end
35
+
36
+ it 'reads explicit options even when disabled' do
37
+ settings = Fog::Proxmox::AgentHelper.extract('enabled=0,freeze-fs-on-backup=0,fstrim_cloned_disks=1,type=isa')
38
+ assert_equal({ 'enabled' => '0', 'freeze-fs-on-backup' => '0', 'fstrim_cloned_disks' => '1', 'type' => 'isa' }, settings)
39
+ end
40
+
41
+ it 'prefers freeze-fs regardless of option order' do
42
+ ['freeze-fs=0,freeze-fs-on-backup=1', 'freeze-fs-on-backup=1,freeze-fs=0'].each do |options|
43
+ assert_equal '0', Fog::Proxmox::AgentHelper.extract("1,#{options}")['freeze-fs-on-backup']
44
+ end
45
+ end
46
+
47
+ it 'splits option values at only the first equals sign' do
48
+ assert_equal 'a=b', Fog::Proxmox::AgentHelper.extract('type=a=b')['type']
49
+ end
50
+
51
+ it 'returns independent settings without changing defaults' do
52
+ Fog::Proxmox::AgentHelper.extract(nil)['enabled'] = '1'
53
+ assert_equal '0', Fog::Proxmox::AgentHelper.extract(nil)['enabled']
54
+ end
55
+ end
56
+
57
+ describe '.flatten' do
58
+ %w[0 1].each do |enabled|
59
+ it "serializes all options with enabled=#{enabled}" do
60
+ settings = { 'enabled' => enabled, 'freeze-fs-on-backup' => '0', 'fstrim_cloned_disks' => '1', 'type' => 'isa' }
61
+ assert_equal "enabled=#{enabled},freeze-fs-on-backup=0,fstrim_cloned_disks=1,type=isa", Fog::Proxmox::AgentHelper.flatten(settings)
62
+ assert_equal settings, Fog::Proxmox::AgentHelper.extract(Fog::Proxmox::AgentHelper.flatten(settings))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'spec_helper'
19
+ require 'fog/proxmox/helpers/hotplug_helper'
20
+
21
+ describe 'HotplugHelper' do
22
+ describe '.extract' do
23
+ [nil, '', '1', 1].each do |hotplug|
24
+ it "uses enabled default devices for #{hotplug.inspect}" do
25
+ assert_equal({ enabled: true, devices: %w[disk network usb] }, Fog::Proxmox::HotplugHelper.extract(hotplug))
26
+ end
27
+ end
28
+
29
+ ['0', 0].each do |hotplug|
30
+ it "disables hotplug for #{hotplug.inspect}" do
31
+ settings = Fog::Proxmox::HotplugHelper.extract(hotplug)
32
+ assert_equal false, settings[:enabled]
33
+ assert_empty Fog::Proxmox::HotplugHelper::DEVICES & settings[:devices]
34
+ end
35
+ end
36
+
37
+ it 'reads an explicit device list' do
38
+ assert_equal({ enabled: true, devices: %w[disk cpu memory] }, Fog::Proxmox::HotplugHelper.extract('disk,cpu,memory'))
39
+ end
40
+
41
+ it 'returns an independent default device list' do
42
+ Fog::Proxmox::HotplugHelper.extract(nil)[:devices].clear
43
+ assert_equal %w[disk network usb], Fog::Proxmox::HotplugHelper.extract(nil)[:devices]
44
+ end
45
+ end
46
+
47
+ describe '.flatten' do
48
+ it 'serializes selected devices in the existing device order' do
49
+ assert_equal 'disk,network,cpu', Fog::Proxmox::HotplugHelper.flatten(enabled: true, devices: %w[cpu network disk])
50
+ end
51
+
52
+ it 'disables hotplug even when devices are selected' do
53
+ assert_equal '0', Fog::Proxmox::HotplugHelper.flatten(enabled: false, devices: %w[disk network])
54
+ end
55
+
56
+ it 'disables hotplug when no supported devices are selected' do
57
+ [[], ['unknown']].each do |devices|
58
+ assert_equal '0', Fog::Proxmox::HotplugHelper.flatten(enabled: true, devices: devices)
59
+ end
60
+ end
61
+
62
+ it 'round trips explicit supported devices' do
63
+ raw = 'disk,cpu,memory'
64
+ assert_equal raw, Fog::Proxmox::HotplugHelper.flatten(Fog::Proxmox::HotplugHelper.extract(raw))
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of Fog::Proxmox.
4
+
5
+ # Fog::Proxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # Fog::Proxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'spec_helper'
19
+ require 'fog/proxmox/helpers/rng_helper'
20
+
21
+ rng_helper_class = Fog::Proxmox::RngHelper
22
+
23
+ describe Fog::Proxmox::RngHelper do
24
+ let(:rng_value) { 'max_bytes=1024,period=2000,source=/dev/urandom' }
25
+ let(:rng_attributes) { { max_bytes: '1024', period: '2000', source: '/dev/urandom' } }
26
+
27
+ it 'extracts all RNG options' do
28
+ assert_equal rng_attributes, rng_helper_class.extract(rng_value)
29
+ end
30
+
31
+ it 'extracts options regardless of their order' do
32
+ assert_equal rng_attributes,
33
+ rng_helper_class.extract('source=/dev/urandom,period=2000,max_bytes=1024')
34
+ end
35
+
36
+ it 'extracts a quoted period without the surrounding quotes' do
37
+ assert_equal rng_attributes,
38
+ rng_helper_class.extract('source=/dev/urandom,max_bytes=1024,period="2000"')
39
+ end
40
+
41
+ it 'extracts quoted values for all RNG options' do
42
+ assert_equal rng_attributes,
43
+ rng_helper_class.extract('max_bytes="1024",period="2000",source="/dev/urandom"')
44
+ end
45
+
46
+ it 'extracts a source without optional rate limits' do
47
+ assert_equal({ source: '/dev/hwrng' },
48
+ rng_helper_class.extract('source=/dev/hwrng'))
49
+ end
50
+
51
+ it 'ignores unsupported options when extracting' do
52
+ assert_equal({ source: '/dev/urandom' },
53
+ rng_helper_class.extract('source=/dev/urandom,unknown=value'))
54
+ end
55
+
56
+ it 'flattens all RNG options' do
57
+ assert_equal({ rng0: rng_value }, rng_helper_class.flatten(rng_attributes))
58
+ end
59
+
60
+ it 'accepts each supported RNG source' do
61
+ %w[/dev/urandom /dev/random /dev/hwrng].each do |source|
62
+ assert_equal({ rng0: "source=#{source}" }, rng_helper_class.flatten(source: source))
63
+ end
64
+ end
65
+
66
+ it 'requires a source when configuring an RNG device' do
67
+ [{}, { max_bytes: 1024 }, { source: nil }, { source: '' }].each do |attributes|
68
+ error = assert_raises(ArgumentError) { rng_helper_class.flatten(attributes) }
69
+ assert_match(/RNG source is required/, error.message)
70
+ end
71
+ end
72
+
73
+ it 'rejects unsupported or partially matching source paths' do
74
+ ['/dev/null', '/dev/random/extra', 'prefix/dev/urandom', "/dev/hwrng\n", ' '].each do |source|
75
+ assert_raises(ArgumentError) { rng_helper_class.flatten(source: source) }
76
+ end
77
+ end
78
+
79
+ it 'accepts integer rate limits' do
80
+ assert_equal({ rng0: rng_value },
81
+ rng_helper_class.flatten(max_bytes: 1024, period: 2000, source: '/dev/urandom'))
82
+ end
83
+
84
+ %i[max_bytes period].each do |key|
85
+ it "rejects invalid #{key} values" do
86
+ ['abc', '1024bytes', '1.5', 1.5, -1, '-1', true, ' ', "1024\n"].each do |value|
87
+ error = assert_raises(ArgumentError) do
88
+ rng_helper_class.flatten(source: '/dev/urandom', key => value)
89
+ end
90
+ assert_equal "RNG #{key} must be a non-negative integer", error.message
91
+ end
92
+ end
93
+ end
94
+
95
+ it 'omits unset options and retains zero values' do
96
+ assert_equal({ rng0: 'max_bytes=0,source=/dev/random' },
97
+ rng_helper_class.flatten(max_bytes: 0, period: nil, source: '/dev/random'))
98
+ end
99
+
100
+ it 'omits blank limits and unsupported options' do
101
+ assert_equal({ rng0: 'source=/dev/urandom' },
102
+ rng_helper_class.flatten(max_bytes: '', period: '', source: '/dev/urandom', unknown: 'value'))
103
+ end
104
+
105
+ it 'returns an empty hash for a missing device' do
106
+ assert_empty rng_helper_class.flatten(nil)
107
+ end
108
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fog-proxmox
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tristan Robert
@@ -9,20 +9,6 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: bundler
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '2.1'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '2.1'
26
12
  - !ruby/object:Gem::Dependency
27
13
  name: bundler-audit
28
14
  requirement: !ruby/object:Gem::Requirement
@@ -264,6 +250,7 @@ files:
264
250
  - lib/fog/proxmox/compute/models/multipart_iso_upload.rb
265
251
  - lib/fog/proxmox/compute/models/node.rb
266
252
  - lib/fog/proxmox/compute/models/nodes.rb
253
+ - lib/fog/proxmox/compute/models/rng.rb
267
254
  - lib/fog/proxmox/compute/models/server.rb
268
255
  - lib/fog/proxmox/compute/models/server_config.rb
269
256
  - lib/fog/proxmox/compute/models/servers.rb
@@ -315,12 +302,15 @@ files:
315
302
  - lib/fog/proxmox/core.rb
316
303
  - lib/fog/proxmox/errors.rb
317
304
  - lib/fog/proxmox/hash.rb
305
+ - lib/fog/proxmox/helpers/agent_helper.rb
318
306
  - lib/fog/proxmox/helpers/controller_helper.rb
319
307
  - lib/fog/proxmox/helpers/cpu_helper.rb
320
308
  - lib/fog/proxmox/helpers/disk_helper.rb
321
309
  - lib/fog/proxmox/helpers/efidisk_helper.rb
310
+ - lib/fog/proxmox/helpers/hotplug_helper.rb
322
311
  - lib/fog/proxmox/helpers/ip_helper.rb
323
312
  - lib/fog/proxmox/helpers/nic_helper.rb
313
+ - lib/fog/proxmox/helpers/rng_helper.rb
324
314
  - lib/fog/proxmox/identity.rb
325
315
  - lib/fog/proxmox/identity/models/domain.rb
326
316
  - lib/fog/proxmox/identity/models/domain_type.rb
@@ -422,12 +412,15 @@ files:
422
412
  - spec/fog/proxmox/storage/real_upload_iso_spec.rb
423
413
  - spec/fog/proxmox/storage/storage_upload_iso_spec.rb
424
414
  - spec/hash_spec.rb
415
+ - spec/helpers/agent_helper_spec.rb
425
416
  - spec/helpers/controller_helper_spec.rb
426
417
  - spec/helpers/cpu_helper_spec.rb
427
418
  - spec/helpers/disk_helper_spec.rb
428
419
  - spec/helpers/efidisk_helper_spec.rb
420
+ - spec/helpers/hotplug_helper_spec.rb
429
421
  - spec/helpers/ip_helper_spec.rb
430
422
  - spec/helpers/nic_helper_spec.rb
423
+ - spec/helpers/rng_helper_spec.rb
431
424
  - spec/identity_spec.rb
432
425
  - spec/network_spec.rb
433
426
  - spec/proxmox_vcr.rb