wdmc 0.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 +7 -0
- data/bin/wdmc +5 -0
- data/lib/wdmc.rb +13 -0
- data/lib/wdmc/acl.rb +101 -0
- data/lib/wdmc/cli.rb +33 -0
- data/lib/wdmc/client.rb +246 -0
- data/lib/wdmc/config.rb +17 -0
- data/lib/wdmc/device.rb +99 -0
- data/lib/wdmc/shares.rb +111 -0
- data/lib/wdmc/storage.rb +22 -0
- data/lib/wdmc/timemachine.rb +38 -0
- data/lib/wdmc/user.rb +106 -0
- data/lib/wdmc/version.rb +3 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bc5201ee0a99c0adfe5fa1776531ebf9ca6d3d8a3aa1566bd9012cb4f32866f5
|
4
|
+
data.tar.gz: 5d0825e62405fad42e724d549bad456c570cc9d79c4ce6dccbbb3fc4fbca39e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 48cc825bdee958293a354d084ffa448d8878c0deb7341b208a9e8a2e9348fae534ec707c6efa2a252d622b95cc9a4629498d8a26bbe6e4690a634b2bc7e6cfde
|
7
|
+
data.tar.gz: febb689d76bf5c03da5789d2bc3aa0f394f377a3283a5a8909a28d6508d9fa0ff61dac2ea4763aa5d9c56caa5de684232cfc277392cfe4d789fc3c5c12c250e2
|
data/bin/wdmc
ADDED
data/lib/wdmc.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "filesize"
|
3
|
+
require "rainbow/ext/string"
|
4
|
+
require "json"
|
5
|
+
require "wdmc/version"
|
6
|
+
require "wdmc/config"
|
7
|
+
require "wdmc/client"
|
8
|
+
require "wdmc/shares"
|
9
|
+
require "wdmc/acl"
|
10
|
+
require "wdmc/version"
|
11
|
+
require "wdmc/device"
|
12
|
+
require "wdmc/timemachine"
|
13
|
+
require "wdmc/user"
|
data/lib/wdmc/acl.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'wdmc/shares'
|
2
|
+
|
3
|
+
module Wdmc
|
4
|
+
class Acl < Thor
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@wdmc = Wdmc::Client.new
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'get [SHARE NAME]', 'Get shares acl'
|
13
|
+
def get( name )
|
14
|
+
share_exists = @wdmc.share_exists?( name )
|
15
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
16
|
+
Wdmc::Share.new.show( name )
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'set [SHARE NAME]', 'Set ACL for a share'
|
20
|
+
method_option :user, :aliases => '-u', :desc => 'Username', :type => :string, :required => true
|
21
|
+
method_option :access, :aliases => '-a', :desc => 'Can be RO (Read only) or RW (READ/WRITE)', :type => :string, :required => true
|
22
|
+
def set( name )
|
23
|
+
share_exists = @wdmc.share_exists?( name )
|
24
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
25
|
+
begin
|
26
|
+
data = {
|
27
|
+
:share_name => name,
|
28
|
+
:username => options['user'],
|
29
|
+
:access => options['access']
|
30
|
+
}
|
31
|
+
puts "Set ACL:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.set_acl( data )
|
32
|
+
share = @wdmc.get_acl( name )
|
33
|
+
share[:share_access].map do |access|
|
34
|
+
puts "\s- Username\t:\s".color(:whitesmoke) + access[:username]
|
35
|
+
puts "\s- User ID\t:\s".color(:whitesmoke) + access[:user_id]
|
36
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:green) if access[:access] == 'RW'
|
37
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:yellow) if access[:access] == 'RO'
|
38
|
+
puts
|
39
|
+
end
|
40
|
+
rescue RestClient::ExceptionWithResponse => e
|
41
|
+
puts e.response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'modify [SHARE NAME]', 'Edit ACL for a share'
|
46
|
+
method_option :user, :aliases => '-u', :desc => 'Username', :type => :string, :required => true
|
47
|
+
method_option :access, :aliases => '-a', :desc => 'Can be RO (Read only) or RW (READ/WRITE)', :type => :string, :required => true
|
48
|
+
def edit( name )
|
49
|
+
share_exists = @wdmc.share_exists?( name )
|
50
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
51
|
+
begin
|
52
|
+
data = {
|
53
|
+
:share_name => name,
|
54
|
+
:username => options['user'],
|
55
|
+
:access => options['access']
|
56
|
+
}
|
57
|
+
puts "Modify ACL:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.modify_acl( data )
|
58
|
+
share = @wdmc.get_acl( name )
|
59
|
+
share[:share_access].map do |access|
|
60
|
+
puts "\s- Username\t:\s".color(:whitesmoke) + access[:username]
|
61
|
+
puts "\s- User ID\t:\s".color(:whitesmoke) + access[:user_id]
|
62
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:green) if access[:access] == 'RW'
|
63
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:yellow) if access[:access] == 'RO'
|
64
|
+
puts
|
65
|
+
end
|
66
|
+
rescue RestClient::ExceptionWithResponse => e
|
67
|
+
puts e.response
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'remove [SHARE NAME]', 'Delete ACL for a share'
|
72
|
+
method_option :user, :aliases => '-u', :desc => 'Username', :type => :string, :required => true
|
73
|
+
method_option :force, :aliases => '-f', :desc => 'force (caution!)', :type => :boolean
|
74
|
+
def delete( name )
|
75
|
+
share_exists = @wdmc.share_exists?( name )
|
76
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
77
|
+
begin
|
78
|
+
data = {
|
79
|
+
:share_name => name,
|
80
|
+
:username => options['user']
|
81
|
+
}
|
82
|
+
unless options['force']
|
83
|
+
puts "\nYou are going to delete #{options['access']} access for user #{options['user']} to #{name}".color(:orange)
|
84
|
+
return unless yes?("DELETE? (yes/no): ")
|
85
|
+
end
|
86
|
+
puts "Remove ACL:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.delete_acl( data )
|
87
|
+
share = @wdmc.get_acl( name )
|
88
|
+
share[:share_access].map do |access|
|
89
|
+
puts "\s- Username\t:\s".color(:whitesmoke) + access[:username]
|
90
|
+
puts "\s- User ID\t:\s".color(:whitesmoke) + access[:user_id]
|
91
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:green) if access[:access] == 'RW'
|
92
|
+
puts "\s- Access\t:\s".color(:whitesmoke) + access[:access].color(:yellow) if access[:access] == 'RO'
|
93
|
+
puts
|
94
|
+
end
|
95
|
+
rescue RestClient::ExceptionWithResponse => e
|
96
|
+
puts e.response
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/lib/wdmc/cli.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'wdmc'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Wdmc
|
5
|
+
class CLI < Thor
|
6
|
+
|
7
|
+
desc 'sysinfo', 'Device information'
|
8
|
+
def sysinfo
|
9
|
+
Wdmc::Device.new.summary
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'user', 'User commands'
|
13
|
+
subcommand "user", User
|
14
|
+
|
15
|
+
desc 'acl', 'Fileshare ACLs'
|
16
|
+
subcommand "acl", Acl
|
17
|
+
|
18
|
+
desc 'share', 'Fileshare commands'
|
19
|
+
subcommand "share", Share
|
20
|
+
|
21
|
+
desc 'tm', 'TimeMachine commands'
|
22
|
+
subcommand "tm", TimeMachine
|
23
|
+
|
24
|
+
desc 'device', 'Device commands'
|
25
|
+
subcommand "device", Device
|
26
|
+
|
27
|
+
desc 'version', 'Version information'
|
28
|
+
def version
|
29
|
+
puts Wdmc::VERSION
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/wdmc/client.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Wdmc
|
5
|
+
class Client
|
6
|
+
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
@config = Wdmc::Config.load
|
11
|
+
@config[:verify_ssl] = (@config['validate_cert'].nil? or @config['validate_cert'] != false) ? true : false
|
12
|
+
@config['api_net_nl_bug'] = @config['api_net_nl_bug'].nil? ? false : @config['api_net_nl_bug']
|
13
|
+
@cookiefile = File.join(ENV['HOME'], '.wdmc_cookie')
|
14
|
+
login
|
15
|
+
end
|
16
|
+
|
17
|
+
def login
|
18
|
+
@url = @config['url']
|
19
|
+
@username = @config['username']
|
20
|
+
@password = @config['password']
|
21
|
+
|
22
|
+
begin
|
23
|
+
api = get("#{@url}/api/2.1/rest/local_login?username=#{@username}&password=#{@password}")
|
24
|
+
rescue RestClient::SSLCertificateNotVerified => e
|
25
|
+
if @config['validate_cert'] == 'warn'
|
26
|
+
$stderr.puts("Warning: #{ e.class.name}: #{ e.message } for host URL: '#{ @url }'")
|
27
|
+
@config[:verify_ssl] = false
|
28
|
+
api = get("#{@url}/api/2.1/rest/local_login?username=#{@username}&password=#{@password}")
|
29
|
+
else
|
30
|
+
raise(e)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
cookie = api.cookies
|
35
|
+
File.write(@cookiefile, api.cookies)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cookies
|
39
|
+
file = File.read(@cookiefile)
|
40
|
+
eval(file)
|
41
|
+
#file = YAML.load_file(@cookiefile)
|
42
|
+
end
|
43
|
+
|
44
|
+
# device
|
45
|
+
def system_information
|
46
|
+
response = get("#{@config['url']}/api/2.1/rest/system_information", {accept: :json, :cookies => cookies})
|
47
|
+
JSON.parse(response, :symbolize_names => true)[:system_information]
|
48
|
+
end
|
49
|
+
|
50
|
+
def system_state
|
51
|
+
response = get("#{@config['url']}/api/2.1/rest/system_state", {accept: :json, :cookies => cookies})
|
52
|
+
JSON.parse(response, :symbolize_names => true)[:system_state]
|
53
|
+
end
|
54
|
+
|
55
|
+
def firmware
|
56
|
+
response = get("#{@config['url']}/api/2.1/rest/firmware_info", {accept: :json, :cookies => cookies})
|
57
|
+
JSON.parse(response, :symbolize_names => true)[:firmware_info]
|
58
|
+
end
|
59
|
+
|
60
|
+
def device_description
|
61
|
+
response = get("#{@config['url']}/api/2.1/rest/device_description", {accept: :json, :cookies => cookies})
|
62
|
+
JSON.parse(response, :symbolize_names => true)[:device_description]
|
63
|
+
end
|
64
|
+
|
65
|
+
def network
|
66
|
+
response = get("#{@config['url']}/api/2.1/rest/network_configuration", {accept: :json, :cookies => cookies})
|
67
|
+
if @config['api_net_nl_bug']
|
68
|
+
response = response.delete("\n")
|
69
|
+
end
|
70
|
+
begin
|
71
|
+
JSON.parse(response, :symbolize_names => true)[:network_configuration]
|
72
|
+
rescue JSON::ParserError => e
|
73
|
+
unless @config['api_net_nl_bug']
|
74
|
+
$stderr.puts(<<~EOL.tr("\n", " ")
|
75
|
+
Warning: Consider adding 'api_net_nl_bug: true' to your configuration file
|
76
|
+
to mitigate a known bug retrieving network configuration data.
|
77
|
+
EOL
|
78
|
+
)
|
79
|
+
end
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# storage
|
85
|
+
def storage_usage
|
86
|
+
response = get("#{@config['url']}/api/2.1/rest/storage_usage", {accept: :json, :cookies => cookies})
|
87
|
+
JSON.parse(response, :symbolize_names => true)[:storage_usage]
|
88
|
+
end
|
89
|
+
|
90
|
+
## working with shares
|
91
|
+
# get all shares
|
92
|
+
def all_shares
|
93
|
+
response = get("#{@config['url']}/api/2.1/rest/shares", {accept: :json, :cookies => cookies})
|
94
|
+
JSON.parse(response, :symbolize_names => true)[:shares][:share]
|
95
|
+
end
|
96
|
+
|
97
|
+
# find a share by name
|
98
|
+
def find_share( name )
|
99
|
+
result = []
|
100
|
+
all_shares.each do |share|
|
101
|
+
result.push share if share[:share_name] == name
|
102
|
+
end
|
103
|
+
return result
|
104
|
+
end
|
105
|
+
|
106
|
+
# check if share with exists
|
107
|
+
def share_exists?( name )
|
108
|
+
result = []
|
109
|
+
all_shares.each do |share|
|
110
|
+
result.push share[:share_name] if share[:share_name].include?(name)
|
111
|
+
end
|
112
|
+
return result
|
113
|
+
end
|
114
|
+
|
115
|
+
# add new share
|
116
|
+
def add_share( data )
|
117
|
+
response = post("#{@config['url']}/api/2.1/rest/shares", data, {accept: :json, :cookies => cookies})
|
118
|
+
return response.code
|
119
|
+
end
|
120
|
+
|
121
|
+
# modifies a share
|
122
|
+
def modify_share( data )
|
123
|
+
response = put("#{@config['url']}/api/2.1/rest/shares", data, {accept: :json, :cookies => cookies})
|
124
|
+
return response.code
|
125
|
+
end
|
126
|
+
|
127
|
+
# delete a share
|
128
|
+
def delete_share( name )
|
129
|
+
response = delete("#{@config['url']}/api/2.1/rest/shares/#{name}", {accept: :json, :cookies => cookies})
|
130
|
+
return response.code
|
131
|
+
end
|
132
|
+
|
133
|
+
## working with ACL of a share
|
134
|
+
# get the specified share access
|
135
|
+
def get_acl( name )
|
136
|
+
response = get("#{@config['url']}/api/2.1/rest/share_access/#{name}", {accept: :json, :cookies => cookies})
|
137
|
+
JSON.parse(response, :symbolize_names => true)[:share_access_list]
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_acl( data )
|
141
|
+
response = post("#{@config['url']}/api/2.1/rest/share_access", data, {accept: :json, :cookies => cookies})
|
142
|
+
return response.code
|
143
|
+
end
|
144
|
+
|
145
|
+
def modify_acl( data )
|
146
|
+
response = put("#{@config['url']}/api/2.1/rest/share_access", data, {accept: :json, :cookies => cookies})
|
147
|
+
return response.code
|
148
|
+
end
|
149
|
+
|
150
|
+
def delete_acl( data )
|
151
|
+
# well, I know the code below is not very pretty...
|
152
|
+
# if someone knows how this shitty delete with rest-client will work
|
153
|
+
response = delete("#{@config['url']}/api/2.1/rest/share_access?share_name=#{data[:share_name]}&username=#{data[:username]}", {accept: :json, :cookies => cookies})
|
154
|
+
return response
|
155
|
+
end
|
156
|
+
## ACL end
|
157
|
+
|
158
|
+
## TimeMachine
|
159
|
+
# Get TimeMachine Configuration
|
160
|
+
def get_tm
|
161
|
+
response = get("#{@config['url']}/api/2.1/rest/time_machine", {accept: :json, :cookies => cookies})
|
162
|
+
JSON.parse(response, :symbolize_names => true)[:time_machine]
|
163
|
+
end
|
164
|
+
|
165
|
+
# Set TimeMachine Configuration
|
166
|
+
def set_tm( data )
|
167
|
+
response = put("#{@config['url']}/api/2.1/rest/time_machine", data, {accept: :json, :cookies => cookies})
|
168
|
+
return response
|
169
|
+
end
|
170
|
+
|
171
|
+
## Users
|
172
|
+
# Get all users
|
173
|
+
def all_users
|
174
|
+
response = get("#{@config['url']}/api/2.1/rest/users", {accept: :json, :cookies => cookies})
|
175
|
+
JSON.parse(response, :symbolize_names => true)[:users][:user]
|
176
|
+
end
|
177
|
+
|
178
|
+
# find a user by name
|
179
|
+
def find_user( name )
|
180
|
+
result = []
|
181
|
+
all_users.each do |user|
|
182
|
+
result.push user if user[:username] == name
|
183
|
+
end
|
184
|
+
return result
|
185
|
+
end
|
186
|
+
|
187
|
+
# check if user with name exists
|
188
|
+
def user_exists?( name )
|
189
|
+
result = []
|
190
|
+
all_users.each do |user|
|
191
|
+
result.push user[:username] if user[:username].include?(name)
|
192
|
+
end
|
193
|
+
return result
|
194
|
+
end
|
195
|
+
|
196
|
+
# add new user
|
197
|
+
def add_user( data )
|
198
|
+
response = post("#{@config['url']}/api/2.1/rest/users", data, {accept: :json, :cookies => cookies})
|
199
|
+
return response.code
|
200
|
+
end
|
201
|
+
|
202
|
+
# update an existing user
|
203
|
+
def update_user( name, data )
|
204
|
+
response = put("#{@config['url']}/api/2.1/rest/users/#{name}", data, {accept: :json, :cookies => cookies})
|
205
|
+
return response.code
|
206
|
+
end
|
207
|
+
|
208
|
+
# delete user
|
209
|
+
def delete_user( name )
|
210
|
+
response = delete("#{@config['url']}/api/2.1/rest/users/#{name}", {accept: :json, :cookies => cookies})
|
211
|
+
return response.code
|
212
|
+
end
|
213
|
+
|
214
|
+
## Users
|
215
|
+
|
216
|
+
def volumes
|
217
|
+
login
|
218
|
+
response = get("#{@config['url']}/api/2.1/rest/volumes", {accept: :json, :cookies => cookies})
|
219
|
+
JSON.parse(response, :symbolize_names => true)[:volumes][:volume]
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def get(url, headers={}, &block)
|
225
|
+
execute_request(:method => :get, :url => url, :headers => headers, &block)
|
226
|
+
end
|
227
|
+
|
228
|
+
def post(url, payload, headers={}, &block)
|
229
|
+
execute_request(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
|
230
|
+
end
|
231
|
+
|
232
|
+
def put(url, payload, headers={}, &block)
|
233
|
+
execute_request(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
|
234
|
+
end
|
235
|
+
|
236
|
+
def delete(url, headers={}, &block)
|
237
|
+
execute_request(:method => :delete, :url => url, :headers => headers, &block)
|
238
|
+
end
|
239
|
+
|
240
|
+
def execute_request(args, &block)
|
241
|
+
args[:verify_ssl] = @config[:verify_ssl]
|
242
|
+
RestClient::Request.execute(args, &block)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
data/lib/wdmc/config.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Wdmc
|
4
|
+
class Config
|
5
|
+
|
6
|
+
def self.load
|
7
|
+
dotfile = File.join(ENV['HOME'], '.wdmc.yml')
|
8
|
+
if File.exist?(dotfile)
|
9
|
+
config = YAML.load_file(dotfile)
|
10
|
+
else
|
11
|
+
abort "Config not found #{dotfile}"
|
12
|
+
end
|
13
|
+
config
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/wdmc/device.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Wdmc
|
2
|
+
class Device < Thor
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@wdmc = Wdmc::Client.new
|
7
|
+
@device_description = @wdmc.device_description
|
8
|
+
@system_information = @wdmc.system_information
|
9
|
+
@firmware = @wdmc.firmware
|
10
|
+
@shares = @wdmc.all_shares
|
11
|
+
@network = @wdmc.network
|
12
|
+
@system_state = @wdmc.system_state
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'summary', 'Complete device overview'
|
17
|
+
def summary
|
18
|
+
info
|
19
|
+
puts
|
20
|
+
firmware
|
21
|
+
puts
|
22
|
+
usage
|
23
|
+
puts
|
24
|
+
state
|
25
|
+
puts
|
26
|
+
network
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'firmware', 'Firmware information'
|
30
|
+
def firmware
|
31
|
+
puts "Firmware".upcase.color(:magenta)
|
32
|
+
puts "\sFW Version\t\t: ".color(:whitesmoke) + @firmware[:current_firmware][:package][:version]
|
33
|
+
if @firmware[:firmware_update_available][:available] == 'false'
|
34
|
+
puts "\s\s- Updates available\t: ".color(:whitesmoke) + @firmware[:firmware_update_available][:available].color(:green)
|
35
|
+
else
|
36
|
+
puts "\s\s- Updates available\t: ".color(:whitesmoke) + @firmware[:firmware_update_available][:available].color(:orange)
|
37
|
+
end
|
38
|
+
if @firmware[:upgrades][:available] == 'false'
|
39
|
+
puts "\s\s- Upgrades available\t: ".color(:whitesmoke) + @firmware[:upgrades][:available].color(:green)
|
40
|
+
else
|
41
|
+
puts "\s\s- Upgrades available\t: ".color(:whitesmoke) + @firmware[:upgrades][:available].color(:orange)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'info', 'Basic information'
|
46
|
+
def info
|
47
|
+
puts "Device Information".upcase.color(:magenta)
|
48
|
+
puts "\sDevice Name\t\t: ".color(:whitesmoke) + @device_description[:machine_name]
|
49
|
+
puts "\sModel / Number\t\t: ".color(:whitesmoke) + "#{@system_information[:model_name]} " + @system_information[:model_number]
|
50
|
+
puts "\sSerial Number\t\t: ".color(:whitesmoke) + @system_information[:serial_number]
|
51
|
+
puts "\sDrive Number Serial\t: ".color(:whitesmoke) + @system_information[:master_drive_serial_number]
|
52
|
+
puts "\sCapacity\t\t: ".color(:whitesmoke) + @system_information[:capacity]
|
53
|
+
puts "\sShares\t\t\t: ".color(:whitesmoke) + @shares.size.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'usage', 'Device usage'
|
57
|
+
def usage
|
58
|
+
usage = @wdmc.storage_usage
|
59
|
+
free = usage[:size].to_i - usage[:usage].to_i
|
60
|
+
puts "Device Usage".upcase.color(:magenta)
|
61
|
+
puts "\sCapacity\t\t: ".color(:whitesmoke) + Filesize.from("#{usage[:size]} B").to_s('GB')
|
62
|
+
puts "\sFree\t\t\t: ".color(:whitesmoke) + Filesize.from("#{free} B").to_s('GB')
|
63
|
+
puts "\sUsage\t\t\t: ".color(:whitesmoke) + Filesize.from("#{usage[:usage]} B").to_s('GB')
|
64
|
+
puts "\s\s- Videos\t\t: ".color(:whitesmoke) + Filesize.from("#{usage[:video]} B").to_s('GB') if usage[:video].to_i > 0
|
65
|
+
puts "\s\s- Photos\t\t: ".color(:whitesmoke) + Filesize.from("#{usage[:photo]} B").to_s('GB') if usage[:photo].to_i > 0
|
66
|
+
puts "\s\s- Music\t\t: ".color(:whitesmoke) + Filesize.from("#{usage[:music]} B").to_s('GB') if usage[:music].to_i > 0
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'network', 'Device network information'
|
70
|
+
def network
|
71
|
+
puts "Network Configuration".upcase.color(:magenta)
|
72
|
+
puts "\sInterface\t\t: ".color(:whitesmoke) + @network[:ifname]
|
73
|
+
puts "\sType\t\t\t: ".color(:whitesmoke) + @network[:iftype]
|
74
|
+
puts "\sProtocol\t\t: ".color(:whitesmoke) + @network[:proto]
|
75
|
+
puts "\sIP Address\t\t: ".color(:whitesmoke) + @network[:ip]
|
76
|
+
puts "\sMAC Address\t\t: ".color(:whitesmoke) + @system_information[:mac_address]
|
77
|
+
puts "\sNetmask\t\t: ".color(:whitesmoke) + @network[:netmask]
|
78
|
+
puts "\sGateway\t\t: ".color(:whitesmoke) + @network[:gateway]
|
79
|
+
puts "\sDNS Servers\t\t: ".color(:whitesmoke) +
|
80
|
+
[@network[:dns0], @network[:dns1], @network[:dns2]].reject(&:empty?).join(', ')
|
81
|
+
end
|
82
|
+
|
83
|
+
desc 'state', 'Device state'
|
84
|
+
def state
|
85
|
+
puts "Device State".upcase.color(:magenta)
|
86
|
+
puts "\sStatus\t\t\t: ".color(:whitesmoke) + @system_state[:status]
|
87
|
+
puts "\sTemperature\t\t: ".color(:whitesmoke) + @system_state[:temperature].color(:green) if @system_state[:temperature] == 'good'
|
88
|
+
puts "\sTemperature\t\t: ".color(:whitesmoke) + @system_state[:temperature].color(:orange) if @system_state[:temperature] == 'bad'
|
89
|
+
puts "\sS.M.A.R.T\t\t: ".color(:whitesmoke) + @system_state[:smart].color(:green) if @system_state[:smart] == 'good'
|
90
|
+
puts "\sS.M.A.R.T\t\t: ".color(:whitesmoke) + @system_state[:smart].color(:orange) if @system_state[:smart] == 'bad'
|
91
|
+
puts "\sVolume\t\t\t: ".color(:whitesmoke) + @system_state[:volume].color(:green) if @system_state[:volume] == 'good'
|
92
|
+
puts "\sVolume\t\t\t: ".color(:whitesmoke) + @system_state[:volume].color(:orange) if @system_state[:volume] == 'bad'
|
93
|
+
puts "\sFree Space\t\t: ".color(:whitesmoke) + @system_state[:free_space].color(:green) if @system_state[:free_space] == 'good'
|
94
|
+
puts "\sFree Space\t\t: ".color(:whitesmoke) + @system_state[:free_space].color(:orange) if @system_state[:free_space] == 'bad'
|
95
|
+
puts "\sReported Status\t: ".color(:whitesmoke) + @system_state[:reported_status].color(:green) if @system_state[:reported_status] == 'good'
|
96
|
+
puts "\sReported Status\t: ".color(:whitesmoke) + @system_state[:reported_status].color(:orange) if @system_state[:reported_status] == 'bad'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/wdmc/shares.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require "wdmc/acl"
|
2
|
+
|
3
|
+
module Wdmc
|
4
|
+
class Share < Thor
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@wdmc = Wdmc::Client.new
|
9
|
+
@device_description = @wdmc.device_description
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'list', 'List all shares'
|
14
|
+
def list
|
15
|
+
shares = @wdmc.all_shares
|
16
|
+
puts "Available Shares".upcase.color(:magenta)
|
17
|
+
shares.each do |share|
|
18
|
+
puts "\s- #{share[:share_name]}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'show [NAME]', 'Show share'
|
23
|
+
def show( name )
|
24
|
+
shares = @wdmc.find_share( name )
|
25
|
+
share_exists = @wdmc.share_exists?( name )
|
26
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
27
|
+
shares.each do |share|
|
28
|
+
puts "Name:\s".upcase.color(:magenta) + share[:share_name]
|
29
|
+
puts "\sRemote Access\t\t: ".color(:whitesmoke) + "#{share[:remote_access]}"
|
30
|
+
puts "\sPublic Access\t\t: ".color(:whitesmoke) + "#{share[:public_access]}"
|
31
|
+
puts "\sMedia Serving\t\t: ".color(:whitesmoke) + "#{share[:media_serving]}"
|
32
|
+
if share[:share_access]
|
33
|
+
puts "Permissions\t\t ".upcase.color(:magenta)
|
34
|
+
share[:share_access].map do |access|
|
35
|
+
puts "\s#{access[:username]}\t\t\t:\s" + access[:access].color(:green) if access[:access] == 'RW'
|
36
|
+
puts "\s#{access[:username]}\t\t\t:\s" + access[:access].color(:cyan) if access[:access] == 'RO'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'create [NAME]', 'Create a new share'
|
43
|
+
method_option :description, :aliases => '-d', :desc => 'Volume description', :required => false
|
44
|
+
method_option :media_serving, :aliases => '-m', :desc => 'Enable media serving', :type => :boolean, :default => true
|
45
|
+
method_option :public_access, :aliases => '-p', :desc => 'Enable public access', :type => :boolean, :default => false
|
46
|
+
method_option :samba_available, :aliases => '-s', :desc => 'Enable samba access', :type => :boolean, :default => true
|
47
|
+
method_option :share_access_locked, :aliases => '-l', :desc => 'When set to true, share access cannot be set or modified on this Share once created.', :type => :boolean, :default => false
|
48
|
+
method_option :grant_share_access, :aliases => '-g', :desc => 'When set to true, automatically grants RW share access to the new Share for the User who is creating the new Share.', :type => :boolean, :default => false
|
49
|
+
def create( name )
|
50
|
+
share_exists = @wdmc.share_exists?( name )
|
51
|
+
abort "Share already exists!".color(:yellow) if share_exists.include?( name )
|
52
|
+
begin
|
53
|
+
data = {
|
54
|
+
:share_name => name,
|
55
|
+
:description => options['description'],
|
56
|
+
:media_serving => options['media_serving'],
|
57
|
+
:public_access => options['public_access'],
|
58
|
+
:samba_available => options['samba_available'],
|
59
|
+
:share_access_locked => options['share_access_locked'],
|
60
|
+
:grant_share_access => options['grant_share_access']
|
61
|
+
}
|
62
|
+
puts "Create share:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.add_share( data )
|
63
|
+
rescue RestClient::ExceptionWithResponse => e
|
64
|
+
e.response.each do |x|
|
65
|
+
p x
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'modify [NAME]', 'Modify a share'
|
71
|
+
method_option :new_share_name, :aliases => '-n', :desc => 'New name', :type => :string
|
72
|
+
method_option :description, :aliases => '-d', :desc => 'Description', :required => false
|
73
|
+
method_option :media_serving, :aliases => '-m', :desc => 'Enable media serving', :type => :boolean, :default => true
|
74
|
+
method_option :public_access, :aliases => '-p', :desc => 'Enable public access', :type => :boolean, :default => false
|
75
|
+
method_option :remote_access, :aliases => '-r', :desc => 'Enable remote access', :type => :boolean, :default => true
|
76
|
+
def modify( name )
|
77
|
+
share_exists = @wdmc.share_exists?( name )
|
78
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
79
|
+
begin
|
80
|
+
data = {
|
81
|
+
:share_name => name,
|
82
|
+
:new_share_name => options['new_share_name'] || name,
|
83
|
+
:description => options['description'],
|
84
|
+
:media_serving => options['media_serving'],
|
85
|
+
:public_access => options['public_access'],
|
86
|
+
:remote_access => options['remote_access']
|
87
|
+
}
|
88
|
+
puts "Modify share:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.modify_share( data )
|
89
|
+
rescue RestClient::ExceptionWithResponse => e
|
90
|
+
puts e.response
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'delete [NAME]', 'Delete a share'
|
95
|
+
method_option :force, :aliases => '-f', :desc => 'force (caution!)', :type => :boolean
|
96
|
+
def delete( name )
|
97
|
+
share_exists = @wdmc.share_exists?( name )
|
98
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
99
|
+
unless options['force']
|
100
|
+
puts "\nDeleting this share will delete all content and configuration settings within the share!".color(:orange)
|
101
|
+
puts "Are you sure you want to delete:\s".color(:orange) + "#{name}".color(:whitesmoke)
|
102
|
+
return unless yes?("DELETE? (yes/no):")
|
103
|
+
end
|
104
|
+
puts "Delete share:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.delete_share( name )
|
105
|
+
end
|
106
|
+
|
107
|
+
desc 'acl', 'Share access'
|
108
|
+
subcommand "acl", Acl
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
data/lib/wdmc/storage.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'wdmc'
|
2
|
+
require 'rainbow/ext/string'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
module Wdmc
|
8
|
+
class Storage < Thor
|
9
|
+
|
10
|
+
option :json, aliases: '-j', type: 'boolean', banner: 'Output as JSON'
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
@sc = Xsc::Client.new
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'search [QUERY]', 'Search Groups'
|
18
|
+
def shares
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Wdmc
|
2
|
+
class TimeMachine < Thor
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@wdmc = Wdmc::Client.new
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'get', 'Get TimeMachine configuration'
|
11
|
+
def get
|
12
|
+
get_tm = @wdmc.get_tm
|
13
|
+
puts "Time Machine".upcase.color(:magenta)
|
14
|
+
puts "\sBackup Share\t\t: ".color(:whitesmoke) + get_tm[:backup_share]
|
15
|
+
puts "\sEnabled\t\t: ".color(:whitesmoke) + get_tm[:backup_enabled].color(:green) if get_tm[:backup_enabled] == 'true'
|
16
|
+
puts "\sEnabled\t\t: ".color(:whitesmoke) + get_tm[:backup_enabled].color(:orange) if get_tm[:backup_enabled] == 'false'
|
17
|
+
if get_tm[:backup_size_limit] == '0'
|
18
|
+
puts "\sSize Limit\t\t: ".color(:whitesmoke) + "unlimited"
|
19
|
+
else
|
20
|
+
puts "\sSize Limit\t\t: ".color(:whitesmoke) + Filesize.from("#{get_tm[:backup_size_limit]} B").to_s('GB')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'set', 'Set TimeMachine configuration'
|
25
|
+
method_option :backup_size_limit, :aliases => '-s', :desc => 'Size limit [100 GB], default: unlimited', :type => :string, :default => '0GB'
|
26
|
+
method_option :backup_enabled, :aliases => '-d', :desc => 'Disable Backup', :type => :boolean, :default => true
|
27
|
+
def set( name )
|
28
|
+
share_exists = @wdmc.share_exists?( name )
|
29
|
+
abort "\nShare does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless share_exists.include?( name )
|
30
|
+
data = {
|
31
|
+
:backup_enabled => options[:backup_enabled],
|
32
|
+
:backup_size_limit => Filesize.from("#{options[:backup_size_limit]}").to_i
|
33
|
+
}
|
34
|
+
puts "Set TimeMachine:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.set_tm( data )
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/wdmc/user.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Wdmc
|
4
|
+
class User < Thor
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@wdmc = Wdmc::Client.new
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'list', 'List all users'
|
13
|
+
def list
|
14
|
+
users = @wdmc.all_users
|
15
|
+
puts "Users".upcase.color(:magenta)
|
16
|
+
users.each do |user|
|
17
|
+
puts "\s- #{user[:username]}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'show [USERNAME]', 'Show user information'
|
22
|
+
def show( name )
|
23
|
+
users = @wdmc.find_user( name )
|
24
|
+
user_exists = @wdmc.user_exists?( name )
|
25
|
+
abort "\nUser does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless user_exists.include?( name )
|
26
|
+
users.each do |user|
|
27
|
+
puts "Username:\s".upcase.color(:magenta) + user[:username]
|
28
|
+
puts "\sUser ID\t\t: ".color(:whitesmoke) + "#{user[:user_id]}"
|
29
|
+
puts "\sFullname\t\t: ".color(:whitesmoke) + "#{user[:fullname]}" unless user[:fullname].empty?
|
30
|
+
puts "\sAdmin\t\t\t: ".color(:whitesmoke) + "#{user[:is_admin]}"
|
31
|
+
puts "\sPassword set\t\t: ".color(:whitesmoke) + "#{user[:is_password]}".color(:green) if user[:is_password] == 'true'
|
32
|
+
puts "\sPassword set\t\t: ".color(:whitesmoke) + "#{user[:is_password]}".color(:orange) if user[:is_password] == 'false'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'create [USERNAME] [OPTIONS]', 'Create an new user'
|
37
|
+
method_option :password, :aliases => '-p', :desc => 'Password', :type => :string
|
38
|
+
method_option :fullname, :desc => 'Fullname of the user to be created', :type => :string
|
39
|
+
method_option :first_name, :aliases => '-f', :desc => 'First name of the user to be created', :type => :string
|
40
|
+
method_option :last_name, :aliases => '-l', :desc => 'Last name of the user to be created', :type => :string
|
41
|
+
method_option :group_names, :aliases => '-g', :desc => 'Accepts a comma separated list of group names', :type => :string
|
42
|
+
method_option :email, :aliases => '-m', :desc => 'If provided, a device user will be created as well as a local (Linux) user.', :type => :string
|
43
|
+
method_option :admin, :aliases => '-a', :desc => 'Give user admin rights', :type => :boolean
|
44
|
+
def create( name )
|
45
|
+
password = Base64.strict_encode64(options[:password]) if options[:password]
|
46
|
+
user_exists = @wdmc.user_exists?( name )
|
47
|
+
abort "\nUser does not exist: ".color(:yellow) + "#{name}".color(:cyan) if user_exists.include?( name )
|
48
|
+
begin
|
49
|
+
groups = ['cloudholders']
|
50
|
+
groups.push options[:group_names] if options[:group_names]
|
51
|
+
data = {
|
52
|
+
:email => options[:email],
|
53
|
+
:username => name,
|
54
|
+
:password => password,
|
55
|
+
:fullname => options[:fullname],
|
56
|
+
:is_admin => options[:admin],
|
57
|
+
:group_names => groups.join(','),
|
58
|
+
:first_name => options[:first_name],
|
59
|
+
:last_name => options[:last_name]
|
60
|
+
}
|
61
|
+
puts "Create user:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.add_user( data )
|
62
|
+
rescue RestClient::ExceptionWithResponse => e
|
63
|
+
puts eval(e.response)[:users][:error_message].color(:orange)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'update [USERNAME] [OPTIONS]', 'Updates an existing user'
|
68
|
+
method_option :new_username, :aliases => '-r', :desc => 'New username', :type => :string
|
69
|
+
method_option :password, :aliases => '-p', :desc => 'New password', :type => :string
|
70
|
+
method_option :fullname, :desc => 'Update Fullname of the user', :type => :string
|
71
|
+
method_option :first_name, :aliases => '-f', :desc => 'Update first name of the user', :type => :string
|
72
|
+
method_option :last_name, :aliases => '-l', :desc => 'Update last name of the user', :type => :string
|
73
|
+
method_option :admin, :aliases => '-a', :desc => '[true/false] default = false, Give user admin rights ', :type => :boolean, :default => false
|
74
|
+
def update( name )
|
75
|
+
password = Base64.strict_encode64(options[:password]) if options[:password]
|
76
|
+
user_exists = @wdmc.user_exists?( name )
|
77
|
+
abort "\nUser does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless user_exists.include?( name )
|
78
|
+
begin
|
79
|
+
data = {
|
80
|
+
:username => options[:new_username] || name,
|
81
|
+
:password => password,
|
82
|
+
:fullname => options[:fullname],
|
83
|
+
:is_admin => options[:admin],
|
84
|
+
:first_name => options[:first_name],
|
85
|
+
:last_name => options[:last_name]
|
86
|
+
}
|
87
|
+
puts "Update user:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.update_user( name, data )
|
88
|
+
rescue RestClient::ExceptionWithResponse => e
|
89
|
+
puts eval(e.response)[:users] #[:error_message].color(:orange)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'delete [USERNAME]', 'Delete a user'
|
94
|
+
method_option :force, :aliases => '-f', :desc => 'force', :type => :boolean
|
95
|
+
def delete( name )
|
96
|
+
user_exists = @wdmc.user_exists?( name )
|
97
|
+
abort "\nUser does not exist: ".color(:yellow) + "#{name}".color(:cyan) unless user_exists.include?( name )
|
98
|
+
unless options['force']
|
99
|
+
puts "\nAre you sure you want to delete this user?\s".color(:orange) + "#{name}".color(:whitesmoke)
|
100
|
+
return unless yes?("DELETE? (yes/no):")
|
101
|
+
end
|
102
|
+
puts "Delete user:\s".color(:whitesmoke) + "OK".color(:green) if @wdmc.delete_user( name )
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
data/lib/wdmc/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wdmc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ole Kleinschmidt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-10-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rest-client
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rainbow
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
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: highline
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
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: filesize
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- ok@datenreisende.org
|
114
|
+
executables:
|
115
|
+
- wdmc
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- bin/wdmc
|
120
|
+
- lib/wdmc.rb
|
121
|
+
- lib/wdmc/acl.rb
|
122
|
+
- lib/wdmc/cli.rb
|
123
|
+
- lib/wdmc/client.rb
|
124
|
+
- lib/wdmc/config.rb
|
125
|
+
- lib/wdmc/device.rb
|
126
|
+
- lib/wdmc/shares.rb
|
127
|
+
- lib/wdmc/storage.rb
|
128
|
+
- lib/wdmc/timemachine.rb
|
129
|
+
- lib/wdmc/user.rb
|
130
|
+
- lib/wdmc/version.rb
|
131
|
+
homepage: https://github.com/okleinschmidt/wdmc
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.7.8
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Commandline for WD MyCloud NAS
|
155
|
+
test_files: []
|