palo_alto 0.1.7 → 0.1.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3f5565d4ace0fcd1e96290bcd2b5e2d1ffa726b53fd7fe691497f7a2c137d42
4
- data.tar.gz: 9e8e63ab0508abf76cc5fdf13e6827067aa42100f6beb996ef16b1256dd96f5b
3
+ metadata.gz: aa30215e631895511ecb3645281eb160db2df58f0a6f6351d2d88069d80d1adc
4
+ data.tar.gz: e9acd3ba580ac92ec84546fc626ab52d9b7cfdd27591bb7b702082d3ddb1e791
5
5
  SHA512:
6
- metadata.gz: 59e3ee0f6f425554cf6dd10ac49310cdc4ebba3ec24ad9cf3914f7e5a6c465a386ccfe8cae77041691ddbc157c281c66aaee84b49f617057bcd750cd81e7c0e3
7
- data.tar.gz: ad822a803c73d950cfdf0e428c41ee47b7fc3c3dd8b3fb631cecf2cf19c4b07e31d120fe0468cf25bed0ad250b859e7ee3d6ce7467a7c9adf7254a9697d7e1fa
6
+ metadata.gz: c556ea99594a4c09967e3271a0250d9864856ef42e8f89e518fe28b4054aeffc40160f5a1c298ebf5c77937c10d25a6bb5443eef64d0c03708bf9283c5647203
7
+ data.tar.gz: cc4a72eeea889757961e407712a9067cf54ee1fad39b394d3e29e230dbc8f306e9f53df2966ed576298135b9afb8496a77e4f372dd63c4d30a493653ca5b5136
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PaloAlto
4
- VERSION = '0.1.7'
4
+ VERSION = '0.1.8'
5
5
  end
data/lib/palo_alto.rb CHANGED
@@ -71,13 +71,13 @@ module PaloAlto
71
71
 
72
72
  class SessionTimedOutException < TemporaryException
73
73
  end
74
-
74
+
75
75
  module Helpers
76
76
  class Rest
77
77
  def self.make_request(opts)
78
- options = {}
79
- options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
80
- options[:timeout] = 60
78
+ options = {}
79
+ options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
80
+ options[:timeout] = 60
81
81
 
82
82
  headers = {}
83
83
  headers['User-Agent'] = 'ruby-keystone-client'
@@ -101,22 +101,23 @@ module PaloAlto
101
101
 
102
102
  response = thread[:http].post('/api/', URI.encode_www_form(options[:payload]), options[:headers])
103
103
 
104
- if response.code == '200'
104
+ case response.code
105
+ when '200'
105
106
  return response.body
106
- elsif response.code == '400' or response.code == '403'
107
+ when '400', '403'
107
108
  begin
108
109
  data = Nokogiri::XML.parse(response.body)
109
110
  message = data.xpath('//response/response/msg').text
110
111
  code = response.code.to_i
111
- rescue
112
+ rescue StandardError
112
113
  raise ConnectionErrorException, "#{response.code} #{response.message}"
113
114
  end
114
115
  raise_error(code, message)
115
116
  else
116
117
  raise ConnectionErrorException, "#{response.code} #{response.message}"
117
118
  end
118
- nil
119
119
 
120
+ nil
120
121
  rescue Net::OpenTimeout, Errno::ECONNREFUSED => e
121
122
  raise ConnectionErrorException, e.message
122
123
  end
@@ -141,7 +142,7 @@ module PaloAlto
141
142
  when 19..20 then SuccessException
142
143
  when 22 then SessionTimedOutException
143
144
  else InternalErrorException
144
- end
145
+ end
145
146
  raise error, message
146
147
  end
147
148
 
@@ -155,36 +156,27 @@ module PaloAlto
155
156
  options[:payload] = payload
156
157
  options[:headers] = headers
157
158
 
158
- if XML.debug.include?(:sent)
159
- warn "sent: (#{Time.now}\n#{options.pretty_inspect}\n"
160
- end
159
+ warn "sent: (#{Time.now}\n#{options.pretty_inspect}\n" if XML.debug.include?(:sent)
161
160
 
162
161
  start_time = Time.now
163
162
  text = Helpers::Rest.make_request(options)
164
163
  if XML.debug.include?(:statistics)
165
- warn "Elapsed for API call #{payload[:type]}/#{payload[:action]||'(unknown action)'}: #{Time.now-start_time} seconds"
164
+ warn "Elapsed for API call #{payload[:type]}/#{payload[:action] || '(unknown action)'}: #{Time.now - start_time} seconds"
166
165
  end
167
166
 
168
- if XML.debug.include?(:received)
169
- warn "received: #{Time.now}\n#{text}\n"
170
- end
167
+ warn "received: #{Time.now}\n#{text}\n" if XML.debug.include?(:received)
171
168
 
172
169
  data = Nokogiri::XML.parse(text)
