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.
@@ -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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'wdmc/cli'
4
+
5
+ Wdmc::CLI.start
@@ -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"
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Wdmc
2
+ VERSION = "0.1.0"
3
+ end
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: []