proxmox-api 0.1.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/proxmox_api.rb +54 -6
- data/spec/api_path_spec.rb +42 -0
- data/spec/spec_helper.rb +50 -0
- metadata +51 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfffdfb8c0570120b8dd060da7d5d7fa379c00989d83523a4297264616f2d8f3
|
4
|
+
data.tar.gz: 0a48cc99643a7a2aa894f3f6b5f5ad354b83f428f89b891296701b5bd386aa6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7087e2ceba545878007e3d00a006ab22cc50068058d95acf2d8afb95482d94a68a4152394f1a82a710e612fad085d466413d2fc00b27fabba8e2ac41915f7e98
|
7
|
+
data.tar.gz: a580714df5b78651b3d82583a425f8474825f9c21e4ceee75805802730b9089a7f43ed55acd98ba17596913fd7670eaa5d1a8c2d6b8bde283208ac5d4166e675
|
data/lib/proxmox_api.rb
CHANGED
@@ -3,13 +3,20 @@
|
|
3
3
|
require 'rest_client'
|
4
4
|
require 'json'
|
5
5
|
|
6
|
+
# This class is wrapper for Proxmox PVE APIv2.
|
7
|
+
# See README for usage examples.
|
8
|
+
#
|
9
|
+
# @author Eugene Lapeko
|
6
10
|
class ProxmoxAPI
|
7
|
-
AUTH_PARAMS = %i[username realm password otp
|
11
|
+
AUTH_PARAMS = %i[username realm password otp].freeze
|
8
12
|
REST_METHODS = %i[get post put delete].freeze
|
9
|
-
SSL_OPTIONS = %i[ssl_client_cert ssl_client_key ssl_ca_file verify_ssl].freeze
|
10
13
|
|
14
|
+
# This class is used to collect api path before request
|
11
15
|
class ApiPath
|
16
|
+
# @param [ProxmoxAPI] api ProxmoxAPI object to call when request is executed
|
12
17
|
def initialize(api)
|
18
|
+
raise ArgumentError, 'Not an instance of ProxmoxAPI' unless api.is_a? ProxmoxAPI
|
19
|
+
|
13
20
|
@api = api
|
14
21
|
@path = []
|
15
22
|
end
|
@@ -24,7 +31,7 @@ class ProxmoxAPI
|
|
24
31
|
end
|
25
32
|
|
26
33
|
def method_missing(method, *args)
|
27
|
-
return @api.__send__(:submit, method, to_s, *args) if REST_METHODS.
|
34
|
+
return @api.__send__(:submit, method, to_s, *args) if REST_METHODS.any? { |rm| /^#{rm}!?$/.match? method }
|
28
35
|
|
29
36
|
@path << method.to_s
|
30
37
|
self
|
@@ -35,10 +42,38 @@ class ProxmoxAPI
|
|
35
42
|
end
|
36
43
|
end
|
37
44
|
|
45
|
+
# This exception is raised when Proxmox API returns error code
|
46
|
+
#
|
47
|
+
# @!attribute [r] response
|
48
|
+
# @return [RestClient::Response] answer from Proxmox server
|
49
|
+
class ApiException < RuntimeError
|
50
|
+
attr_reader :response
|
51
|
+
|
52
|
+
def initialize(response, description)
|
53
|
+
@response = response
|
54
|
+
|
55
|
+
super description
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Constructor method for ProxmoxAPI
|
60
|
+
#
|
61
|
+
# @param [String] cluster hostname/ip of cluster to control
|
62
|
+
# @param [Hash] options cluster connection parameters
|
63
|
+
#
|
64
|
+
# @option options [String] :username - username to be used for connection
|
65
|
+
# @option options [String] :password - password to be used for connection
|
66
|
+
# @option options [String] :realm - auth realm, can be given in :username ('user@realm')
|
67
|
+
# @option options [String] :otp - one-time password for two-factor auth
|
68
|
+
#
|
69
|
+
# @option options [Boolean] :verify_ssl - verify server certificate
|
70
|
+
#
|
71
|
+
# You can also pass here all ssl options supported by rest-client gem
|
72
|
+
# @see https://github.com/rest-client/rest-client
|
38
73
|
def initialize(cluster, options)
|
39
74
|
@connection = RestClient::Resource.new(
|
40
75
|
"https://#{cluster}:#{options[:port] || 8006}/api2/json/",
|
41
|
-
options.select { |k, _v|
|
76
|
+
options.select { |k, _v| RestClient::Request::SSLOptionList.unshift('verify_ssl').include? k.to_s }
|
42
77
|
)
|
43
78
|
@auth_ticket = create_auth_ticket(options.select { |k, _v| AUTH_PARAMS.include? k })
|
44
79
|
end
|
@@ -57,9 +92,16 @@ class ProxmoxAPI
|
|
57
92
|
|
58
93
|
private
|
59
94
|
|
95
|
+
def raise_on_failure(response, message = 'Proxmox API request failed')
|
96
|
+
return unless response.code.to_i >= 400
|
97
|
+
|
98
|
+
raise ApiException.new(response, message)
|
99
|
+
end
|
100
|
+
|
60
101
|
def create_auth_ticket(options)
|
61
102
|
@connection['access/ticket'].post options do |response, _request, _result, &_block|
|
62
|
-
|
103
|
+
raise_on_failure(response, 'Proxmox authentication failure')
|
104
|
+
|
63
105
|
data = JSON.parse(response.body, symbolize_names: true)[:data]
|
64
106
|
{
|
65
107
|
cookies: { PVEAuthCookie: data[:ticket] },
|
@@ -80,8 +122,14 @@ class ProxmoxAPI
|
|
80
122
|
end
|
81
123
|
|
82
124
|
def submit(method, url, data = {})
|
125
|
+
if /!$/.match? method
|
126
|
+
method = method.to_s.tr('!', '').to_sym
|
127
|
+
skip_raise = true
|
128
|
+
end
|
129
|
+
|
83
130
|
@connection[url].__send__(method, *prepare_options(method, data)) do |response|
|
84
|
-
|
131
|
+
raise_on_failure(response) unless skip_raise
|
132
|
+
|
85
133
|
JSON.parse(response.body, symbolize_names: true)[:data]
|
86
134
|
end
|
87
135
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
describe 'ApiPath' do
|
6
|
+
before(:each) do
|
7
|
+
@api_object = double('ProxmoxAPI')
|
8
|
+
allow(@api_object).to receive(:is_a?).with(ProxmoxAPI) { true }
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'raises exception when object is not ProxmoxAPI' do
|
12
|
+
[1, 'String', Class].each do |item|
|
13
|
+
expect { ProxmoxAPI::ApiPath.new(item) }.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'collects api path using methods' do
|
18
|
+
expect(ProxmoxAPI::ApiPath.new(@api_object).ticket.to_s).to eq 'ticket'
|
19
|
+
expect(ProxmoxAPI::ApiPath.new(@api_object).nodes.pve1.to_s).to eq 'nodes/pve1'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'collects api path using []' do
|
23
|
+
expect(ProxmoxAPI::ApiPath.new(@api_object)[:ticket].to_s).to eq 'ticket'
|
24
|
+
expect(ProxmoxAPI::ApiPath.new(@api_object)[:nodes]['pve1'][15].to_s).to eq 'nodes/pve1/15'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'collects api path combining [] and methods' do
|
28
|
+
expect(ProxmoxAPI::ApiPath.new(@api_object).nodes['pve1'].lxc.to_s).to eq 'nodes/pve1/lxc'
|
29
|
+
end
|
30
|
+
|
31
|
+
%i[get post delete put].each do |method|
|
32
|
+
it "should send #{method} to ProxmoxAPI" do
|
33
|
+
expect(@api_object).to receive(:submit).with(method, 'nodes/pve')
|
34
|
+
ProxmoxAPI::ApiPath.new(@api_object).nodes.pve.__send__(method)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should send #{method}! to ProxmoxAPI" do
|
38
|
+
expect(@api_object).to receive(:submit).with("#{method}!".to_sym, 'nodes/pve')
|
39
|
+
ProxmoxAPI::ApiPath.new(@api_object).nodes.pve.__send__("#{method}!")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'proxmox_api'
|
4
|
+
|
5
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
8
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
9
|
+
# files.
|
10
|
+
#
|
11
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
15
|
+
# a separate helper file that requires the additional dependencies and performs
|
16
|
+
# the additional setup, and require it from the spec files that actually need
|
17
|
+
# it.
|
18
|
+
#
|
19
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
20
|
+
RSpec.configure do |config|
|
21
|
+
# rspec-expectations config goes here. You can use an alternate
|
22
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
23
|
+
# assertions if you prefer.
|
24
|
+
config.expect_with :rspec do |expectations|
|
25
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
26
|
+
# and `failure_message` of custom matchers include text for helper methods
|
27
|
+
# defined using `chain`, e.g.:
|
28
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
29
|
+
# # => "be bigger than 2 and smaller than 4"
|
30
|
+
# ...rather than:
|
31
|
+
# # => "be bigger than 2"
|
32
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
33
|
+
end
|
34
|
+
|
35
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
36
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
37
|
+
config.mock_with :rspec do |mocks|
|
38
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
39
|
+
# a real object. This is generally recommended, and will default to
|
40
|
+
# `true` in RSpec 4.
|
41
|
+
mocks.verify_partial_doubles = true
|
42
|
+
end
|
43
|
+
|
44
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
45
|
+
# have no way to turn it off -- the option exists only for backwards
|
46
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
47
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
48
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
49
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
50
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: proxmox-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugene Lapeko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -38,7 +38,49 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.1'
|
41
|
-
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Proxmox VE REST API wrapper
|
42
84
|
email: eugene@lapeko.info
|
43
85
|
executables: []
|
44
86
|
extensions: []
|
@@ -47,6 +89,8 @@ files:
|
|
47
89
|
- Gemfile
|
48
90
|
- LICENSE
|
49
91
|
- lib/proxmox_api.rb
|
92
|
+
- spec/api_path_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
50
94
|
homepage: https://github.com/L-Eugene/proxmox-api
|
51
95
|
licenses:
|
52
96
|
- MIT
|
@@ -69,5 +113,7 @@ requirements: []
|
|
69
113
|
rubygems_version: 3.0.8
|
70
114
|
signing_key:
|
71
115
|
specification_version: 4
|
72
|
-
summary:
|
73
|
-
test_files:
|
116
|
+
summary: Proxmox VE REST API wrapper
|
117
|
+
test_files:
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
- spec/api_path_spec.rb
|