173
170
  unless data.xpath('//response/@status').to_s == 'success'
174
- if XML.debug.include?(:sent_on_error)
175
- warn "sent:\n#{options.inspect}\n"
176
- end
177
- if XML.debug.include?(:received_on_error)
178
- warn "received:\n#{text.inspect}\n"
179
- end
171
+ warn "sent:\n#{options.inspect}\n" if XML.debug.include?(:sent_on_error)
172
+ warn "received:\n#{text.inspect}\n" if XML.debug.include?(:received_on_error)
180
173
  code = data.at_xpath('//response/@code')&.value.to_i # sometimes there is no code :( e.g. for 'op' errors
181
174
  message = data.xpath('/response/msg/line').map(&:text).map(&:strip).join("\n")
182
175
  raise_error(code, message)
183
176
  end
184
177
 
185
- return data
178
+ data
186
179
  end
187
-
188
180
  end
189
181
  end
190
182
 
@@ -195,23 +187,21 @@ module PaloAlto
195
187
  def execute(payload)
196
188
  retried = false
197
189
  begin
198
- Helpers::Rest.execute(payload, headers: {'X-PAN-KEY': self.auth_key})
190
+ Helpers::Rest.execute(payload, headers: { 'X-PAN-KEY': auth_key })
199
191
  rescue TemporaryException => e
200
192
  dont_continue_at = [
201
193
  'Partial revert is not allowed. Full system commit must be completed.',
202
- 'Config for scope '
194
+ 'Config for scope ',
195
+ 'Config is not currently locked for scope ',
196
+ 'Commit lock is not currently held by'
203
197
  ]
204
- unless retried || dont_continue_at.any? { |x| e.message.start_with?(x) }
205
- if XML.debug.include?(:warnings)
206
- warn "Got error #{e.inspect}; retrying"
207
- end
198
+ if retried || dont_continue_at.any? { |x| e.message.start_with?(x) }
199
+ raise e
200
+ else
201
+ warn "Got error #{e.inspect}; retrying" if XML.debug.include?(:warnings)
208
202
  retried = true
209
- if e.is_a?(SessionTimedOutException)
210
- get_auth_key
211
- end
203
+ get_auth_key if e.is_a?(SessionTimedOutException)
212
204
  retry
213
- else
214
- raise e
215
205
  end
216
206
  end
217
207
  end
@@ -225,7 +215,7 @@ module PaloAlto
225
215
  else
