class PaloAlto::XML::ConfigClass

Attributes

props[RW]
api_attributes[R]
force_relative[RW]
parent_instance[RW]
subclasses[R]

Public Class Methods

new(parent_instance:, create_children: false) click to toggle source
# File lib/palo_alto/config.rb, line 385
def initialize(parent_instance:, create_children: false)
        @parent_instance = parent_instance
        @subclasses = {}
        @values = {}
        @create_children = create_children
        @external_values = {} # data we received and don't need to set again
        @api_attributes={}

        @expression = :child
        unless self.is_a?(ArrayConfigClass) # for ArrayConfigClass, it will be set externally after the constructor
                xpath_argument = @parent_instance
                @arguments = [ xpath_argument, [_section] ]
        end
end

Public Instance Methods

array_class_setter(*args, klass:, section:, &block) click to toggle source
# File lib/palo_alto/config.rb, line 588
def array_class_setter(*args, klass:, section:, &block)
        # either we have a selector or a block
        raise(ArgumentError, "wrong number of arguments (expected one argument or block)") unless (args.length==1 && !block) || (args.empty? && block)

        entry = klass.new(parent_instance: self, create_children: @create_children)
        if block
                obj = self.child(section.to_sym).where(PaloAlto.instance_eval(&block))
                entry.expression = obj.expression
                entry.arguments = obj.arguments
                entry
        else
                selector=args[0]
                @subclasses[section]||= {}

                entry.instance_variable_get('@external_values').merge!({"@#{selector.keys.first}" => selector.values.first})
                entry.selector = selector
                entry.set_xpath_from_selector

                if @create_children
                        @subclasses[section][selector] ||= entry
                else
                        entry
                end
        end
end
clear!() click to toggle source
# File lib/palo_alto/config.rb, line 433
def clear!
        @subclasses = {}
        @values = {}
        self
end
create!() click to toggle source
# File lib/palo_alto/config.rb, line 404
def create!
        @create_children = true
        self
end
delete!() click to toggle source
# File lib/palo_alto/config.rb, line 716
def delete!
        payload = {
                type:              'config',
                action: 'delete',
                xpath:     self.to_xpath
        }
        XML.execute(payload)
end
delete_child(name) click to toggle source
# File lib/palo_alto/config.rb, line 712
def delete_child(name)
        @subclasses.delete(name) && true || false
end
enforce_type(prop_arr, value, value_type: prop_arr['type']) click to toggle source
# File lib/palo_alto/config.rb, line 524
def enforce_type(prop_arr, value, value_type: prop_arr['type'])
        case value_type
                when 'bool'
                        return true if ['yes', true].include?(value)
                        return false if ['no', false].include?(value)
                        raise ArgumentError, 'Not bool: ' + value.inspect
                when 'string', 'ipdiscontmask', 'iprangespec', 'ipspec', 'rangelistspec'
                        raise(ArgumentError, 'Not string') unless value.is_a?(String)
                        if prop_arr['regex']
                                raise ArgumentError, "Not matching regex: #{value.inspect} (#{prop_arr["regex"].inspect})" unless value.match(prop_arr["regex"])
                        end
                        if prop_arr['maxlen']
                                raise ArgumentError, 'Too long' if value.length > prop_arr['maxlen'].to_i
                        end
                        return value
                when 'enum'
                        accepted_values = prop_arr.is_a?(Hash) ? prop_arr['enum'].map{|x| x['value']} : prop_arr.map{|x| x['value']} # is an array if part of value_type 'multiple'
                        return value if accepted_values.include?(value)
                        raise ArgumentError, "not allowed: #{value.inspect} (not within #{accepted_values.inspect})"
                when 'float'
                        return Float(value)
                when 'rangedint'
                        number = Integer(value)
                        return number if number >= prop_arr['min'].to_i && number <= prop_arr['max'].to_i
                        raise ArgumentError, "not in range #{prop_arr['min']}..#{prop_arr['max']}: #{number}"
                when 'multiple'
                        prop_arr['multi-types'].keys.each{|key|
                                return enforce_type(prop_arr['multi-types'][key], value, value_type: key) rescue false
                        }
                        raise(ArgumentError, "Nothing matching found for #{value.inspect} (#{prop_arr.inspect})")
        end
end
external_set(data) click to toggle source
# File lib/palo_alto/config.rb, line 493
def external_set(data)
        data.element_children.map{|child|
                child.name.match(/\A[a-zA-Z0-9_-]*\z/) or raise 'invalid character'
                if prop = self.class.props[child.name]
                        if has_multiple_values?
                                @external_values[child.name]||=[]
                                @external_values[child.name] << enforce_type(prop, child.text)
                        else
                                @external_values[child.name] = enforce_type(prop, child.text)
                        end

                elsif new_class=eval('self.class::' + child.name.capitalize.gsub(/-(.)/) {|e| $1.upcase}) rescue false # check for class name in camelcase format
                        if new_class.superclass == ConfigClass
                                subclass = self.send(child.name.gsub('-','_'))
                        elsif new_class.superclass == ArrayConfigClass
                                primary_key = get_primary_key(child.attribute_nodes, new_class.props)

                                subclass = self.send(child.name, primary_key) # create subclass

                                subclass.set_array_class_attributes(child, primary_key) # primary key, api_attributes
                        else
                                raise
                        end
                        subclass.external_set(child)
                else
                        raise "unknown key: #{child.name}"
                end
                subclass
        }
end
get(ignore_empty_result: false, xpath: self.to_xpath) click to toggle source
# File lib/palo_alto/config.rb, line 439
def get(ignore_empty_result: false, xpath: self.to_xpath)
        if self.class.superclass == ArrayConfigClass && !@selector
                raise(InvalidCommandException, "Please use 'get_all' here")
        end

        payload = {
                type:              'config',
                action: 'get',
                xpath:     xpath
        }

        data = XML.execute(payload)
        start_time=Time.now

        if data.xpath('//response/result/*').length != 1
                if ignore_empty_result==false
                        raise(ObjectNotPresentException, "empty result: #{payload.inspect}")
                end
        else
                #self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first).first
                @create_children=true
                n = data.xpath('//response/result/*')

                clear!
                external_set(n.first)

                if is_a?(ArrayConfigClass)
                        primary_key = get_primary_key(n.first.attribute_nodes, self.class.props)
                        set_array_class_attributes(n.first, primary_key) # primary key, api_attributes
                end
        end
        if XML.debug.include?(:statistics)
                puts "Elapsed for parsing: #{Time.now-start_time} seconds"
        end
        self
end
get_all() click to toggle source
# File lib/palo_alto/config.rb, line 416
def get_all
        raise(InvalidCommandException, "please use 'get' here") if self.class.superclass != ArrayConfigClass
        payload = {
                type:              "config",
                action: "get",
                xpath:     self.to_xpath
        }

        data = XML.execute(payload)
        start_time=Time.now
        result = self.parent_instance.dup.create!.clear!.external_set(data.xpath('//response/result').first)
        if XML.debug.include?(:statistics)
                puts "Elapsed for parsing #{result.length} results: #{Time.now-start_time} seconds"
        end
        result
end
get_primary_key(attribute_nodes, props) click to toggle source
# File lib/palo_alto/config.rb, line 476
def get_primary_key(attribute_nodes, props)
        primary_key_attr = attribute_nodes.find{|attr|
                props.keys.find{|k| k=="@#{attr.name}"}
        }
        Hash[primary_key_attr.name.to_sym, primary_key_attr.value]
end
inspect() click to toggle source
# File lib/palo_alto/config.rb, line 412
def inspect
        self.to_s[0..-1] + ' ' + self.values(full_tree: false).map{|k,v| "#{k}: #{v.inspect}"}.join(", ") + ">"
end
multimove!(dst:, members:, all_errors: false) click to toggle source
# File lib/palo_alto/config.rb, line 725
def multimove!(dst:, members:, all_errors: false)
        source = self.to_xpath

        builder = Nokogiri::XML::Builder.new{|xml|
                xml.root {
                        xml.send('selected-list') {
                                xml.source(xpath: source) {
                                        members.each{|member| xml.member member}
                                }
                        }
                        xml.send('all-errors', all_errors ? 'yes' : 'no')
                }
        }

        element = builder.doc.root.children.map(&:to_xml).join("\n")

        payload = {
                type:              'config',
                action: 'multi-move',
                xpath:     dst,
                element: element
        }

        XML.execute(payload)
end
prop_get(prop, include_defaults: true) click to toggle source
# File lib/palo_alto/config.rb, line 656
def prop_get(prop, include_defaults: true)
        my_prop = self.class.props[prop]
        if @values.has_key?(prop)
                return @values[prop]
        elsif @external_values.has_key?(prop) && @external_values[prop].is_a?(Array)
                return @values[prop] = @external_values[prop].dup
        elsif @external_values.has_key?(prop)
                return @external_values[prop]
        elsif my_prop.has_key?("default") && include_defaults
                return enforce_type(my_prop, my_prop['default'])
        else
                return nil
        end
end
prop_set(prop, value) click to toggle source
# File lib/palo_alto/config.rb, line 671
def prop_set(prop, value)
        my_prop = self.class.props[prop] or raise(InternalErrorException, "Unknown attribute for #{self.class}: #{prop}")

        if has_multiple_values? && value.is_a?(String)
                value = value.split(/\s+/)
        end

        if value.is_a?(Array)
                @values[prop] = value.map{|v| enforce_type(my_prop, v)}
        elsif value.nil?
                @values[prop] = nil
        else
                @values[prop] = enforce_type(my_prop, value)
        end
end
push!() click to toggle source
# File lib/palo_alto/config.rb, line 700
def push!
        xml_str = self.to_xml(changed_only: false, full_tree: true, include_root: true)

        payload = {
                type:              'config',
                action: 'edit',
                xpath:     self.to_xpath,
                element: xml_str
        }
        XML.execute(payload)
end
set_array_class_attributes(child, primary_key) click to toggle source
# File lib/palo_alto/config.rb, line 483
def set_array_class_attributes(child, primary_key)
        @external_values.merge!({ '@' + primary_key.keys.first.to_s => primary_key.values.first})

        #  save also the other attributes like loc and uuid, if set
        child.attribute_nodes.each{|attr|
                next if attr.name.to_sym == primary_key.keys.first
                api_attributes[attr.name] = attr.value
        }
end
set_values(h, external: false) click to toggle source
# File lib/palo_alto/config.rb, line 635
def set_values(h, external: false)
        if h.is_a?(PaloAlto::XML::ConfigClass)
                h=h.values(include_defaults: false)
        end
        raise(ArgumentError, 'needs to be a Hash') unless h.is_a?(Hash)
        clear!
        create!
        h.each{|k,v|
                if v.is_a?(Hash)
                        self.send(k.to_s.gsub('-','_')).set_values(v, external: external)
                else
                        if external
                                @external_values[k]=v
                        else
                                self.prop_set(k,v)
                        end
                end
        }
        self
end
to_xml(changed_only:, full_tree:, include_root: ) click to toggle source
# File lib/palo_alto/config.rb, line 687
def to_xml(changed_only:, full_tree:, include_root: )
        builder = Nokogiri::XML::Builder.new{|xml|
                xml.send(self._section, (self.selector rescue nil)) {
                        self.xml_builder(xml, changed_only: changed_only, full_tree: full_tree)
                }
        }
        if include_root
                builder.doc.root.to_xml
        else
                builder.doc.root.children.map(&:to_xml).join("\n")
        end
end
values(full_tree: true, include_defaults: true) click to toggle source
# File lib/palo_alto/config.rb, line 614
def values(full_tree: true, include_defaults: true)
        h={}
        self.class.props.keys.map{|k|
                prop = prop_get(k, include_defaults: include_defaults)
                h[k] = prop if prop
        }
        if full_tree
                @subclasses.each{|k,subclass|
                        if subclass.is_a?(Hash)
                                h[k]||={}
                                subclass.each{|k2, subclass2|
                                        h[k][k2]=subclass2.values(full_tree: true, include_defaults: include_defaults)
                                }
                        else
                                h[k]=subclass.values(full_tree: true, include_defaults: include_defaults)
                        end
                }
        end
        return h
end
xml_builder(xml, full_tree: false, changed_only: true) click to toggle source
# File lib/palo_alto/config.rb, line 557
def xml_builder(xml, full_tree: false, changed_only: true)
        keys = changed_only ? @values.keys : self.class.props.keys

        keys.map{|k|
                next if k.start_with?('@')
                v=prop_get(k, include_defaults: false)
                next unless v
                Array(v).each{|val|
                        val='yes' if val==true
                        val='no' if val==false
                        xml.method_missing(k, val) # somehow .send does not work with k==:system
                }
        }
        if full_tree
                @subclasses.each{|k,subclass|
                        if subclass.is_a?(Hash)
                                subclass.each{|k2, subclass2|
                                        xml.send(k, k2){|xml|
                                                subclass2.xml_builder(xml, full_tree: full_tree, changed_only: changed_only)
                                        }
                                }
                        else
                                xml.method_missing(k){|xml| # somehow .send does not work with k==:system
                                        subclass.xml_builder(xml, full_tree: full_tree, changed_only: changed_only)
                                }
                        end
                }
        end
        return xml
end