palo_alto 0.3.2 → 0.4.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/lib/palo_alto/config.rb +66077 -41475
- data/lib/palo_alto/log.rb +27 -13
- data/lib/palo_alto/version.rb +1 -1
- data/lib/palo_alto.rb +61 -21
- data/palo_alto.gemspec +1 -1
- metadata +3 -3
data/lib/palo_alto/log.rb
CHANGED
@@ -12,7 +12,7 @@ module PaloAlto
|
|
12
12
|
class Log < Enumerator
|
13
13
|
def initialize(client:, query:, log_type:, nlogs: 20, dir: :backward, show_detail: false, days: 7) # rubocop:disable Metrics/MethodLength,Metrics/ParameterLists
|
14
14
|
@client = client
|
15
|
-
|
15
|
+
@log_query_payload = {
|
16
16
|
type: 'log',
|
17
17
|
'log-type': log_type,
|
18
18
|
nlogs: nlogs,
|
@@ -22,15 +22,22 @@ module PaloAlto
|
|
22
22
|
}
|
23
23
|
|
24
24
|
if days
|
25
|
-
|
25
|
+
@log_query_payload[:query] += " AND (receive_time geq '#{(Time.now - days * 3600 * 24).strftime('%Y/%m/%d %H:%M:%S')}')"
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
run_query
|
29
|
+
|
30
|
+
@first_result = fetch_result
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def run_query
|
35
|
+
result = @client.execute(@log_query_payload)
|
29
36
|
@job_id = result.at_xpath('response/result/job').text
|
37
|
+
warn "#{@client.host} #{Time.now}: Got job id #{@job_id} for log query"
|
38
|
+
|
30
39
|
@count = nil
|
31
40
|
@skip = 0
|
32
|
-
@first_result = fetch_result
|
33
|
-
super
|
34
41
|
end
|
35
42
|
|
36
43
|
def restore_first
|
@@ -46,17 +53,24 @@ module PaloAlto
|
|
46
53
|
def fetch_result # rubocop:disable Metrics/MethodLength
|
47
54
|
return nil if @count && @skip == @count
|
48
55
|
|
49
|
-
payload = {
|
50
|
-
type: 'log',
|
51
|
-
action: 'get',
|
52
|
-
'job-id': @job_id,
|
53
|
-
skip: @skip
|
54
|
-
}
|
55
|
-
|
56
56
|
i = 0
|
57
57
|
loop do
|
58
58
|
sleep 0.5 if i.positive?
|
59
|
-
|
59
|
+
begin
|
60
|
+
payload = {
|
61
|
+
type: 'log',
|
62
|
+
action: 'get',
|
63
|
+
'job-id': @job_id,
|
64
|
+
skip: @skip
|
65
|
+
}
|
66
|
+
@current_result = @client.execute(payload)
|
67
|
+
rescue PaloAlto::UnknownErrorException => e
|
68
|
+
if e.message == 'Query timed out'
|
69
|
+
warn 'Retrying log query'
|
70
|
+
run_query
|
71
|
+
retry
|
72
|
+
end
|
73
|
+
end
|
60
74
|
i += 1
|
61
75
|
break if @current_result.at_xpath('response/result/job/status').text == 'FIN'
|
62
76
|
end
|
data/lib/palo_alto/version.rb
CHANGED
data/lib/palo_alto.rb
CHANGED
@@ -30,7 +30,7 @@ module PaloAlto
|
|
30
30
|
class UnknownCommandException < PermanentException
|
31
31
|
end
|
32
32
|
|
33
|
-
class
|
33
|
+
class UnknownErrorException < TemporaryException
|
34
34
|
end
|
35
35
|
|
36
36
|
class BadXpathException < PermanentException
|
@@ -39,6 +39,9 @@ module PaloAlto
|
|
39
39
|
class ObjectNotPresentException < PermanentException
|
40
40
|
end
|
41
41
|
|
42
|
+
class ObjectNotFoundException < PermanentException
|
43
|
+
end
|
44
|
+
|
42
45
|
class ObjectNotUniqueException < PermanentException
|
43
46
|
end
|
44
47
|
|
@@ -122,6 +125,8 @@ module PaloAlto
|
|
122
125
|
case response.code
|
123
126
|
when '200'
|
124
127
|
return response.body
|
128
|
+
when '401'
|
129
|
+
raise SessionTimedOutException
|
125
130
|
when '400', '403'
|
126
131
|
begin
|
127
132
|
data = Nokogiri::XML.parse(response.body)
|
@@ -136,7 +141,7 @@ module PaloAlto
|
|
136
141
|
end
|
137
142
|
|
138
143
|
nil
|
139
|
-
rescue Net::OpenTimeout, Errno::ECONNREFUSED => e
|
144
|
+
rescue Net::OpenTimeout, Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
140
145
|
raise ConnectionErrorException, e.message
|
141
146
|
end
|
142
147
|
|
@@ -145,13 +150,12 @@ module PaloAlto
|
|
145
150
|
when 400 then BadRequestException
|
146
151
|
when 403 then ForbiddenException
|
147
152
|
when 1 then UnknownCommandException
|
148
|
-
when 2..5 then InternalErrorsException
|
149
153
|
when 6 then BadXpathException
|
150
154
|
when 7 then ObjectNotPresentException
|
151
155
|
when 8 then ObjectNotUniqueException
|
152
156
|
when 10 then ReferenceCountNotZeroException
|
153
|
-
when 0, 11, 21 then InternalErrorException # also if there is no code..
|
154
157
|
when 12 then InvalidObjectException
|
158
|
+
when 13 then ObjectNotFoundException
|
155
159
|
when 14 then OperationNotPossibleException
|
156
160
|
when 15 then OperationDeniedException
|
157
161
|
when 16 then UnauthorizedException
|
@@ -159,7 +163,8 @@ module PaloAlto
|
|
159
163
|
when 18 then MalformedCommandException
|
160
164
|
when 19..20 then SuccessException
|
161
165
|
when 22 then SessionTimedOutException
|
162
|
-
|
166
|
+
when 2..5, 11, 21 then InternalErrorException
|
167
|
+
else UnknownErrorException
|
163
168
|
end
|
164
169
|
raise error, message
|
165
170
|
end
|
@@ -170,7 +175,7 @@ module PaloAlto
|
|
170
175
|
attr_accessor :host, :username, :auth_key, :verify_ssl, :debug, :timeout
|
171
176
|
|
172
177
|
def pretty_print_instance_variables
|
173
|
-
super - [:@password, :@subclasses, :@subclasses, :@expression, :@arguments, :@cache]
|
178
|
+
super - [:@password, :@subclasses, :@subclasses, :@expression, :@arguments, :@cache, :@op, :@auth_key]
|
174
179
|
end
|
175
180
|
|
176
181
|
def execute(payload, skip_authentication: false, skip_cache: false)
|
@@ -202,7 +207,7 @@ module PaloAlto
|
|
202
207
|
end
|
203
208
|
end
|
204
209
|
|
205
|
-
retried =
|
210
|
+
retried = 0
|
206
211
|
begin
|
207
212
|
# configure options for the request
|
208
213
|
options = {}
|
@@ -229,27 +234,57 @@ module PaloAlto
|
|
229
234
|
|
230
235
|
data = Nokogiri::XML.parse(text)
|
231
236
|
unless data.xpath('//response/@status').to_s == 'success'
|
232
|
-
|
233
|
-
|
234
|
-
|
237
|
+
unless %w[op commit].include?(payload[:type]) # here we fail silent
|
238
|
+
warn "command failed on host #{host} at #{Time.now}"
|
239
|
+
warn "sent:\n#{options.inspect}\n" if debug.include?(:sent_on_error)
|
240
|
+
warn "received:\n#{text.inspect}\n" if debug.include?(:received_on_error)
|
241
|
+
end
|
235
242
|
code = data.at_xpath('//response/@code')&.value.to_i # sometimes there is no code :( e.g. for 'op' errors
|
236
243
|
message = data.xpath('/response/msg/line').map(&:text).map(&:strip).join("\n")
|
237
244
|
Helpers::Rest.raise_error(code, message)
|
238
245
|
end
|
239
246
|
|
240
247
|
data
|
248
|
+
rescue EOFError, Net::ReadTimeout => e
|
249
|
+
max_retries = if %w[keygen config].include?(payload[:type])
|
250
|
+
# TODO: only retry on config, when it's get or edit, otherwise you may get strange errors
|
251
|
+
3
|
252
|
+
else
|
253
|
+
0
|
254
|
+
end
|
255
|
+
|
256
|
+
raise e if retried >= max_retries
|
257
|
+
|
258
|
+
retried += 1
|
259
|
+
warn "Got error #{e.inspect}; retrying (try #{retried})" if debug.include?(:warnings)
|
260
|
+
sleep 10
|
261
|
+
retry
|
241
262
|
rescue TemporaryException => e
|
242
263
|
dont_retry_at = [
|
243
264
|
'Partial revert is not allowed. Full system commit must be completed.',
|
244
265
|
'Config for scope ',
|
245
266
|
'Config is not currently locked for scope ',
|
246
267
|
'Commit lock is not currently held by',
|
247
|
-
'You already own a config lock for scope '
|
268
|
+
'You already own a config lock for scope ',
|
269
|
+
'This operation is blocked because of ',
|
270
|
+
'Other administrators are holding config locks ',
|
271
|
+
'Configuration is locked by ',
|
272
|
+
' device-group' # device-group -> ... is already in use
|
248
273
|
]
|
249
|
-
raise e if retried || dont_retry_at.any? { |x| e.message.start_with?(x) }
|
250
274
|
|
251
|
-
|
252
|
-
|
275
|
+
max_retries = if dont_retry_at.any? { |str| e.message.start_with?(str) }
|
276
|
+
0
|
277
|
+
elsif e.message.start_with?('Timed out while getting config lock. Please try again.')
|
278
|
+
10
|
279
|
+
else
|
280
|
+
1
|
281
|
+
end
|
282
|
+
|
283
|
+
raise e if retried >= max_retries
|
284
|
+
|
285
|
+
retried += 1
|
286
|
+
warn "Got error #{e.inspect}; retrying (try #{retried})" if debug.include?(:warnings)
|
287
|
+
|
253
288
|
get_auth_key if e.is_a?(SessionTimedOutException)
|
254
289
|
retry
|
255
290
|
end
|
@@ -280,7 +315,7 @@ module PaloAlto
|
|
280
315
|
def commit!(all: false, device_groups: nil, templates: nil,
|
281
316
|
admins: [username],
|
282
317
|
raw_result: false,
|
283
|
-
wait_for_completion: true, wait: 5, timeout:
|
318
|
+
wait_for_completion: true, wait: 5, timeout: 60 * 20)
|
284
319
|
return nil if device_groups.is_a?(Array) && device_groups.empty? && templates.is_a?(Array) && templates.empty?
|
285
320
|
|
286
321
|
cmd = if all
|
@@ -359,9 +394,10 @@ module PaloAlto
|
|
359
394
|
cmd = { request: { "#{area}-lock": { add: { comment: comment || '(null)' } } } }
|
360
395
|
op.execute(cmd, type: type, location: location)
|
361
396
|
true
|
362
|
-
rescue PaloAlto::
|
397
|
+
rescue PaloAlto::UnknownErrorException => e
|
363
398
|
return true if e.message.start_with?('You already own a config lock for scope ') ||
|
364
|
-
e.message == "Config for scope shared is currently locked by #{username}"
|
399
|
+
e.message == "Config for scope shared is currently locked by #{username}" ||
|
400
|
+
e.message == "Config for scope #{location} is currently locked by #{username}"
|
365
401
|
|
366
402
|
false
|
367
403
|
end
|
@@ -375,7 +411,9 @@ module PaloAlto
|
|
375
411
|
{ request: { "#{area}-lock": 'remove' } }
|
376
412
|
end
|
377
413
|
op.execute(cmd, type: type, location: location)
|
378
|
-
rescue PaloAlto::
|
414
|
+
rescue PaloAlto::UnknownErrorException => e
|
415
|
+
return true if e.message.start_with?('Config is not currently locked')
|
416
|
+
|
379
417
|
return false
|
380
418
|
end
|
381
419
|
true
|
@@ -411,7 +449,7 @@ module PaloAlto
|
|
411
449
|
|
412
450
|
nil
|
413
451
|
rescue => e
|
414
|
-
warn [:job_query_error, e].inspect
|
452
|
+
warn [:job_query_error, @host, e].inspect
|
415
453
|
false
|
416
454
|
end
|
417
455
|
|
@@ -438,7 +476,7 @@ module PaloAlto
|
|
438
476
|
job_result
|
439
477
|
end
|
440
478
|
|
441
|
-
def wait_for_job_completion(job_id, wait: 5, timeout:
|
479
|
+
def wait_for_job_completion(job_id, wait: 5, timeout: 20 * 60)
|
442
480
|
cmd = { show: { jobs: { id: job_id } } }
|
443
481
|
start = Time.now
|
444
482
|
loop do
|
@@ -447,11 +485,13 @@ module PaloAlto
|
|
447
485
|
status = result.at_xpath('response/result/job/status')&.text
|
448
486
|
return result unless %w[ACT PEND].include?(status)
|
449
487
|
rescue => e
|
450
|
-
warn [:job_query_error, e].inspect
|
488
|
+
warn [:job_query_error, Time.now, @host, e].inspect
|
489
|
+
return false if e.message =~ /\Ajob \d+ not found\z/
|
451
490
|
end
|
452
491
|
sleep wait
|
453
492
|
break unless start + timeout > Time.now
|
454
493
|
end
|
494
|
+
warn [:job_query_error, Time.now, @host, :timeout].inspect
|
455
495
|
false
|
456
496
|
end
|
457
497
|
|
data/palo_alto.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = 'Palo Alto API for Ruby'
|
12
12
|
spec.homepage = 'https://github.com/Sebbb/'
|
13
|
-
spec.license = '
|
13
|
+
spec.license = 'Artistic-2.0'
|
14
14
|
spec.required_ruby_version = '>= 2.7.0'
|
15
15
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: palo_alto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Roesner
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -52,7 +52,7 @@ files:
|
|
52
52
|
- palo_alto.gemspec
|
53
53
|
homepage: https://github.com/Sebbb/
|
54
54
|
licenses:
|
55
|
-
-
|
55
|
+
- Artistic-2.0
|
56
56
|
metadata:
|
57
57
|
homepage_uri: https://github.com/Sebbb/
|
58
58
|
source_code_uri: https://github.com/Sebbb/palo_alto/
|