palo_alto 0.2.6 → 0.3.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/CHANGELOG.md +1 -0
- data/console.rb +0 -1
- data/examples/connecttest.rb +1 -1
- data/examples/test_config.rb +1 -1
- data/examples/test_log.rb +1 -1
- data/examples/test_op.rb +1 -1
- data/lib/palo_alto/config.rb +23914 -35590
- data/lib/palo_alto/version.rb +1 -1
- data/lib/palo_alto.rb +69 -50
- metadata +2 -2
data/lib/palo_alto/version.rb
CHANGED
data/lib/palo_alto.rb
CHANGED
@@ -74,30 +74,35 @@ module PaloAlto
|
|
74
74
|
|
75
75
|
module Helpers
|
76
76
|
class Rest
|
77
|
+
@http_clients = {} # will include [http_client, lock]
|
78
|
+
@global_lock = Mutex.new
|
79
|
+
|
77
80
|
def self.make_request(opts)
|
78
81
|
options = {}
|
79
|
-
options[:verify_ssl]
|
80
|
-
options[:timeout] = 60
|
82
|
+
options[:verify_ssl] ||= OpenSSL::SSL::VERIFY_PEER
|
81
83
|
|
82
|
-
headers
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
headers = {
|
85
|
+
'User-Agent': 'ruby-keystone-client',
|
86
|
+
'Accept': 'application/xml',
|
87
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
88
|
+
}
|
86
89
|
|
87
90
|
# merge in settings from method caller
|
88
91
|
options = options.merge(opts)
|
89
92
|
options[:headers].merge!(headers)
|
90
93
|
|
91
|
-
|
92
|
-
unless thread[:http]
|
93
|
-
thread[:http] = Net::HTTP.new(options[:host], options[:port])
|
94
|
-
thread[:http].use_ssl = true
|
95
|
-
thread[:http].verify_mode = options[:verify_ssl]
|
96
|
-
thread[:http].read_timeout = thread[:http].open_timeout = options[:timeout]
|
97
|
-
thread[:http].set_debug_output($stdout) if options[:debug].include?(:http)
|
98
|
-
end
|
94
|
+
http_client = lock = nil
|
99
95
|
|
100
|
-
|
96
|
+
@global_lock.synchronize do
|
97
|
+
unless (http_client, lock = @http_clients[options[:host]])
|
98
|
+
http_client = Net::HTTP.new(options[:host], 443)
|
99
|
+
http_client.use_ssl = true
|
100
|
+
http_client.verify_mode = options[:verify_ssl]
|
101
|
+
http_client.read_timeout = http_client.open_timeout = (options[:timeout] || 60)
|
102
|
+
http_client.set_debug_output(options[:debug].include?(:http) ? $stdout : nil)
|
103
|
+
@http_clients[options[:host]] = [http_client, (lock = Mutex.new)]
|
104
|
+
end
|
105
|
+
end
|
101
106
|
|
102
107
|
payload = options[:payload]
|
103
108
|
post_req = Net::HTTP::Post.new('/api/', options[:headers])
|
@@ -109,7 +114,11 @@ module PaloAlto
|
|
109
114
|
post_req.set_form_data(payload)
|
110
115
|
end
|
111
116
|
|
112
|
-
response =
|
117
|
+
response = lock.synchronize do
|
118
|
+
http_client.start unless http_client.started?
|
119
|
+
|
120
|
+
http_client.request(post_req)
|
121
|
+
end
|
113
122
|
|
114
123
|
case response.code
|
115
124
|
when '200'
|
@@ -159,7 +168,7 @@ module PaloAlto
|
|
159
168
|
end
|
160
169
|
|
161
170
|
class XML
|
162
|
-
attr_accessor :host, :
|
171
|
+
attr_accessor :host, :username, :password, :auth_key, :verify_ssl, :debug, :timeout
|
163
172
|
|
164
173
|
def execute(payload)
|
165
174
|
retried = false
|
@@ -167,10 +176,10 @@ module PaloAlto
|
|
167
176
|
# configure options for the request
|
168
177
|
options = {}
|
169
178
|
options[:host] = host
|
170
|
-
options[:port] = port
|
171
179
|
options[:verify_ssl] = verify_ssl
|
172
180
|
options[:payload] = payload
|
173
181
|
options[:debug] = debug
|
182
|
+
options[:timeout] = timeout || 180
|
174
183
|
options[:headers] = if payload[:type] == 'keygen'
|
175
184
|
{}
|
176
185
|
else
|
@@ -189,6 +198,7 @@ module PaloAlto
|
|
189
198
|
|
190
199
|
data = Nokogiri::XML.parse(text)
|
191
200
|
unless data.xpath('//response/@status').to_s == 'success'
|
201
|
+
warn 'command failed'
|
192
202
|
warn "sent:\n#{options.inspect}\n" if debug.include?(:sent_on_error)
|
193
203
|
warn "received:\n#{text.inspect}\n" if debug.include?(:received_on_error)
|
194
204
|
code = data.at_xpath('//response/@code')&.value.to_i # sometimes there is no code :( e.g. for 'op' errors
|
@@ -197,7 +207,6 @@ module PaloAlto
|
|
197
207
|
end
|
198
208
|
|
199
209
|
data
|
200
|
-
|
201
210
|
rescue TemporaryException => e
|
202
211
|
dont_retry_at = [
|
203
212
|
'Partial revert is not allowed. Full system commit must be completed.',
|
@@ -206,27 +215,25 @@ module PaloAlto
|
|
206
215
|
'Commit lock is not currently held by',
|
207
216
|
'You already own a config lock for scope '
|
208
217
|
]
|
209
|
-
if retried || dont_retry_at.any? { |x| e.message.start_with?(x) }
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
retry
|
216
|
-
end
|
218
|
+
raise e if retried || dont_retry_at.any? { |x| e.message.start_with?(x) }
|
219
|
+
|
220
|
+
warn "Got error #{e.inspect}; retrying" if debug.include?(:warnings)
|
221
|
+
retried = true
|
222
|
+
get_auth_key if e.is_a?(SessionTimedOutException)
|
223
|
+
retry
|
217
224
|
end
|
218
225
|
end
|
219
226
|
|
220
|
-
def commit!(all: false, device_groups: nil, wait_for_completion: true)
|
221
|
-
return nil if device_groups.is_a?(Array) && device_groups.empty?
|
227
|
+
def commit!(all: false, device_groups: nil, templates: nil, wait_for_completion: true, wait: 5, timeout: 480)
|
228
|
+
return nil if device_groups.is_a?(Array) && device_groups.empty? && templates.is_a?(Array) && templates.empty?
|
222
229
|
|
223
230
|
cmd = if all
|
224
231
|
'commit'
|
225
232
|
else
|
226
233
|
{ commit: { partial: [
|
227
234
|
{ 'admin': [username] },
|
228
|
-
device_groups ? { 'device-group': device_groups } : nil,
|
229
|
-
'no-template',
|
235
|
+
device_groups ? ( device_groups.empty? ? 'no-device-group' : { 'device-group': device_groups } ) : nil,
|
236
|
+
templates ? ( templates.empty? ? 'no-template' : { 'template': templates } ) : nil,
|
230
237
|
'no-template-stack',
|
231
238
|
'no-log-collector',
|
232
239
|
'no-log-collector-group',
|
@@ -236,12 +243,13 @@ module PaloAlto
|
|
236
243
|
{ 'shared-object': 'excluded' }
|
237
244
|
].compact } }
|
238
245
|
end
|
239
|
-
op.execute(cmd)
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
246
|
+
result = op.execute(cmd)
|
247
|
+
|
248
|
+
job_id = result.at_xpath('response/result/job')&.text
|
249
|
+
|
250
|
+
return result unless job_id && wait_for_completion
|
251
|
+
|
252
|
+
wait_for_job_completion(job_id, wait: wait, timeout: timeout) if job_id
|
245
253
|
end
|
246
254
|
|
247
255
|
def full_commit_required?
|
@@ -289,7 +297,9 @@ module PaloAlto
|
|
289
297
|
cmd = { request: { "#{area}-lock": { add: { comment: comment || '(null)' } } } }
|
290
298
|
op.execute(cmd, type: type, location: location)
|
291
299
|
true
|
292
|
-
rescue PaloAlto::InternalErrorException
|
300
|
+
rescue PaloAlto::InternalErrorException => e
|
301
|
+
return true if e.message.start_with?('You already own a config lock for scope ')
|
302
|
+
|
293
303
|
false
|
294
304
|
end
|
295
305
|
end
|
@@ -329,7 +339,7 @@ module PaloAlto
|
|
329
339
|
}
|
330
340
|
end
|
331
341
|
|
332
|
-
def wait_for_job_completion(job_id, wait: 5, timeout:
|
342
|
+
def wait_for_job_completion(job_id, wait: 5, timeout: 480)
|
333
343
|
cmd = { show: { jobs: { id: job_id } } }
|
334
344
|
start = Time.now
|
335
345
|
loop do
|
@@ -343,7 +353,8 @@ module PaloAlto
|
|
343
353
|
false
|
344
354
|
end
|
345
355
|
|
346
|
-
|
356
|
+
# wait: how long revert is retried (every 10 seconds)
|
357
|
+
def revert!(all: false, wait: 60)
|
347
358
|
cmd = if all
|
348
359
|
{ revert: 'config' }
|
349
360
|
else
|
@@ -359,16 +370,25 @@ module PaloAlto
|
|
359
370
|
{ 'shared-object': 'excluded' }
|
360
371
|
] } } }
|
361
372
|
end
|
362
|
-
|
373
|
+
|
374
|
+
waited = 0
|
375
|
+
begin
|
376
|
+
op.execute(cmd)
|
377
|
+
rescue StandardError => e
|
378
|
+
puts 'Revert failed; waiting and retrying'
|
379
|
+
sleep 10
|
380
|
+
waited += 1
|
381
|
+
retry while waited < wait
|
382
|
+
raise e
|
383
|
+
end
|
363
384
|
end
|
364
385
|
|
365
|
-
def initialize(host:,
|
366
|
-
self.host
|
367
|
-
self.
|
368
|
-
self.
|
369
|
-
self.
|
370
|
-
self.
|
371
|
-
self.debug = debug
|
386
|
+
def initialize(host:, username:, password:, verify_ssl: OpenSSL::SSL::VERIFY_NONE, debug: [])
|
387
|
+
self.host = host
|
388
|
+
self.username = username
|
389
|
+
self.password = password
|
390
|
+
self.verify_ssl = verify_ssl
|
391
|
+
self.debug = debug
|
372
392
|
|
373
393
|
@subclasses = {}
|
374
394
|
|
@@ -377,8 +397,7 @@ module PaloAlto
|
|
377
397
|
@arguments = [Expression.new(:this_node), []]
|
378
398
|
|
379
399
|
# attempt to obtain the auth_key
|
380
|
-
|
381
|
-
self.get_auth_key
|
400
|
+
get_auth_key
|
382
401
|
end
|
383
402
|
|
384
403
|
# Perform a query to the API endpoint for an auth_key based on the credentials provided
|
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.3.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:
|
11
|
+
date: 2022-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|