226
216
  { commit: { partial: [
227
217
  { 'admin': [XML.username] },
228
- device_groups ? {'device-group': device_groups } : nil,
218
+ device_groups ? { 'device-group': device_groups } : nil,
229
219
  'no-template',
230
220
  'no-template-stack',
231
221
  'no-log-collector',
@@ -245,21 +235,21 @@ module PaloAlto
245
235
  end
246
236
 
247
237
  def full_commit_required?
248
- result = Op.new.execute({check: 'full-commit-required'})
238
+ result = Op.new.execute({ check: 'full-commit-required' })
249
239
  return true unless result.at_xpath('response/result').text == 'no'
250
240
 
251
241
  false
252
242
  end
253
243
 
254
244
  def primary_active?
255
- cmd = {show: {'high-availability': 'state'}}
245
+ cmd = { show: { 'high-availability': 'state' } }
256
246
  state = Op.new.execute(cmd)
257
- state.at_xpath("response/result/local-info/state").text == "primary-active"
247
+ state.at_xpath('response/result/local-info/state').text == 'primary-active'
258
248
  end
259
249
 
260
250
  # area: config, commit
261
251
  def show_locks(area:)
262
- cmd = {show: "#{area}-locks"}
252
+ cmd = { show: "#{area}-locks" }
263
253
  ret = Op.new.execute(cmd)
264
254
  ret.xpath("response/result/#{area}-locks/entry").map do |lock|
265
255
  comment = lock.at_xpath('comment').inner_text
@@ -273,6 +263,18 @@ module PaloAlto
273
263
  end
274
264
  end
275
265
 
266
+ def execute_with_type(cmd, type:, location:)
267
+ if type == 'tpl'
268
+ run_with_template_scope(location) { Op.new.execute(cmd) }
269
+ elsif type == 'dg'
270
+ Op.new.execute(cmd, { vsys: location })
271
+ elsif !type || type == 'shared'
272
+ Op.new.execute(cmd)
273
+ else
274
+ raise(ArgumentError, "invalid type: #{type.inspect}")
275
+ end
276
+ end
277
+
276
278
  # will execute block if given and unlock afterwards. returns false if lock could not be aquired
277
279
  def lock(area:, comment: nil, type: nil, location: nil)
278
280
  if block_given?
@@ -288,18 +290,22 @@ module PaloAlto
288
290
  end
289
291
 
290
292
  begin
291
- cmd = {request: {"#{area}-lock": {add: {comment: comment || '(null)' }}}}
292
- Op.new.execute(cmd, get_extra_argument(type: type, location: location))
293
+ cmd = { request: { "#{area}-lock": { add: { comment: comment || '(null)' } } } }
294
+ execute_with_type(cmd, type: type, location: location)
293
295
  true
294
296
  rescue PaloAlto::InternalErrorException
295
297
  false
296
298
  end
297
299
  end
298
300
 
299
- def unlock(area:, type: nil, location: nil)
301
+ def unlock(area:, type: nil, location: nil, name: nil)
300
302
  begin
301
- cmd = {request: {"#{area}-lock": 'remove'}}
302
- Op.new.execute(cmd, get_extra_argument(type: type, location: location))
303
+ cmd = if name
304
+ { request: { "#{area}-lock": { remove: { admin: name } } } }
305
+ else
306
+ { request: { "#{area}-lock": 'remove' } }
307
+ end
308
+ execute_with_type(cmd, type: type, location: location)
303
309
  rescue PaloAlto::InternalErrorException
304
310
  return false
305
311
  end
@@ -307,29 +313,48 @@ module PaloAlto
307
313
  end
308
314
 
309
315
  def remove_all_locks
310
- %w(config commit).each do |area|
311
- show_locks(area: area).each {|lock|
312
- unlock(area: area, type: lock[:type], location: lock[:location])
313
- }
316
+ %w[config commit].each do |area|
317
+ show_locks(area: area).each do |lock|
318
+ unlock(area: area, type: lock[:type], location: lock[:location], name: area=='commit' ? lock[:name] : nil )
319
+ end
314
320
  end
315
321
  end
316
322
 
323
+ def run_with_template_scope(name)
324
+ if block_given?
325
+ run_with_template_scope(name)
326
+ begin
327
+ return yield
328
+ ensure
329
+ run_with_template_scope(nil)
330
+ end
331
+ end
332
+
333
+ cmd = if name
334
+ { set: { system: { setting: { target: { template: { name: name } } } } } }
335
+ else
336
+ { set: { system: { setting: { target: 'none' } } } }
337
+ end
338
+
339
+ Op.new.execute(cmd)
340
+ end
341
+
317
342
  def check_for_changes(usernames: [XML.username])
318
- result = Op.new.execute({show: {config: {list: {'change-summary': {partial: {admin: usernames}}}}}})
343
+ result = Op.new.execute({ show: { config: { list: { 'change-summary': { partial: { admin: usernames } } } } } })
319
344
  result.xpath('response/result/summary/device-group/member').map(&:inner_text)
320
345
  end
321
346
 
322
347
  def wait_for_job_completion(job_id, wait: 5, timeout: 300)
323
- cmd = {show: {jobs: {id: job_id}}}
348
+ cmd = { show: { jobs: { id: job_id } } }
324
349
  start = Time.now
325
- begin
350
+ loop do
326
351
  result = Op.new.execute(cmd)
327
- unless result.at_xpath('response/result/job/status')&.text=='ACT'
328
- return result
329
- end
352
+ return result unless result.at_xpath('response/result/job/status')&.text == 'ACT'
353
+
330
354
  sleep wait
331
- end while start+timeout > Time.now
332
- return false
355
+ break unless start + timeout > Time.now
356
+ end
357
+ false
333
358
  end
334
359
 
335
360
  def revert!(all: false)
@@ -366,7 +391,7 @@ module PaloAlto
366
391
  @arguments = [Expression.new(:this_node), []]
367
392
 
368
393
  # attempt to obtain the auth_key
369
- #raise 'Exception attempting to obtain the auth_key' if (self.class.auth_key = get_auth_key).nil?
394
+ # raise 'Exception attempting to obtain the auth_key' if (self.class.auth_key = get_auth_key).nil?
370
395
  self.class.get_auth_key
371
396
 
372
397
  self
@@ -374,27 +399,14 @@ module PaloAlto
374
399
 
375
400
  # Perform a query to the API endpoint for an auth_key based on the credentials provided
376
401
  def self.get_auth_key
377
-
378
402
  # establish the required options for the key request
379
403
  payload = { type: 'keygen',
380
- user: self.username,
381
- password: self.password }
404
+ user: username,
405
+ password: password }
382
406
 
383
407
  # get and parse the response for the key
384
408
  xml_data = Helpers::Rest.execute(payload)
385
409
  self.auth_key = xml_data.xpath('//response/result/key')[0].content
386
410
  end
387
-
388
- private
389
-
390
- # used to limit an op command to a specifc dg/template
391
- def get_extra_argument(type:, location:)
392
- case type
393
- when 'dg' then {vsys: location}
394
- when 'tpl' then raise
395
- else {}
396
- end
397
- end
398
-
399
411
  end
400
412
  end
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.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Roesner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-22 00:00:00.000000000 Z
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri