palo_alto 0.3.0 → 0.3.1

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: b68e035e0f51de7ec25732169187fae61088d874498b3f04aa8c0729f3ff31ce
4
- data.tar.gz: f9a00e1a28101f24b8ec95f2d226bec3b6d8f25226aabfaeef9c47183de2e240
3
+ metadata.gz: f00537b8aa9a599ba43138f32b97eb01447dddcc51dcfb612739b7dd178e0c53
4
+ data.tar.gz: 70fc4d52ef2a266324e5435423e674f24e84a9421de113768bf3f2f8eab1ae67
5
5
  SHA512:
6
- metadata.gz: 23c323a9076ee9f963a25eb6582b83110a95a1bb8c5f9ee65121d7afaf3c24639f682e93d602d7730968c0eec7de7f73651702e1996b658ffc69508fe8d21b34
7
- data.tar.gz: 5fe403c5af1998c3a8696326ff46045a3990784f42ba209baa8f36a5a0f2247693e092820489d5a8c68304329ab6a149707eb62f9c2b2ba344ef1fc267d3965f
6
+ metadata.gz: 686fa339ef2c3b0fd1612774cb185d60136ce8b1da967741d20ae252e9946874e33208287d2152540dc0af8f344596a00a99aa1edbb6565b225d7b0da4218583
7
+ data.tar.gz: 1b66201dde43d65272f7f3bba5504a95b397b3943df30b08899a1bc787796e4b6585ceb058b3cc28008e339d8f4d622dfd3eaa0a58dee5e6946f58147adf4a4f
data/README.md CHANGED
@@ -1,3 +1,6 @@
1
1
  Works for me :)
2
2
 
3
+ Version 0.2.x: Panorama 10.0
4
+ Version 0.3.x: Panorama 10.1
5
+
3
6
  You can find examples on how to use this module in the examples/ directory
@@ -1,6 +1,7 @@
1
1
  require 'palo_alto'
2
2
 
3
- client = PaloAlto::XML.new(host: "panorama-test", username: "admin", password: "Admin123!", debug: [:sent, :received, :statistics])
3
+ client = PaloAlto::XML.new(host: 'panorama-test', username: 'admin', password: 'Admin123!',
4
+ debug: %i[sent received statistics])
4
5
  dg = 'PLAYGROUND'
5
6
 
6
7
  # create a tag
@@ -43,7 +44,7 @@ pp rules
43
44
  pp rules.length
44
45
 
45
46
  pp rules.first.api_attributes # attributes like uuid and loc
46
- pp rules.first.values() # values as hash
47
+ pp rules.first.values # values as hash
47
48
 
48
49
  rule = rules.first
49
50
  rule.tag.member = [new_tag.name]
@@ -59,5 +60,5 @@ pp rule.name
59
60
  exit 0
60
61
 
61
62
  # create a new template
62
- new_template = client.config.devices.entry(name:'localhost.localdomain').template.entry(name: 'testtemplate').create!
63
+ new_template = client.config.devices.entry(name: 'localhost.localdomain').template.entry(name: 'testtemplate').create!
63
64
  new_template.push!
data/examples/test_op.rb CHANGED
@@ -1,63 +1,62 @@
1
1
  require 'palo_alto'
2
2
 
3
- a= {commit: { partial:[
4
- {'admin': ['admin']},
5
- 'no-template',
6
- 'no-template-stack',
7
- 'no-log-collector',
8
- 'no-log-collector-group',
9
- 'no-wildfire-appliance',
10
- 'no-wildfire-appliance-cluster',
11
- {'device-and-network': 'excluded'},
12
- {'shared-object': 'excluded'}
13
- ]}}
14
-
15
- b= { show: {devices: 'all' } }
16
-
17
- c = {revert: { config: {
18
- partial:[
19
- {'admin': ['admin']},
20
- 'no-template',
21
- 'no-template-stack',
22
- 'no-log-collector',
23
- 'no-log-collector-group',
24
- 'no-wildfire-appliance',
25
- 'no-wildfire-appliance-cluster',
26
- {'device-and-network': 'excluded'},
27
- {'shared-object': 'excluded'}
28
- ]}}}
29
-
30
- d = {commit: nil}
3
+ a = { commit: { partial: [
4
+ { admin: ['admin'] },
5
+ 'no-template',
6
+ 'no-template-stack',
7
+ 'no-log-collector',
8
+ 'no-log-collector-group',
9
+ 'no-wildfire-appliance',
10
+ 'no-wildfire-appliance-cluster',
11
+ { 'device-and-network': 'excluded' },
12
+ { 'shared-object': 'excluded' }
13
+ ] } }
14
+
15
+ b = { show: { devices: 'all' } }
16
+
17
+ c = { revert: { config: {
18
+ partial: [
19
+ { admin: ['admin'] },
20
+ 'no-template',
21
+ 'no-template-stack',
22
+ 'no-log-collector',
23
+ 'no-log-collector-group',
24
+ 'no-wildfire-appliance',
25
+ 'no-wildfire-appliance-cluster',
26
+ { 'device-and-network': 'excluded' },
27
+ { 'shared-object': 'excluded' }
28
+ ]
29
+ } } }
30
+
31
+ d = { commit: nil }
31
32
 
32
33
  e = 'commit'
33
34
 
34
- f = {revert: 'config'}
35
+ f = { revert: 'config' }
35
36
 
36
- g= {show: 'templates'}
37
+ g = { show: 'templates' }
37
38
 
38
- h= {show: 'devicegroups'}
39
+ h = { show: 'devicegroups' }
39
40
 
40
- j={show: {jobs: {id: 12431}}}
41
+ j = { show: { jobs: { id: 12_431 } } }
41
42
 
42
- k={check: 'full-commit-required'}
43
+ k = { check: 'full-commit-required' }
43
44
 
44
- push_to_device={ 'commit-all': { 'shared-policy': { 'device-group': [{name:'TEST-DG'}]}}}
45
+ push_to_device = { 'commit-all': { 'shared-policy': { 'device-group': [{ name: 'TEST-DG' }] } } }
45
46
 
46
- #validate:
47
- p={ 'commit-all':
48
- {
49
- 'shared-policy': [
50
- {'device-group': [{name:'PLAYGROUND'}]},
51
- {'include-template':'yes'},
52
- {'merge-with-candidate-cfg':'yes'},
53
- {'force-template-values':'no'},
54
- {'validate-only':'yes'}
55
- ]
56
- }
57
- }
58
-
59
- i = {show: {query: {result: {id: 10438 }}}}
47
+ # validate:
48
+ p = { 'commit-all':
49
+ {
50
+ 'shared-policy': [
51
+ { 'device-group': [{ name: 'PLAYGROUND' }] },
52
+ { 'include-template': 'yes' },
53
+ { 'merge-with-candidate-cfg': 'yes' },
54
+ { 'force-template-values': 'no' },
55
+ { 'validate-only': 'yes' }
56
+ ]
57
+ } }
60
58
 
59
+ i = { show: { query: { result: { id: 10_438 } } } }
61
60
 
62
61
  # hit counts:
63
62
  device_group = 'PLAYGROUND'
