redfish_tools 0.1.2 → 0.2.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 +4 -4
- data/.travis.yml +11 -2
- data/README.md +37 -10
- data/lib/redfish_tools/cli/serve.rb +5 -1
- data/lib/redfish_tools/cli.rb +19 -9
- data/lib/redfish_tools/datastore.rb +10 -11
- data/lib/redfish_tools/server.rb +16 -4
- data/lib/redfish_tools/servlet.rb +84 -30
- data/lib/redfish_tools/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62d5e62a684fe41e5f66474396da984161869dc0
|
4
|
+
data.tar.gz: 401e723d7f305dd2576e45352e19fe9b5da901e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f1eb827d58a02555259bb9b618ac0444c5483599ffd5b2477e57fe15f4e10559431b3d041a21b2f1684370df60daf0fd73e50135f5f8136528165079fa9663
|
7
|
+
data.tar.gz: 7ec1d90be137f6006b9cf18dd75e622e03d5efccda563ef682200d3954a5a60f43acb8a1ef70f5b65e508e2cd6015d9079d1e18a1aedd988d91e9dc1ee603994
|
data/.travis.yml
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
|
+
cache: bundler
|
3
4
|
rvm:
|
4
|
-
|
5
|
-
|
5
|
+
- 2.4.3
|
6
|
+
script: true
|
7
|
+
deploy:
|
8
|
+
provider: rubygems
|
9
|
+
api_key:
|
10
|
+
secure: r3WiTmAZTHbqi30saYw0Iv2YEUZNAv4dp2RPzDoc7K9l1yV+Pt+fol/NCOg8gVBXPn5WHHrXUpw4jz158CneOPlmp7opdzW8SZymUlphXBl7kcI9qEsDJnqZOAJkj1YVwMvjMwRqE1UjWs43Ok93wVQlt2M9FIztLBXHhKtm+M0T6GpeFisAntgeIYiRl0/XTYr36SG23dTcWogkKEHahX24/W1ap5Z2O+p0wZNyRbhFXMMkvMVqwS6EFn9CR1GuJ2f93WYzsIZWm4LhjDZisAxh4oHtcGfXyOSHTU4d4fOnoBd0H7Ejyf/4mr407gnfSqpuXJUIiTsNPDrlMTGT0l/sSJYPWY6P1nygOTlCd7oLG+E3nOUKD5L0KPdzLkAxqylrNbCFx9dPMfyuT6wUin4XW/NulwCAy7qLrMFn0MuugxZINMJE09dWCMd5B+guMPtmpj7eVerrDi26H7hEO7Mdvg1rmhEfUVLFbiiAOzi3SvHhFYZC7Rr1WFt+8zUxgAUxwuAfAyJX4Zolkj1xcQF1Qssj4g8wAtaQ1ZWhdWWoJjEziJcqZPOIckeB+uuvauXVSd1a8ASsHLBCrnZUYKp6ZOn7a3t5zaoUjUTl4BuPZFfZ0gAqP3YMEmGboeDPFRM4uNk7l5eA5qc0s0HT2wgjYXeL+M2ZmHk5ACInWAw=
|
11
|
+
gem: redfish_tools
|
12
|
+
on:
|
13
|
+
tags: true
|
14
|
+
repo: xlab-si/redfish_tools
|
data/README.md
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
-
#
|
1
|
+
# Redfish tools
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.com/xlab-si/redfish_tools)
|
4
|
+
|
5
|
+
|
6
|
+
This repository contains source code for redfish_tools gem that contains tools
|
7
|
+
for testing application that know how to work with Redfish API.
|
8
|
+
|
9
|
+
The only tool that is currently available is mock server, but in the near
|
10
|
+
future, we will also add a mock creator tool and interactive inspector for
|
11
|
+
Redfish services.
|
4
12
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
13
|
|
7
14
|
## Installation
|
8
15
|
|
9
16
|
Add this line to your application's Gemfile:
|
10
17
|
|
11
|
-
|
12
|
-
gem 'redfish_tools'
|
13
|
-
```
|
18
|
+
gem "redfish_tools"
|
14
19
|
|
15
20
|
And then execute:
|
16
21
|
|
@@ -20,16 +25,38 @@ Or install it yourself as:
|
|
20
25
|
|
21
26
|
$ gem install redfish_tools
|
22
27
|
|
28
|
+
|
23
29
|
## Usage
|
24
30
|
|
25
|
-
|
31
|
+
The simplest way to start using Redfish tools is to simply run the `redfish`
|
32
|
+
command and read the provided help. At the moment, the output should look like
|
33
|
+
this:
|
34
|
+
|
35
|
+
$ redfish
|
36
|
+
Commands:
|
37
|
+
redfish help [COMMAND] # Describe available commands
|
38
|
+
redfish serve [OPTIONS] PATH # serve mock from PATH
|
39
|
+
|
40
|
+
To start serving existing Redfish recording, we run the `serve` command:
|
41
|
+
|
42
|
+
$ redfish serve --ssl --user test --pass demo path/to/recording
|
43
|
+
|
44
|
+
To get the description of all available options, use `help` command or add
|
45
|
+
`-h` flag anywhere in the command.
|
46
|
+
|
26
47
|
|
27
48
|
## Development
|
28
49
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
50
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
51
|
+
Unfortunately, this gem contains no tests at the moment, so if you feel like
|
52
|
+
contributing, this would be a great place to start.
|
53
|
+
|
54
|
+
To create new release, increment the version number, commit the change, tag
|
55
|
+
the commit and push tag to the GitHub. Travis CI will pick from there on and
|
56
|
+
create new release, publishing it on https://rubygems.org.
|
30
57
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
58
|
|
33
59
|
## Contributing
|
34
60
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
61
|
+
Bug reports and pull requests are welcome on GitHub at
|
62
|
+
https://github.com/xlab-si/redfish_tools.
|
@@ -14,8 +14,12 @@ module RedfishTools
|
|
14
14
|
def run
|
15
15
|
datastore = RedfishTools::DataStore.new(@path)
|
16
16
|
server = RedfishTools::Server.new(datastore,
|
17
|
+
@options[:user],
|
18
|
+
@options[:pass],
|
17
19
|
Port: @options[:port],
|
18
|
-
BindAddress: @options[:bind]
|
20
|
+
BindAddress: @options[:bind],
|
21
|
+
SSLEnable: @options[:ssl],
|
22
|
+
SSLCertName: [%w[CN localhost]])
|
19
23
|
trap("INT") { server.shutdown }
|
20
24
|
server.start
|
21
25
|
end
|
data/lib/redfish_tools/cli.rb
CHANGED
@@ -11,6 +11,10 @@ module RedfishTools
|
|
11
11
|
super(args)
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.exit_on_failure?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
14
18
|
desc "serve [OPTIONS] PATH", "serve mock from PATH"
|
15
19
|
option :port,
|
16
20
|
desc: "port that should be used to serve the mock",
|
@@ -19,18 +23,24 @@ module RedfishTools
|
|
19
23
|
option :bind,
|
20
24
|
desc: "address that server should bind to",
|
21
25
|
default: "127.0.0.1"
|
26
|
+
option :ssl,
|
27
|
+
desc: "use SSL",
|
28
|
+
default: false,
|
29
|
+
type: :boolean
|
30
|
+
option :user,
|
31
|
+
desc: "username to use"
|
32
|
+
option :pass,
|
33
|
+
desc: "password to use"
|
22
34
|
def serve(path)
|
35
|
+
user = options[:user]
|
36
|
+
pass = options[:pass]
|
37
|
+
raise Thor::Error, "Missing password" if user && pass.nil?
|
38
|
+
raise Thor::Error, "Missing username" if user.nil? && pass
|
39
|
+
|
23
40
|
require "redfish_tools/cli/serve"
|
24
41
|
Serve.new(path, options).run
|
25
|
-
|
26
|
-
|
27
|
-
desc "query [OPTIONS] URL QUERY", "query service at URL using QUERY"
|
28
|
-
def query(url, *query
|
29
|
-
|
30
|
-
desc "record [OPTIONS] URL PATH", "record service at URL"
|
31
|
-
def record(url, path)
|
32
|
-
require "redfish_tools/cli/record"
|
33
|
-
Record.new(url, path, options).run
|
42
|
+
rescue StandardError => e
|
43
|
+
raise Thor::Error, e.to_s
|
34
44
|
end
|
35
45
|
end
|
36
46
|
end
|
@@ -7,14 +7,16 @@ module RedfishTools
|
|
7
7
|
Resource = Struct.new(:id, :body, :headers, :time)
|
8
8
|
|
9
9
|
def initialize(base_path)
|
10
|
-
# TODO(tadeboro): check for folder and determine mode of operation
|
11
10
|
@base_path = File.expand_path(base_path)
|
12
11
|
@overlay = {}
|
12
|
+
|
13
|
+
root_file = File.join(@base_path, "redfish", "v1", "index.json")
|
14
|
+
raise "Invalid recording folder" unless File.file?(root_file)
|
13
15
|
end
|
14
16
|
|
15
17
|
def get(id)
|
16
18
|
id = id.chomp("/")
|
17
|
-
@overlay[id]
|
19
|
+
@overlay[id] ||= load_resource(id)
|
18
20
|
end
|
19
21
|
|
20
22
|
def set(id, body, headers: nil, time: nil)
|
@@ -28,24 +30,21 @@ module RedfishTools
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def load_body(id)
|
31
|
-
|
33
|
+
load_json(File.join(@base_path, id, "index.json"))
|
32
34
|
end
|
33
35
|
|
34
36
|
def load_headers(id)
|
35
|
-
load_json(File.join(@base_path, id, "headers.json"))
|
37
|
+
headers = load_json(File.join(@base_path, id, "headers.json"))
|
38
|
+
headers && headers["GET"]
|
36
39
|
end
|
37
40
|
|
38
41
|
def load_time(id)
|
39
|
-
load_json(File.join(@base_path, id, "time.json"))
|
40
|
-
|
41
|
-
|
42
|
-
def load(path)
|
43
|
-
File.readable?(path) ? File.read(path) : nil
|
42
|
+
times = load_json(File.join(@base_path, id, "time.json"))
|
43
|
+
times && times["GET_Time"]&.to_f
|
44
44
|
end
|
45
45
|
|
46
46
|
def load_json(path)
|
47
|
-
|
48
|
-
content ? JSON.parse(content) : {}
|
47
|
+
File.readable?(path) ? JSON.parse(File.read(path)) : nil
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|
data/lib/redfish_tools/server.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "base64"
|
3
4
|
require "openssl"
|
4
5
|
require "webrick"
|
5
6
|
require "webrick/https"
|
@@ -8,12 +9,23 @@ require "redfish_tools/servlet"
|
|
8
9
|
|
9
10
|
module RedfishTools
|
10
11
|
class Server < WEBrick::HTTPServer
|
11
|
-
def initialize(datastore, config = {})
|
12
|
+
def initialize(datastore, username, password, config = {})
|
12
13
|
super(config)
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
@datastore = datastore
|
16
|
+
root = datastore.get("/redfish/v1").body
|
17
|
+
@login_path = root.dig("Links", "Sessions", "@odata.id")&.chomp("/")
|
18
|
+
@username = username
|
19
|
+
@password = password
|
20
|
+
|
21
|
+
mount("/", Servlet)
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :datastore, :login_path, :username, :password
|
25
|
+
|
26
|
+
def basic_auth_header
|
27
|
+
@basic_auth_header ||= "Basic " +
|
28
|
+
Base64.strict_encode64("#{username}:#{password}")
|
17
29
|
end
|
18
30
|
end
|
19
31
|
end
|
@@ -1,38 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
require "json"
|
5
|
+
require "securerandom"
|
3
6
|
require "set"
|
4
7
|
require "webrick"
|
5
8
|
|
6
9
|
module RedfishTools
|
7
10
|
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators :@server,
|
14
|
+
:datastore, :login_path, :username, :password,
|
15
|
+
:basic_auth_header
|
16
|
+
|
8
17
|
BAD_HEADERS = Set.new(["connection", "content-length", "keep-alive"])
|
9
18
|
DEFAULT_HEADERS = {
|
10
19
|
"content-type" => "application/json"
|
11
20
|
}.freeze
|
12
21
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
def service(request, response)
|
23
|
+
return response.status = 401 unless authorized?(request)
|
24
|
+
return response.status = 404 unless datastore.get(request.path).body
|
25
|
+
|
26
|
+
super
|
17
27
|
end
|
18
28
|
|
19
29
|
def do_GET(request, response)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
response.body =
|
24
|
-
set_headers(response, resource.headers)
|
25
|
-
response.status = response.body ? 200 : 404
|
30
|
+
item = datastore.get(request.path)
|
31
|
+
response.status = 200
|
32
|
+
set_headers(response, item.headers)
|
33
|
+
response.body = item.body.to_json
|
26
34
|
end
|
27
35
|
|
28
36
|
def do_POST(request, response)
|
29
|
-
|
37
|
+
item = datastore.get(request.path)
|
38
|
+
return response.status = 405 unless item.body["Members"]
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
data = JSON.parse(request.body)
|
41
|
+
item_n = login_path?(request) ? login(item, data) : new_item(item, data)
|
42
|
+
return response.status = 400 unless item_n.body
|
43
|
+
|
44
|
+
response.status = 201
|
45
|
+
set_headers(response, item_n.headers)
|
46
|
+
response.body = item_n.body.to_json
|
47
|
+
rescue JSON::ParserError
|
48
|
+
response.status = 400
|
36
49
|
end
|
37
50
|
|
38
51
|
def do_PUT(_request, response)
|
@@ -49,24 +62,65 @@ module RedfishTools
|
|
49
62
|
|
50
63
|
private
|
51
64
|
|
52
|
-
def login_path?(
|
53
|
-
|
65
|
+
def login_path?(request)
|
66
|
+
login_path == request.path.chomp("/")
|
54
67
|
end
|
55
68
|
|
56
|
-
def login(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
69
|
+
def login(item, data)
|
70
|
+
user = data["UserName"]
|
71
|
+
pass = data["Password"]
|
72
|
+
return nil unless username == user && password == pass
|
73
|
+
|
74
|
+
res = new_item(item,
|
75
|
+
"@odata.type" => "#Session.v1_1_0.Session",
|
76
|
+
"UserName" => user,
|
77
|
+
"Password" => nil)
|
78
|
+
res.headers = DEFAULT_HEADERS.merge("X-Auth-Token" => res.body["Id"])
|
79
|
+
res
|
80
|
+
end
|
81
|
+
|
82
|
+
def new_item(item, data)
|
83
|
+
id = SecureRandom.uuid
|
84
|
+
oid = item.id.chomp("/") + "/" + id
|
85
|
+
item.body["Members@odata.count"] += 1
|
86
|
+
item.body["Members"].push("@odata.id" => oid)
|
87
|
+
|
88
|
+
base = { "@odata.id" => oid, "Id" => id, "Name" => id }
|
89
|
+
datastore.set(id, base.merge(data))
|
90
|
+
end
|
91
|
+
|
92
|
+
def authorized?(request)
|
93
|
+
username.nil? || # Server has no credentials set
|
94
|
+
always_allow?(request.path) || # Non-protected endpoints
|
95
|
+
authorized_basic?(request) ||
|
96
|
+
authorized_session?(request) ||
|
97
|
+
(request.request_method == "POST" && login_path?(request))
|
98
|
+
end
|
99
|
+
|
100
|
+
def always_allow?(path)
|
101
|
+
[
|
102
|
+
"/redfish",
|
103
|
+
"/redfish/v1",
|
104
|
+
"/redfish/v1/$metadata",
|
105
|
+
"/redfish/v1/odata"
|
106
|
+
].include?(path.chomp("/"))
|
107
|
+
end
|
108
|
+
|
109
|
+
def authorized_basic?(request)
|
110
|
+
request["authorization"] == basic_auth_header
|
111
|
+
end
|
112
|
+
|
113
|
+
def authorized_session?(request)
|
114
|
+
return false if login_path.nil? || request["X-Auth-Token"].nil?
|
115
|
+
remove_stale_sessions
|
116
|
+
datastore.get(login_path).body["Members"].any? do |session|
|
117
|
+
session["@odata.id"].index(request["X-Auth-Token"])
|
118
|
+
end
|
65
119
|
end
|
66
120
|
|
67
|
-
def
|
68
|
-
# TODO(tadeboro):
|
69
|
-
|
121
|
+
def remove_stale_sessions
|
122
|
+
# TODO(tadeboro): implement session expiration
|
123
|
+
nil
|
70
124
|
end
|
71
125
|
|
72
126
|
def set_headers(response, headers)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redfish_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tadej Borovšak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redfish_client
|