palo_alto 0.3.1 → 0.3.2
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/README.md +2 -2
- data/lib/palo_alto/config.rb +10288 -10295
- data/lib/palo_alto/version.rb +1 -1
- data/lib/palo_alto.rb +100 -10
- metadata +2 -2
data/lib/palo_alto/version.rb
CHANGED
data/lib/palo_alto.rb
CHANGED
@@ -170,14 +170,38 @@ module PaloAlto
|
|
170
170
|
attr_accessor :host, :username, :auth_key, :verify_ssl, :debug, :timeout
|
171
171
|
|
172
172
|
def pretty_print_instance_variables
|
173
|
-
super - [:@password, :@subclasses, :@subclasses, :@expression, :@arguments]
|
173
|
+
super - [:@password, :@subclasses, :@subclasses, :@expression, :@arguments, :@cache]
|
174
174
|
end
|
175
175
|
|
176
|
-
def execute(payload, skip_authentication: false)
|
176
|
+
def execute(payload, skip_authentication: false, skip_cache: false)
|
177
177
|
if !auth_key && !skip_authentication
|
178
178
|
get_auth_key
|
179
179
|
end
|
180
180
|
|
181
|
+
if payload[:type] == 'config' && !skip_cache
|
182
|
+
if payload[:action] == 'get'
|
183
|
+
start_time = Time.now
|
184
|
+
@cache.each do |cached_xpath, cache|
|
185
|
+
search_xpath = payload[:xpath].sub('/descendant::device-group[1]/', '/device-group/')
|
186
|
+
next unless search_xpath.start_with?(cached_xpath)
|
187
|
+
|
188
|
+
remove = cached_xpath.split('/')[1...-1].join('/').length
|
189
|
+
new_xpath = 'response/result/' + search_xpath[(remove+2)..]
|
190
|
+
|
191
|
+
results = cache.xpath(new_xpath)
|
192
|
+
xml = Nokogiri.parse("<?xml version=\"1.0\"?><response><result>#{results.to_s}</result></response>")
|
193
|
+
|
194
|
+
if debug.include?(:statistics)
|
195
|
+
warn "Elapsed for parsing cache: #{Time.now - start_time} seconds"
|
196
|
+
end
|
197
|
+
|
198
|
+
return xml
|
199
|
+
end
|
200
|
+
elsif !@keep_cache_on_edit
|
201
|
+
@cache = {}
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
181
205
|
retried = false
|
182
206
|
begin
|
183
207
|
# configure options for the request
|
@@ -198,7 +222,7 @@ module PaloAlto
|
|
198
222
|
start_time = Time.now
|
199
223
|
text = Helpers::Rest.make_request(options)
|
200
224
|
if debug.include?(:statistics)
|
201
|
-
warn "Elapsed for API call #{payload[:type]}/#{payload[:action] || '(unknown action)'} on #{host}: #{Time.now - start_time} seconds"
|
225
|
+
warn "Elapsed for API call #{payload[:type]}/#{payload[:action] || '(unknown action)'} on #{host}: #{Time.now - start_time} seconds, #{text.length} bytes"
|
202
226
|
end
|
203
227
|
|
204
228
|
warn "received: #{Time.now}\n#{text}\n" if debug.include?(:received)
|
@@ -231,14 +255,39 @@ module PaloAlto
|
|
231
255
|
end
|
232
256
|
end
|
233
257
|
|
234
|
-
def
|
258
|
+
def clear_cache!
|
259
|
+
@cache = {}
|
260
|
+
@keep_cache_on_edit = nil
|
261
|
+
end
|
262
|
+
|
263
|
+
def keep_cache_on_edit!
|
264
|
+
@keep_cache_on_edit = true
|
265
|
+
end
|
266
|
+
|
267
|
+
def cache!(xpath)
|
268
|
+
cached_xpath = xpath.is_a?(String) ? xpath : xpath.to_xpath
|
269
|
+
|
270
|
+
payload = {
|
271
|
+
type: 'config',
|
272
|
+
action: 'get',
|
273
|
+
xpath: cached_xpath
|
274
|
+
}
|
275
|
+
|
276
|
+
@cache[cached_xpath] = execute(payload, skip_cache: true)
|
277
|
+
true
|
278
|
+
end
|
279
|
+
|
280
|
+
def commit!(all: false, device_groups: nil, templates: nil,
|
281
|
+
admins: [username],
|
282
|
+
raw_result: false,
|
283
|
+
wait_for_completion: true, wait: 5, timeout: 480)
|
235
284
|
return nil if device_groups.is_a?(Array) && device_groups.empty? && templates.is_a?(Array) && templates.empty?
|
236
285
|
|
237
286
|
cmd = if all
|
238
287
|
'commit'
|
239
288
|
else
|
240
289
|
{ commit: { partial: [
|
241
|
-
{ 'admin':
|
290
|
+
{ 'admin': admins },
|
242
291
|
if device_groups
|
243
292
|
device_groups.empty? ? 'no-device-group' : { 'device-group': device_groups }
|
244
293
|
end,
|
@@ -256,8 +305,9 @@ module PaloAlto
|
|
256
305
|
end
|
257
306
|
result = op.execute(cmd)
|
258
307
|
|
259
|
-
|
308
|
+
return result if raw_result
|
260
309
|
|
310
|
+
job_id = result.at_xpath('response/result/job')&.text
|
261
311
|
return result unless job_id && wait_for_completion
|
262
312
|
|
263
313
|
wait_for_job_completion(job_id, wait: wait, timeout: timeout) if job_id
|
@@ -294,6 +344,7 @@ module PaloAlto
|
|
294
344
|
|
295
345
|
# will execute block if given and unlock afterwards. returns false if lock could not be aquired
|
296
346
|
def lock(area:, comment: nil, type: nil, location: nil)
|
347
|
+
raise MalformedCommandException, 'No type specified' if location && !type
|
297
348
|
if block_given?
|
298
349
|
return false unless lock(area: area, comment: comment, type: type, location: location)
|
299
350
|
|
@@ -309,7 +360,8 @@ module PaloAlto
|
|
309
360
|
op.execute(cmd, type: type, location: location)
|
310
361
|
true
|
311
362
|
rescue PaloAlto::InternalErrorException => e
|
312
|
-
return true if e.message.start_with?('You already own a config lock for scope ')
|
363
|
+
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}"
|
313
365
|
|
314
366
|
false
|
315
367
|
end
|
@@ -337,9 +389,9 @@ module PaloAlto
|
|
337
389
|
end
|
338
390
|
end
|
339
391
|
|
340
|
-
def check_for_changes(
|
341
|
-
cmd = if
|
342
|
-
{ show: { config: { list: { 'change-summary': { partial: { admin:
|
392
|
+
def check_for_changes(admins: [username])
|
393
|
+
cmd = if admins
|
394
|
+
{ show: { config: { list: { 'change-summary': { partial: { admin: admins } } } } } }
|
343
395
|
else
|
344
396
|
{ show: { config: { list: 'change-summary' } } }
|
345
397
|
end
|
@@ -350,6 +402,42 @@ module PaloAlto
|
|
350
402
|
}
|
351
403
|
end
|
352
404
|
|
405
|
+
# returns nil if job isn't finished yet, otherwise the job result
|
406
|
+
def query_and_parse_job(job_id)
|
407
|
+
cmd = { show: { jobs: { id: job_id } } }
|
408
|
+
result = op.execute(cmd)
|
409
|
+
status = result.at_xpath('response/result/job/status')&.text
|
410
|
+
return result unless %w[ACT PEND].include?(status)
|
411
|
+
|
412
|
+
nil
|
413
|
+
rescue => e
|
414
|
+
warn [:job_query_error, e].inspect
|
415
|
+
false
|
416
|
+
end
|
417
|
+
|
418
|
+
# returns true if successful
|
419
|
+
# returns nil if not completed yet
|
420
|
+
# otherwise returns the error
|
421
|
+
def commit_successful?(commit_result)
|
422
|
+
if commit_result.at_xpath('response/msg')&.text&.start_with?('The result of this commit would be the same as the previous commit queued/processed') ||
|
423
|
+
commit_result.at_xpath('response/msg')&.text == 'There are no changes to commit.'
|
424
|
+
return true
|
425
|
+
end
|
426
|
+
|
427
|
+
job_id = commit_result.at_xpath('response/result/job')&.text
|
428
|
+
unless job_id
|
429
|
+
warn [:no_job_id, result].inspect
|
430
|
+
return false
|
431
|
+
end
|
432
|
+
|
433
|
+
job_result = query_and_parse_job(job_id)
|
434
|
+
return job_result if !job_result # can be either nil or false (errored)
|
435
|
+
|
436
|
+
return true if job_result.xpath('response/result/job/details/line').text&.include?('Configuration committed successfully')
|
437
|
+
|
438
|
+
job_result
|
439
|
+
end
|
440
|
+
|
353
441
|
def wait_for_job_completion(job_id, wait: 5, timeout: 600)
|
354
442
|
cmd = { show: { jobs: { id: job_id } } }
|
355
443
|
start = Time.now
|
@@ -406,6 +494,8 @@ module PaloAlto
|
|
406
494
|
|
407
495
|
@subclasses = {}
|
408
496
|
|
497
|
+
@cache = {}
|
498
|
+
|
409
499
|
# xpath
|
410
500
|
@expression = :root
|
411
501
|
@arguments = [Expression.new(:this_node), []]
|
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.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Roesner
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|