@@ -69,11 +68,11 @@ l = {
69
68
  entry: [{
70
69
  name: device_group
71
70
  }, {
72
- "pre-rulebase": [{
71
+ 'pre-rulebase': [{
73
72
  entry: [{
74
73
  name: 'security'
75
74
  }, {
76
- 'rules': 'all'
75
+ rules: 'all'
77
76
  }]
78
77
  }]
79
78
  }]
@@ -83,7 +82,7 @@ l = {
83
82
  }
84
83
 
85
84
  # hit count for one rule, with more details:
86
- rule_name = "Rule 27"
85
+ rule_name = 'Rule 27'
87
86
  l = {
88
87
  show: {
89
88
  'rule-hit-count': [{
@@ -91,12 +90,12 @@ l = {
91
90
  entry: [{
92
91
  name: device_group
93
92
  }, {
94
- "pre-rulebase": [{
93
+ 'pre-rulebase': [{
95
94
  entry: [{
96
95
  name: 'security'
97
96
  }, {
98
- 'rules': {
99
- "rule-name": [{
97
+ rules: {
98
+ 'rule-name': [{
100
99
  entry: [{
101
100
  name: rule_name
102
101
  }]
@@ -110,18 +109,16 @@ l = {
110
109
  }
111
110
  }
112
111
 
112
+ client = PaloAlto::XML.new(host: 'panorama-test', username: 'admin', password: 'Admin123!', debug: %i[sent received])
113
113
 
114
- client = PaloAlto::XML.new(host: "panorama-test", username: "admin", password: "Admin123!", debug: [:sent, :received])
115
-
116
- #pp client.op.execute(a)
117
- #pp client.op.execute(b)
118
- #pp client.op.execute(c)
114
+ # pp client.op.execute(a)
115
+ # pp client.op.execute(b)
116
+ # pp client.op.execute(c)
119
117
  pp client.op.execute(d)
120
- puts "---------------------------"
118
+ puts '---------------------------'
121
119
  pp client.op.execute(e)
122
- puts "---------------------------"
120
+ puts '---------------------------'
123
121
 
124
- #pp client.op.execute(f)
122
+ # pp client.op.execute(f)
125
123
 
126
124
  pp client.op.execute(k)
127
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # generated: 2022-08-09 10:45:48 +0200
2
+ # generated: 2022-09-09 13:55:04 +0200
3
3
  # rubocop:disable Style/FrozenStringLiteralComment
4
4
  require 'openssl'
5
5
  require 'nokogiri'
@@ -347,12 +347,12 @@ end
347
347
  def inspect
348
348
  to_s[0...-1] + ' ' + values(full_tree: false).map { |k, v| "#{k}: #{v.inspect}" }.join(', ') + '>'
349
349
  end
350
- def get_all
350
+ def get_all(xpath: to_xpath)
351
351
  raise(InvalidCommandException, "please use 'get' here") if self.class.superclass != ArrayConfigClass
352
352
  payload = {
353
353
  type: 'config',
354
354
  action: 'get',
355
- xpath: to_xpath
355
+ xpath: xpath
356
356
  }
357
357
  data = @client.execute(payload)
358
358
  start_time = Time.now
@@ -414,8 +414,10 @@ api_attributes[attr.name] = attr.value
414
414
  end
415
415
  end
416
416
  def get_class_from_child_str(child)
417
- # check for class name in camelcase format
418
- self.class.const_get(child.name.capitalize.gsub(/-(.)/) { Regexp.last_match(1).upcase })
417
+ str = child.name.dup
418
+ str[0] = str[0].upcase
419
+ name = str.gsub(/-(.)/) { |_e| Regexp.last_match(1).upcase }
420
+ self.class.const_get(name)
419
421
  rescue NameError
420
422
  raise "Child not found for #{self.class.to_s}: #{child.name}"
421
423
  end
@@ -425,9 +427,9 @@ child.name.match(/\A[a-zA-Z0-9_-]*\z/) or raise 'invalid character'
425
427
  if (prop = self.class.props[child.name])
426
428
  if has_multiple_values?
427
429
  @external_values[child.name] ||= []
428
- @external_values[child.name] << enforce_type(prop, child.text)
430
+ @external_values[child.name] << enforce_type(prop, child.text, skip_validation: true)
429
431
  else
430
- @external_values[child.name] = enforce_type(prop, child.text)
432
+ @external_values[child.name] = enforce_type(prop, child.text, skip_validation: true)
431
433
  end
432
434
  elsif (new_class = get_class_from_child_str(child))
433
435
  if new_class.superclass == ConfigClass
@@ -446,7 +448,7 @@ end
446
448
  subclass
447
449
  end
448
450
  end
449
- def enforce_type(prop_arr, value, value_type: prop_arr['type'])
451
+ def enforce_type(prop_arr, value, value_type: prop_arr['type'], skip_validation: false)
450
452
  case value_type
451
453
  when 'bool'
452
454
  return true if ['yes', true].include?(value)
@@ -456,10 +458,10 @@ when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec'
456
458
  raise(ArgumentError, "Not string: #{value.inspect}") unless value.is_a?(String)
457
459
  if prop_arr['regex'] && !value.match(prop_arr['regex']) && !value.match(prop_arr['regex'])
458
460
  raise ArgumentError,
459
- "Not matching regex: #{value.inspect} (#{prop_arr['regex'].inspect})"
461
+ "Not matching regex: #{value.inspect} (#{prop_arr['regex'].inspect})" unless skip_validation
460
462
  end
461
463
  if prop_arr['maxlen'] && (value.length > prop_arr['maxlen'].to_i)
462
- raise(ArgumentError, "Too long, max. #{prop_arr['maxlen'].to_i} characters allowed")
464
+ raise(ArgumentError, "Too long, max. #{prop_arr['maxlen'].to_i} characters allowed") unless skip_validation
463
465
  end
464
466
  value
465
467
  when 'enum'
@@ -485,8 +487,8 @@ end
485
487
  raise(ArgumentError, "Nothing matching found for #{value.inspect} (#{prop_arr.inspect})")
486
488
  end
487
489
  end
488
- def xml_builder(xml, full_tree: false, changed_only: true)
489
- keys = changed_only ? @values.keys : self.class.props.keys
490
+ def xml_builder(xml, full_tree: false)
491
+ keys = self.class.props.keys
490
492
  keys.map do |k|
491
493
  next if k.start_with?('@')
492
494
  v = prop_get(k, include_defaults: false)
@@ -502,12 +504,13 @@ if full_tree
502
504
  if subclass.is_a?(Hash)
503
505
  subclass.each do |k2, subclass2|
504
506
  xml.public_send(k, k2) do |xml2|
505
- subclass2.xml_builder(xml2, full_tree: full_tree, changed_only: changed_only)
507
+ subclass2.xml_builder(xml2, full_tree: full_tree)
506
508
  end
507
509
  end
508
510
  else
511
+ k = 'method_' if k=='method'
509
512
  xml.public_send(k) do |xml2|
510
- subclass.xml_builder(xml2, full_tree: full_tree, changed_only: changed_only)
513
+ subclass.xml_builder(xml2, full_tree: full_tree)
511
514
  end
512
515
  end
513
516
  end
@@ -595,7 +598,7 @@ elsif @external_values.key?(prop) && @external_values[prop].is_a?(Array)
595
598
  @values[prop] = @external_values[prop].dup
596
599
  elsif @external_values.key?(prop)
597
600
  @external_values[prop]
598
- elsif my_prop.key?('default') && include_defaults
601
+ elsif my_prop.key?('default') && ( my_prop['optional'] != 'yes' || include_defaults )
599
602
  enforce_types(my_prop, my_prop['default'])
600
603
  end
601
604
  end
@@ -615,14 +618,14 @@ my_prop = self.class.props[prop] or raise(InternalErrorException,
615
618
  "Unknown attribute for #{self.class}: #{prop}")
616
619
  @values[prop] = enforce_types(my_prop, value)
617
620
  end
618
- def to_xml(changed_only:, full_tree:, include_root:)
621
+ def to_xml(full_tree:, include_root:)
619
622
  builder = Nokogiri::XML::Builder.new do |xml|
620
623
  xml.public_send(_section, begin
621
624
  selector
622
625
  rescue StandardError
623
626
  nil
624
627
  end) do
625
- xml_builder(xml, changed_only: changed_only, full_tree: full_tree)
628
+ xml_builder(xml, full_tree: full_tree)
626
629
  end
627
630
  end
628
631
  if include_root
@@ -632,7 +635,7 @@ builder.doc.root.children.map(&:to_xml).join("\n")
632
635
  end
633
636
  end
634
637
  def push!
635
- xml_str = to_xml(changed_only: false, full_tree: true, include_root: true)
638
+ xml_str = to_xml(full_tree: true, include_root: true)
636
639
  payload = {
637
640
  type: 'config',
638
641
  action: 'edit',
@@ -695,7 +698,9 @@ obj = xpath.where(PaloAlto.xpath_attr(k.to_sym) == v)
695
698
  end
696
699
  def rename!(new_name, internal_only: false)
697
700
  # https://docs.paloaltonetworks.com/pan-os/10-1/pan-os-panorama-api/pan-os-xml-api-request-types/configuration-api/rename-configuration.html
698
- unless internal_only
701
+ result = if internal_only
702
+ true
703
+ else
699
704
  payload = {
700
705
  type: 'config',
701
706
  action: 'rename',
@@ -708,6 +713,7 @@ end
708
713
  selector.transform_values! { new_name }
709
714
  @external_values["@#{selector.keys.first}"] = new_name
710
715
  set_xpath_from_selector!
716
+ result
711
717
  end
712
718
  end
713
719
  # no end of class Xml here as generated source will be added here
@@ -27097,7 +27103,15 @@ end # class Global
27097
27103
  def global
27098
27104
  @subclasses['global'] ||= Global.new(parent_instance: self, client: @client, create_children: @create_children)
27099
27105
  end
27100
- @props = {'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
27106
+ @props = {'validate'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'string', 'help-string'=>'Validate'}, 'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
27107
+ # Validate
27108
+ def validate
27109
+ prop_get('validate')
27110
+ end
27111
+ # Validate
27112
+ def validate=(val)
27113
+ prop_set('validate', val)
27114
+ end
27101
27115
  def dashboard
27102
27116
  prop_get('dashboard')
27103
27117
  end
@@ -29481,7 +29495,15 @@ end # class Global
29481
29495
  def global
29482
29496
  @subclasses['global'] ||= Global.new(parent_instance: self, client: @client, create_children: @create_children)
29483
29497
  end
29484
- @props = {'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
29498
+ @props = {'validate'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'string', 'help-string'=>'Validate'}, 'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
29499
+ # Validate
29500
+ def validate
29501
+ prop_get('validate')
29502
+ end
29503
+ # Validate
29504
+ def validate=(val)
29505
+ prop_set('validate', val)
29506
+ end
29485
29507
  def dashboard
29486
29508
  prop_get('dashboard')
29487
29509
  end
@@ -85332,7 +85354,15 @@ end # class Global
85332
85354
  def global
85333
85355
  @subclasses['global'] ||= Global.new(parent_instance: self, client: @client, create_children: @create_children)
85334
85356
  end
85335
- @props = {'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
85357
+ @props = {'validate'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'string', 'help-string'=>'Validate'}, 'dashboard'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'acc'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'menu-tree-path'=>'ACC', 'prune-on-sdb'=>'cfg.vm-license-type=vm50l,cfg.platform.model=PA-410', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}, 'tasks'=>{'node-type'=>'element', 'optional'=>'yes', 'type'=>'enum', 'enum'=>[{'value'=>'enable'}, {'value'=>'disable'}]}}
85358
+ # Validate
85359
+ def validate
85360
+ prop_get('validate')
85361
+ end
85362
+ # Validate
85363
+ def validate=(val)
85364
+ prop_set('validate', val)
85365
+ end
85336
85366
  def dashboard
85337
85367
  prop_get('dashboard')
85338
85368
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PaloAlto
4
- VERSION = '0.3.0'
4
+ VERSION = '0.3.1'
5
5
  end
data/lib/palo_alto.rb CHANGED
@@ -74,7 +74,6 @@ module PaloAlto
74
74
 
75
75
  module Helpers
76
76
  class Rest
77
- @http_clients = {} # will include [http_client, lock]
78
77
  @global_lock = Mutex.new
79
78
 
80
79
  def self.make_request(opts)
@@ -91,16 +90,18 @@ module PaloAlto
91
90
  options = options.merge(opts)
92
91
  options[:headers].merge!(headers)
93
92
 
94
- http_client = lock = nil
93
+ http_client = nil
95
94
 
96
95
  @global_lock.synchronize do
97
- unless (http_client, lock = @http_clients[options[:host]])
96
+ Thread.current[:http_clients] ||= {}
97
+
98
+ unless (http_client = Thread.current[:http_clients][options[:host]])
98
99
  http_client = Net::HTTP.new(options[:host], 443)
99
100
  http_client.use_ssl = true
100
101
  http_client.verify_mode = options[:verify_ssl]
101
102
  http_client.read_timeout = http_client.open_timeout = (options[:timeout] || 60)
102
103
  http_client.set_debug_output(options[:debug].include?(:http) ? $stdout : nil)
103
- @http_clients[options[:host]] = [http_client, (lock = Mutex.new)]
104
+ Thread.current[:http_clients][options[:host]] = http_client
104
105
  end
105
106
  end
106
107
 
@@ -114,11 +115,9 @@ module PaloAlto
114
115
  post_req.set_form_data(payload)
115
116
  end
116
117
 
117
- response = lock.synchronize do
118
- http_client.start unless http_client.started?
118
+ http_client.start unless http_client.started?
119
119
 
120
- http_client.request(post_req)
121
- end
120
+ response = http_client.request(post_req)
122
121
 
123
122
  case response.code
124
123
  when '200'
@@ -168,9 +167,17 @@ module PaloAlto
168
167
  end
169
168
 
170
169
  class XML
171
- attr_accessor :host, :username, :password, :auth_key, :verify_ssl, :debug, :timeout
170
+ attr_accessor :host, :username, :auth_key, :verify_ssl, :debug, :timeout
171
+
172
+ def pretty_print_instance_variables
173
+ super - [:@password, :@subclasses, :@subclasses, :@expression, :@arguments]
174
+ end
175
+
176
+ def execute(payload, skip_authentication: false)
177
+ if !auth_key && !skip_authentication
178
+ get_auth_key
179
+ end
172
180
 
173
- def execute(payload)
174
181
  retried = false
175
182
  begin
176
183
  # configure options for the request
@@ -191,14 +198,14 @@ module PaloAlto
191
198
  start_time = Time.now
192
199
  text = Helpers::Rest.make_request(options)
193
200
  if debug.include?(:statistics)
194
- warn "Elapsed for API call #{payload[:type]}/#{payload[:action] || '(unknown action)'}: #{Time.now - start_time} seconds"
201
+ warn "Elapsed for API call #{payload[:type]}/#{payload[:action] || '(unknown action)'} on #{host}: #{Time.now - start_time} seconds"
195
202
  end
196
203
 
197
204
  warn "received: #{Time.now}\n#{text}\n" if debug.include?(:received)
198
205
 
199
206
  data = Nokogiri::XML.parse(text)
200
207
  unless data.xpath('//response/@status').to_s == 'success'
201
- warn 'command failed'
208
+ warn "command failed on host #{host} at #{Time.now}"
202
209
  warn "sent:\n#{options.inspect}\n" if debug.include?(:sent_on_error)
203
210
  warn "received:\n#{text.inspect}\n" if debug.include?(:received_on_error)
204
211
  code = data.at_xpath('//response/@code')&.value.to_i # sometimes there is no code :( e.g. for 'op' errors
@@ -232,8 +239,12 @@ module PaloAlto
232
239
  else
233
240
  { commit: { partial: [
234
241
  { 'admin': [username] },
235
- device_groups ? ( device_groups.empty? ? 'no-device-group' : { 'device-group': device_groups } ) : nil,
236
- templates ? ( templates.empty? ? 'no-template' : { 'template': templates } ) : nil,
242
+ if device_groups
243
+ device_groups.empty? ? 'no-device-group' : { 'device-group': device_groups }
244
+ end,
245
+ if templates
246
+ templates.empty? ? 'no-template' : { 'template': templates }
247
+ end,
237
248
  'no-template-stack',
238
249
  'no-log-collector',
239
250
  'no-log-collector-group',
@@ -339,14 +350,17 @@ module PaloAlto
339
350
  }
340
351
  end
341
352
 
342
- def wait_for_job_completion(job_id, wait: 5, timeout: 480)
353
+ def wait_for_job_completion(job_id, wait: 5, timeout: 600)
343
354
  cmd = { show: { jobs: { id: job_id } } }
344
355
  start = Time.now
345
356
  loop do
346
- result = op.execute(cmd)
347
- status = result.at_xpath('response/result/job/status')&.text
348
- return result unless %w[ACT PEND].include?(status)
349
-
357
+ begin
358
+ result = op.execute(cmd)
359
+ status = result.at_xpath('response/result/job/status')&.text
360
+ return result unless %w[ACT PEND].include?(status)
361
+ rescue => e
362
+ warn [:job_query_error, e].inspect
363
+ end
350
364
  sleep wait
351
365
  break unless start + timeout > Time.now
352
366
  end
@@ -384,20 +398,17 @@ module PaloAlto
384
398
  end
385
399
 
386
400
  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
401
+ @host = host
402
+ @username = username
403
+ @password = password
404
+ @verify_ssl = verify_ssl
405
+ @debug = debug
392
406
 
393
407
  @subclasses = {}
394
408
 
395
409
  # xpath
396
410
  @expression = :root
397
411
  @arguments = [Expression.new(:this_node), []]
398
-
399
- # attempt to obtain the auth_key
400
- get_auth_key
401
412
  end
402
413
 
403
414
  # Perform a query to the API endpoint for an auth_key based on the credentials provided
@@ -405,10 +416,10 @@ module PaloAlto
405
416
  # establish the required options for the key request
406
417
  payload = { type: 'keygen',
407
418
  user: username,
408
- password: password }
419
+ password: @password }
409
420
 
410
421
  # get and parse the response for the key
411
- xml_data = execute(payload)
422
+ xml_data = execute(payload, skip_authentication: true)
412
423
  self.auth_key = xml_data.xpath('//response/result/key')[0].content
413
424
  end
414
425
  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.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Roesner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-09 00:00:00.000000000 Z
11
+ date: 2022-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri