text:
require 'spqr/spqr'
require 'rhubarb/rhubarb'
require 'mrg/grid/config-proxies'
require 'mrg/grid/util/quiescent'
require 'socket'

require 'mrg/grid/config/auth'

module Mrg
  module Grid
    module Config
      module QmfV1Kludges
        MAX_ARG_SIZE = 65535          # QMFv1 maximum argument size
        MAX_SIZE_CUSHION = (4096 * 4) # 4 pages is pretty arbitrary
        OBJECT_OVERHEAD = 32
      end

      class NodeUpdatedNotice
         include ::SPQR::Raiseable
         arg :nodes, :map, "A map whose keys are the node names that must update."
         arg :version, :uint64, "The version of the latest configuration for these nodes."
         qmf_class_name :NodeUpdatedNotice
         qmf_package_name "com.redhat.grid.config"
         qmf_severity :notice
      end

      class Store
        include ::SPQR::Manageable
        include QmfV1Kludges
        include ::Mrg::Grid::Util::Quiescent
        include ::Mrg::Grid::Config::Auth::ORIZING

        ENABLE_DEFAULT_GROUP = true
        quiescent :ENABLE_SKELETON_GROUP do
          result = ENV["WALLABY_ENABLE_SKELETON_GROUP"] && ENV["WALLABY_ENABLE_SKELETON_GROUP"] =~ /^[yt1]/i
          log.info "#{result ? "" : "not "}enabling the skeleton group; WALLABY_ENABLE_SKELETON_GROUP == '#{ENV['WALLABY_ENABLE_SKELETON_GROUP'].inspect}'"
          result
        end

        qmf_package_name 'com.redhat.grid.config'
        qmf_class_name 'Store'

        def Store.find_by_id(u)
          @singleton ||= Store.new
        end

        def Store.find_all
          @singleton ||= Store.new
          [@singleton]
        end

        ### Property method declarations
        # property APIVersionNumber uint32 The version of the API the store supports
        qmf_property :apiVersionNumber, :uint32, :desc=>"The version of the API the store supports", :index=>false
        def apiVersionNumber
          20101031
        end

        ### Property method declarations
        # property APIVersionNumber uint32 The version of the API the store supports
        qmf_property :apiMinorNumber, :uint32, :desc=>"The minor version (revision) of the API the store supports", :index=>false
        def apiMinorNumber
          6
        end

        qmf_property :host_and_pid, :list, :desc=>"A tuple consisting of the hostname and process ID, identifying where this wallaby agent is currently running.  (Introduced in 20101031.1)", :index=>false
        def host_and_pid
          [Socket.gethostname, Process.pid]
        end

        qmf_property :wallaby_version, :sstr, :desc=>"A string representation of the version of this wallaby agent.  (Introduced in API version 20101031.1)"
        def wallaby_version
          ::Mrg::Grid::Config::Version.as_string
        end

        ### Schema method declarations

        # sets Wallaby privileges for a given user
        def set_user_privs(username, privs, options=nil)
          options ||= {}

          # note that we authorize here to allow use of WALLABY_SECRET
          authorize_now(:ADMIN) unless authorized_via_secret(options["secret"])

          fail(Errors.make(Errors::BAD_ARGUMENT), "Invalid privilege level #{privs}") unless %w{READ WRITE ADMIN NONE}.include?(privs)

          role = (::Mrg::Grid::Config::Auth::Role.find_first_by_username(username) || ::Mrg::Grid::Config::Auth::Role.create(:username=>username, :privs=>::Mrg::Grid::Config::Auth::Priv::NONE))
          role.privs = ::Mrg::Grid::Config::Auth::Priv.const_get(privs)

          ::Mrg::Grid::Config::Auth::RoleCache.populate

          nil
        end

        expose :set_user_privs do |args|
          args.declare :username, :lstr, :in, "The username to set privileges for."
          args.declare :privs, :sstr, :in, "One of 'READ', 'WRITE', or 'ADMIN.'"
          args.declare :options, :map, :in, "Recognized options include 'secret', which is your installation's Wallaby secret."
        end

        # deletes role for a given user
        def del_user(username, options=nil)
          options ||= {}

          # note that we authorize here to allow use of WALLABY_SECRET
          authorize_now(:ADMIN) unless authorized_via_secret(options["secret"])

          role = ::Mrg::Grid::Config::Auth::Role.find_first_by_username(username)
          fail(Errors.make(Errors::BAD_ARGUMENT), "User #{username} does not exist in the Wallaby role database") unless role

          role.delete

          ::Mrg::Grid::Config::Auth::RoleCache.populate

          nil
        end

        expose :del_user do |args|
          args.declare :username, :lstr, :in, "The username to set privileges for."
          args.declare :options, :map, :in, "Recognized options include 'secret', which is your installation's Wallaby secret."
        end

        def users(options=nil)
          options ||= {}

          authorize_now(:READ) unless authorized_via_secret(options["secret"])

          ::Mrg::Grid::Config::Auth::Role.find_all.inject({}) do |acc, role|
            acc[role.username] = ::Mrg::Grid::Config::Auth::Priv.to_string(role.privs)
            acc
          end
        end

        expose :users do |args|
          args.declare :options, :map, :in, "Recognized options include 'secret', which is your installation's Wallaby secret."
          args.declare :roles, :map, :out, "Map of user names to privilege levels"
        end

        ["default", "skeleton"].each do |special_group|
          define_method "get#{special_group.capitalize}Group".to_sym do
            fail(Errors.make(Errors::NOT_IMPLEMENTED), "get#{special_group.capitalize}Group is not enabled") unless Store.const_get("ENABLE_#{special_group.upcase}_GROUP")
            return Group.send("#{special_group.upcase}_GROUP")
          end

          expose "get#{special_group.capitalize}Group".to_sym do |args|
            args.declare :obj, :objId, :out, "The object ID of the Group object corresponding to the #{special_group} group."
          end

          authorize_before "get#{special_group.capitalize}Group".to_sym, :READ
        end

        # getGroup
        # * query (map/I)
        #   A map(queryType, value) that defines the group desired. The queryType can be either 'ID' or 'Name'. 'ID' queryTypes will search for a group with the ID defined in value. 'Name' queryTypes will search for a group with the name defined in value.
        # * obj (objId/O)
        def getGroup(query)
          qentries = query.entries
          fail(Errors.make(Errors::BAD_QUERY, Errors::GROUP), "Invalid group query #{query.inspect}") if qentries.size != 1
          qkind, qkey = query.entries.pop
          qkind = qkind.upcase

          case qkind
          when "ID"
            grp = Group.find(qkey)
            fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::GROUP), "Group ID #{qkey} not found") unless grp
            return grp
          when "NAME"
            grp = Group.find_first_by_name(qkey)
            fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::GROUP), "Group named #{qkey} not found") unless grp
            return grp
          else fail(Errors.make(Errors::BAD_QUERY, Errors::GROUP), "Invalid group query kind #{qkind}")
          end
        end

        expose :getGroup do |args|
          args.declare :obj, :objId, :out, "The object ID of the Group object corresponding to the requested group."
          args.declare :query, :map, :in, "A map from a query type to a query parameter. The queryType can be either 'ID' or 'Name'. 'ID' queryTypes will search for a group with the ID supplied as a parameter. 'Name' queryTypes will search for a group with the name supplied as a parameter."
        end

        def getGroupByName(name)
          getGroup({"NAME"=>name})
        end

        authorize_before :getGroup, :READ

        expose :getGroupByName do |args|
          args.declare :name, :sstr, :in, "The name of the group to search for."
          args.declare :obj, :objId, :out, "The object ID of the Group object corresponding to the requested group."
        end

        authorize_before :getGroupByName, :READ

        # addExplicitGroup
        # * name (sstr/I)
        # * obj (objId/O)
        def addExplicitGroup(name)
          # Print values of input parameters
          log.debug "addExplicitGroup: name => #{name.inspect}"
          fail(Errors.make(Errors::NAME_ALREADY_IN_USE, Errors::GROUP), "Group name #{name} is already taken") if Group.find_first_by_name(name)
          fail(Errors.make(Errors::INVALID_NAME, Errors::GROUP), "Group name #{name} is invalid; group names may not start with '+++'") if name.slice(0,3) == "+++"
          fail(Errors.make(Errors::INVALID_NAME, Errors::GROUP), "Group names cannot be empty") if name.size == 0
          Group.create(:name=>name)
        end

        expose :addExplicitGroup do |args|
          args.declare :obj, :objId, :out, "The object ID of the Group object corresponding to the newly-created group."
          args.declare :name, :sstr, :in, "The name of the newly-created group.  Names beginning with '+++' are reserved for internal use."
        end

        authorize_before :addExplicitGroup, :WRITE

        # removeGroup
        # * uid (uint32/I)
        def removeGroup(name)
          # Print values of input parameters
          log.debug "removeGroup: name => #{name.inspect}"
          group = Group.find_first_by_name(name)
          fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::GROUP), "Group named #{name} not found") unless group
          fail(Errors.make(Errors::BAD_COMMAND, Errors::GROUP), "Can't remove special group #{group.name}") if group.is_special
          group.db_membership.each {|n| DirtyElement.dirty_node(n) }
          group.delete
        end

        expose :removeGroup do |args|
          args.declare :name, :sstr, :in, "The name of the group to remove."
        end

        authorize_before :removeGroup, :WRITE

        # getFeature
        # * name (sstr/I)
        # * obj (objId/O)
        def getFeature(name)
          # Print values of input parameters
          log.debug "getFeature: name => #{name.inspect}"

          feature = Feature.find_first_by_name(name)
          fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::FEATURE), "Feature named #{name} not found") unless feature
          return feature

        end

        expose :getFeature do |args|
          args.declare :obj, :objId, :out, "The object ID of the Feature object corresponding to the requested feature."
          args.declare :name, :sstr, :in, "The name of the feature to search for."
        end

        authorize_before :getFeature, :READ

        # addFeature
        # * name (sstr/I)
        # * obj (objId/O)
        def addFeature(name)
          # Print values of input parameters
          log.debug "addFeature: name => #{name.inspect}"
          fail(Errors.make(Errors::NAME_ALREADY_IN_USE, Errors::FEATURE), "Feature name #{name} is already taken") if Feature.find_first_by_name(name)
          fail(Errors.make(Errors::INVALID_NAME, Errors::FEATURE), "Feature names cannot be empty") if name.size == 0
          return Feature.create(:name=>name)
        end

        expose :addFeature do |args|
          args.declare :obj, :objId, :out, "The object ID of the newly-created Feature object."
          args.declare :name, :sstr, :in, "The name of the feature to create."
        end

        authorize_before :addFeature, :WRITE

        # removeFeature
        # * uid (uint32/I)
        def removeFeature(name)
          # Print values of input parameters
          log.debug "removeFeature: name => #{name.inspect}"
          feature = Feature.find_first_by_name(name)

          fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::FEATURE), "Feature named #{name} does not exist") unless feature

          feature.delete
        end

        expose :removeFeature do |args|
          args.declare :name, :sstr, :in, "The name of the feature to remove."
        end

        authorize_before :removeFeature, :WRITE

        # activateConfiguration
        # * explain (map/O)
        #   A map containing an explanation of why the configuration isn't valid, or
        #   an empty map if the configuration was successfully pushed out
        # * warnings (map/O)
        #   A map whose keys represent a set of warnings encountered in configuration activation

        def activateConfiguration()
          validate_and_activate
        end

        expose :activateConfiguration do |args|
          args.declare :explain, :map, :out, "A map containing an explanation of why the configuration isn't valid, or an empty map if the configuration was successfully activated."
          args.declare :warnings, :list, :out, "A set of warnings encountered during configuration activation."
        end

        authorize_before :activateConfiguration, :ADMIN

        def validateConfiguration
          validate_and_activate(true)
        end

        expose :validateConfiguration do |args|
          args.declare :explain, :map, :out, "A map containing an explanation of why the configuration isn't valid, or an empty map if the configuration was successfully activated."
          args.declare :warnings, :list, :out, "A set of warnings encountered during configuration activation."
        end

        authorize_before :validateConfiguration, :READ

        def affectedEntities(options=nil)
          options = {"failfast"=>nil}.merge(options || {})
          if options["failfast"]
            dg = DirtyElement.find_first_by_kind(DirtyElement.KIND_EVERYTHING)
            return [dg.to_pair] if dg
          end
          result = DirtyElement.find_all.map {|elt| elt.to_pair}
          bytes = OBJECT_OVERHEAD * 4 # XXX: this is conservative but perhaps unnecessarily so
          truncated_result = result.inject([]) do |acc, val|
            this_val_size = OBJECT_OVERHEAD * 3 + val[0].length + val[1].length
            if bytes + this_val_size < MAX_ARG_SIZE
              bytes += this_val_size
              acc << val
            else
              acc << ["OMITTED", "..."]
            end
          end
          truncated_result
        end

        expose :affectedEntities do |args|
          args.declare :options, :map, :in, "Valid options include 'failfast', which will return a single-element list if the default group or the whole store is dirty."
          args.declare :result, :list, :out, "A list of TYPE, NAME pairs indicating elements that have changed since the last activation and are due for validation and activation.  May be truncated due to communication limitations."
        end

        authorize_before :affectedEntities, :READ

        def affectedNodes(options=nil)
          options ||= {}
          result = Node.get_dirty_nodes
          bytes = OBJECT_OVERHEAD * 4 # XXX: this is conservative but perhaps unnecessarily so
          truncated_result = result.inject([]) do |acc, val|
            this_val_size = OBJECT_OVERHEAD + val.name.length
            if bytes + this_val_size < MAX_ARG_SIZE
              bytes += this_val_size
              acc << val
            else
              acc << "..."
            end
          end
          truncated_result
        end

        expose :affectedNodes do |args|
          args.declare :options, :map, :in, "No options are yet supported."
          args.declare :result, :list, :out, "A list of nodes that are due for validation and activation.  May be truncated due to communication limitations."
        end

        authorize_before :affectedNodes, :READ

        # addNode
        # * name (sstr/I)
        # * obj (objId/O)
        def addNode(name, options=nil)
          # Print values of input parameters
          log.debug "addNode: name => #{name.inspect}; options => #{options.inspect}"
          options ||= {}

          n = Node.find_first_by_name(name)

          if n
            # Since this node already exists but is being explicitly added now, ensure that it is marked as "provisioned"
            n.provisioned = true
          else
            # Return a newly-created node
            n = createNode(name, true, options)
          end

          # Mark the new node as "dirty" so it will get an updated configuration
          DirtyElement.dirty_node(n)

          # Return the appropriate node after ensuring that its identity group is initialized
          n.identity_group
          n
        end

        alias addNodeWithOptions addNode

        expose :addNodeWithOptions do |args|
          args.declare :obj, :objId, :out, "The object ID of the newly-created Node object."
          args.declare :name, :sstr, :in, "The name of the node to create."
          args.declare :options, :map, :in, "Optional arguments. (Introduced in 20101031.2)"
        end

        expose :addNode do |args|
          args.declare :obj, :objId, :out, "The object ID of the newly-created Node object."
          args.declare :name, :sstr, :in, "The name of the node to create."
        end

        # XXX:  perhaps worth considering whether addNode and getNode demand WRITE access
        # or READ access (since adding a node doesn't affect config but getting a node can
        # result in node creation)
        authorize_before :addNode, :WRITE
        authorize_before :addNodeWithOptions, :WRITE

        # getNode
        # * name (sstr/I)
        # * obj (objId/O)
        def getNode(name)
          # Print values of input parameters
          log.debug "getNode: name => #{name.inspect}"

          # Return the node with the given name
          n = Node.find_first_by_name(name)
          unless n
            n = createNode(name, false)

            # Mark the new node as "dirty" so it will get an updated configuration
            DirtyElement.dirty_node(n)
          end

          n
        end

        expose :getNode do |args|
          args.declare :obj, :objId, :out, "The object ID of the retrieved Node object."
          args.declare :name, :sstr, :in, "The name of the node to find.  If no node exists with this name, the store will create an unprovisioned node with the given name."
        end

        authorize_before :getNode, :READ

        def createNode(name, is_provisioned=true, options=nil)
          options ||= {}
          fail(Errors.make(Errors::INVALID_NAME, Errors::NODE), "Node name #{name} is invalid; node names may not start with '+++'") if name.slice(0,3) == "+++"
          fail(Errors.make(Errors::INVALID_NAME, Errors::NODE), "Node names cannot be empty") if name.size == 0

          seek_version = options["seek_versioned_config"]
          seek_version = (seek_version && ConfigVersion.hasVersionedNodeConfig(name, seek_version)) || nil

          n = Node.create(:name=>name, :provisioned=>is_provisioned, :last_checkin=>0, :last_updated_version=>seek_version || 0)

          # add to the skeleton group
          n.modifyMemberships("ADD", [Group::SKELETON_GROUP_NAME], {}) if Store::ENABLE_SKELETON_GROUP

          # XXX:  this is almost certainly not the right place to do this (copying default config over for newly-created nodes)
          n.last_updated_version = seek_version || ConfigVersion.makeInitialConfig(name)
          n
        end

        # removeNode
        # * name (sstr/I)
        def removeNode(name)
          # Print values of input parameters
          log.debug "removeNode: name => #{name.inspect}"

          # Actually remove the node
          node = Node.find_first_by_name(name)

          # Remove any versioned configurations for this node
          VersionedNode[name].delete

          if node
            node.idgroup.delete if node.idgroup
            node.delete
          else
            fail(7, "Failed to remove nonexistent node #{name}")
          end
        end

        expose :removeNode do |args|
          args.declare :name, :sstr, :in, "The name of the node to remove."
        end

        authorize_before :removeNode, :WRITE

        # getParam
        # * name (sstr/I)
        # * obj (objId/O)
        #   A reference to the parameter object that matches the name supplied
        def getParam(name)
          # Print values of input parameters
          log.debug "getParam: name => #{name.inspect}"

          param = Parameter.find_first_by_name(name)
          fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::PARAMETER), "Parameter named #{name} not found") unless param

          return param
        end

        expose :getParam do |args|
          args.declare :obj, :objId, :out, "The object ID of the requested Parameter object."
          args.declare :name, :sstr, :in, "The name of the parameter to find."
        end

        authorize_before :getParam, :READ

        # addParam
        # * name (sstr/I)
        # * obj (objId/O)
        #   A reference to the newly added parameter object
        def addParam(name)
          # Print values of input parameters
          log.debug "addParam: name => #{name.inspect}"

           fail(Errors.make(Errors::NAME_ALREADY_IN_USE, Errors::PARAMETER), "Parameter name #{name} is already taken") if Parameter.find_first_by_name(name)
          fail(Errors.make(Errors::INVALID_NAME, Errors::PARAMETER), "Parameter names cannot be empty") if name.size == 0

          # Return value
          return Parameter.create(:name => name)
        end

        expose :addParam do |args|
          args.declare :obj, :objId, :out, "The object ID of the newly-created Parameter object."
          args.declare :name, :sstr, :in, "The name of the parameter to create."
        end

        authorize_before :addParam, :WRITE

        # getSubsys
        # * name (sstr/I)
        # * obj (objId/O)
        #   A reference to the subsystem object that matches the name supplied
        def getSubsys(name)
          # Print values of input parameters
          log.debug "getSubsys: name => #{name.inspect}"

          return Subsystem.find_first_by_name(name)
        end

        expose :getSubsys do |args|
          args.declare :obj, :objId, :out, "The object ID of the requested Subsystem object."
          args.declare :name, :sstr, :in, "The name of the subsystem to find."
        end

        authorize_before :getSubsys, :READ

        # addSubsys
        # * name (sstr/I)
        # * obj (objId/O)
        #   A reference to the newly added subsystem object
        def addSubsys(name)
          # Print values of input parameters
          log.debug "addSubsys: name => #{name.inspect}"
           fail(Errors.make(Errors::NAME_ALREADY_IN_USE, Errors::SUBSYSTEM), "Subsystem name #{name} is already taken") if Subsystem.find_first_by_name(name)
          fail(Errors.make(Errors::INVALID_NAME, Errors::SUBSYSTEM), "Subsystem names cannot be empty") if name.size == 0

          # Return value
          return Subsystem.create(:name => name)
        end

        expose :addSubsys do |args|
          args.declare :obj, :objId, :out, "The object ID of the newly-created Subsystem object."
          args.declare :name, :sstr, :in, "The name of the subsystem to create."
        end

        authorize_before :addSubsys, :WRITE

        # removeParam
        # * name (sstr/I)
        def removeParam(name)
          # Print values of input parameters
          log.debug "removeParam: name => #{name.inspect}"

          param = Parameter.find_first_by_name(name)
          fail(7, "Parameter named #{name} not found") unless param

          param.delete
        end

        expose :removeParam do |args|
          args.declare :name, :sstr, :in, "The name of the parameter to remove."
        end

        authorize_before :removeParam, :WRITE

        # removeSubsys
        # * name (sstr/I)
        def removeSubsys(name)
          # Print values of input parameters
          log.debug "removeSubsys: name => #{name.inspect}"
          Subsystem.find_first_by_name(name).delete
        end

        expose :removeSubsys do |args|
          args.declare :name, :sstr, :in, "The name of the subsystem to remove."
        end

        authorize_before :removeSubsys, :WRITE

        def internal_storeinit(kwargs=nil)
          kwargs ||= {}
          options = kwargs.keys.map {|k| k.upcase}

          if options.include? "RESETDB"
            clear_db
          end

          if options.include? "REMOVE-SPURIOUS"
            remove_spurious_configs
          end

          Group.DEFAULT_GROUP

          # There's no harm in always creating this.
          Group.SKELETON_GROUP
          nil
        end

        def storeinit(kwargs=nil)
          internal_storeinit(kwargs)
        end

        authorize_before :storeinit, :WRITE

        expose :storeinit do |args|
          args.declare :options, :map, :in, "Setting 'RESETDB' will reset the configuration database."
        end

        # <method name="makeSnapshot">
        #    <arg name="name" dir="I" type="sstr"
        #         desc="A name for this configuration.  A blank name will result
        #               in the store creating a name"/>
        # </method>

        def makeSnapshot(name)
          makeSnapshotWithOptions(name)
        end

        # XXX: this could be "none" or "read," since making a snapshot has no observable effect on configuration
        authorize_before :makeSnapshot, :WRITE

        expose :makeSnapshot do |args|
          args.declare :name, :sstr, :in, "A name for this configuration.  A blank name will result in the store creating a name"
        end

        def makeSnapshotWithOptions(name, opts=nil)
          opts = {"annotation"=>""}.merge(opts || {})
          name = Mrg::Grid::Config::Snapshot.autogen_name("Automatically generated snapshot") if name.size == 0
          fail(42, "Snapshot name #{name} already taken") if Snapshot.find_first_by_name(name)
          result = Snapshot.create(:name=>name, :annotation=>opts["annotation"])
          snaptext = ::Mrg::Grid::SerializedConfigs::ConfigSerializer.new(self, false).serialize.to_yaml
          result.snaptext = snaptext
        end

        # XXX: this could be "none" or "read," since making a snapshot has no observable effect on configuration
        authorize_before :makeSnapshotWithOptions, :WRITE

        expose :makeSnapshotWithOptions do |args|
          args.declare :name, :sstr, :in, "A name for this configuration.  A blank name will result in the store creating a name"
          args.declare :options, :map, :in, "Options for this snapshot.  Valid options include 'annotation', which must map to a string containing an annotation for this snapshot."
        end

        def loadSnapshot(name)
          snap = Snapshot.find_first_by_name(name)

          fail(Errors.make(Errors::NONEXISTENT_ENTITY, Errors::SNAPSHOT), "Invalid snapshot name #{name}") unless snap
          snaptext = snap.snaptext

          storeinit("RESETDB"=>true)

          ::Mrg::Grid::SerializedConfigs::ConfigLoader.log = log

          ::Rhubarb::Persistence::db.transaction do |the_db|
            ::Mrg::Grid::SerializedConfigs::ConfigLoader.new(self, snaptext).load
          end

        end

        authorize_before :loadSnapshot, :ADMIN

        expose :loadSnapshot do |args|
          args.declare :name, :sstr, :in, "A name for the snapshot to load."
        end

        def removeSnapshot(name)
          Snapshot.find_first_by_name(name).delete
        end

        # XXX: erring on the restrictive side here
        authorize_before :removeSnapshot, :ADMIN

        expose :removeSnapshot do |args|
          args.declare :name, :sstr, :in, "A name for the snapshot to remove."
        end

        def getMustChangeParams
          Parameter.s_that_must_change
        end

        authorize_before :getMustChangeParams, :READ

        expose :getMustChangeParams do |args|
          args.declare :params, :map, :out, "Parameters that must change; a map from names to (empty) default values"
        end

        [Feature, Group, Node, Parameter, Subsystem].each do |klass|
          define_method "check#{klass.name.split("::").pop}Validity".to_sym do |fset|
            log.debug "check#{klass.name.split("::").pop}Validity called:  set is #{fset}"
            entities = fset.sort.uniq
            klass.select_invalid(entities)
          end

          expose "check#{klass.name.split("::").pop}Validity".to_sym do |args|
            args.declare :set, :list, :in, "A set of #{klass.name} names to check for validity"
            args.declare "invalid#{klass.name.split("::").pop}s".to_sym, :list, :out, "A (possibly-empty) set consisting of all of the #{klass.name} names from the input set that do not correspond to valid #{klass.name}s"
          end

          authorize_before "check#{klass.name.split("::").pop}Validity".to_sym, :NONE
        end

        [:Feature, :Group, :Node, :Parameter, :Subsystem].each do |klass|
          define_method "#{klass.to_s.downcase}s" do
            instances_of(klass)
          end
        end

        private

        def instances_of(klass)
          ::Mrg::Grid::Config.const_get(klass).find_all
        end

        def app
          Store.app rescue nil
        end

        def clear_db
          MAIN_DB_TABLES.each do |table|
            table.delete_all rescue table.find_all.each {|row| row.delete}
          end
        end

        def remove_spurious_configs
          old_count = VersionedNodeConfig.count
          VersionedNodeConfig.delete_spurious
          new_count = VersionedNodeConfig.count
          log.info(new_count == old_count ? "Inspected #{old_count} versioned configs" :  "Inspected #{old_count} versioned configs and removed #{old_count - new_count} spurious ones")
        end

        def validate_and_activate(validate_only=false, explicit_nodelist=nil, this_version=nil)

          unless ::Rhubarb::Persistence::db.transaction_active? || ConfigVersion.db.transaction_active?
            ConfigVersion.db.transaction do |ignored|
              return _validate_and_activate(validate_only, explicit_nodelist, this_version)
            end
          else
            return _validate_and_activate(validate_only, explicit_nodelist, this_version)
          end

        end

        def _validate_and_activate(validate_only=false, explicit_nodelist=nil, this_version=nil)
          dirty_nodes = explicit_nodelist || Node.get_dirty_nodes
          dirty_elements = DirtyElement.count
          this_version ||= ::Rhubarb::Util::timestamp
          nothing_changed = (dirty_nodes.size == 0)
          default_group_only = (nothing_changed && Node.count == 0)
          all_nodes = dirty_nodes.size == Node.count && !default_group_only
          warnings = []

          log.debug "entering validate_and_activate for an #{explicit_nodelist ? "explicit" : "implicit"} node list; we have #{dirty_elements} dirty element#{dirty_elements == 1 ? "" : "s"}; dirty node count is #{dirty_nodes.size} (#{all_nodes ? "all" : "not all"} nodes)"

          if default_group_only
            log.warn "Attempting to activate a configuration with no nodes; will simply check the configuration of the default group"
            dirty_nodes << Group.DEFAULT_GROUP
            warnings << "No nodes in configuration; only tested #{ENABLE_SKELETON_GROUP ? "default and skeleton groups" : "default group"}"
            nothing_changed = false
          elsif nothing_changed
            log.warn "User requested configuration #{validate_only ? "validation" : "activation"}, but no nodes have changed configurations since last activate."
            warnings << "No node configurations have changed since the last activated config; #{validate_only ? "validate" : "activate"} request will have no effect."
          end

          if ENABLE_SKELETON_GROUP && DirtyElement.find_first_by_grp(Group.SKELETON_GROUP)
            skeleton_node = TransientNode.new("+++SKELETON_NODE")
            skeleton_node.memberships = [Group.SKELETON_GROUP]
            dirty_nodes << skeleton_node
          end

          options = {}

          if ENV['WALLABY_USE_VALIDATE_CACHE'] && ENV['WALLABY_USE_VALIDATE_CACHE'].downcase == "never"
            options[:cache] = DummyCache.new
          else
            options[:cache] = ConfigDataCache.new(*dirty_nodes)
          end
          options[:save_for_version] = this_version unless validate_only

          bytes_left = MAX_ARG_SIZE - MAX_SIZE_CUSHION
          nodes_left = dirty_nodes.size

          node_pairs = dirty_nodes.inject([]) do |acc, node|
            if bytes_left <= 0
              acc << ["*", "More validation errors may have occured; stopped processing nodes with #{nodes_left} nodes left"]
              break acc
            end

            node_result = node.validate(options)
            nodes_left -= 1

            unless node_result == true
              acc << node_result
              bytes_left -= OBJECT_OVERHEAD
              bytes_left -= node_result[0].size
              node_result[1].each do |explain_k, explain_v|
                bytes_left -= explain_k.size
                bytes_left -= explain_v.inject(0) {|running_total,s| running_total += s.size}
              end
            end

            acc
          end

          results = Hash[*node_pairs.flatten]

          warnings << results.delete("*") if results["*"]

          if validate_only || nothing_changed || results.keys.size > 0
            ConfigVersion[this_version].delete
            return [results, warnings]
          end

          # we're activating this configuration; save the default group configuration by itself
          # if possible and if we aren't dealing with an explicit node list
          unless explicit_nodelist
            validate_and_activate(false, [Group.DEFAULT_GROUP], this_version) unless ConfigVersion.hasVersionedNodeConfig(Group::DEFAULT_GROUP_NAME, this_version)
          end

          log.debug "in validate_and_activate; just deleted dirty elements; count is #{DirtyElement.count}"
          DirtyElement.delete_all

          begin
            config_events_to(dirty_nodes, this_version, all_nodes) unless explicit_nodelist
          rescue Exception=>ex
            warnings << "Couldn't send configuration events:  #{ex.inspect}"
          end

          dirty_nodes.each {|dn| dn.last_updated_version = this_version if dn.respond_to? :last_updated_version }

          [results, warnings]
        end

        module PullEventGenerator
          include QmfV1Kludges

          def new_config_event(nodes, current_version)
            notice = NodeUpdatedNotice.new
            notice.nodes = nodes
            notice.version = current_version
            notice.bang!
          end

          def config_events_to(node_list, current_version, all_nodes=false)
            node_names = all_nodes ? ["*"] : node_list.map {|n| n.name}

            log.debug { "calling PullEventGenerator#config_events_to; node_names is #{node_names.inspect}, current_version is #{current_version.inspect}" }
            acc = {}
            bytes = 0

            node_names.sort.each do |node|
              if (bytes + node.size) > (MAX_ARG_SIZE - MAX_SIZE_CUSHION)
                new_config_event(acc, current_version)
                acc = {}
                bytes = 0
              end

              acc[node] = 1
              bytes += (node.size)
            end

            new_config_event(acc, current_version) if acc.size > 0
          end
        end

        include PullEventGenerator
      end
    end
  end
end


text:
id: version-1.11.0-transactions
title: Transactions
original_id: transactions
---

KafkaJS provides a a simple interface to support Kafka transactions.

> Note: Transactions require Kafka version >= v0.11.

##  Sending Messages within a Transaction

You initialize a transaction by making an async call to `producer.transaction()`. The returned transaction object has the methods `send` and `sendBatch` with an identical signature to the producer. When you are done you call `transaction.commit()` or `transaction.abort()` to end the transaction. A transactionally aware consumer will only read messages which were committed.

> Note: Kafka requires that the transactional producer have the following configuration to _guarantee_ EoS ("Exactly-once-semantics"):
>
> - The producer must have a max in flight requests of 1
> - The producer must wait for acknowledgement from all replicas (acks=-1)
> - The producer must have unlimited retries

Configure the producer client with `maxInFlightRequests: 1` and `idempotent: true` to guarantee EOS. Configuring the two options will enable the settings mentioned above.

```javascript
const client = new Kafka({
  clientId: 'transactional-client',
  brokers: ['kafka1:9092', 'kafka2:9092'],
})
const producer = client.producer({ maxInFlightRequests: 1, idempotent: true })
```

Within a transaction, you can produce one or more messages. If `transaction.abort` is called, all messages will be rolled back.

```javascript
const  transaction = await producer.transaction()

try {
  await transaction.send({ topic, messages })

  await transaction.commit()
} catch (e) {
  await transaction.abort()
}
```

####  Sending Offsets

To send offsets as part of a transaction, meaning they will be committed only if the transaction succeeds, use the `transaction.sendOffsets()` method. This is necessary whenever we want a transaction to produce messages derived from a consumer, in a "consume-transform-produce" loop.

```javascript
await transaction.sendOffsets({
  consumerGroupId, topics
})
```

`topics` has the following structure:

```javascript
[{
  topic: <String>,
  partitions: [{
    partition: <Number>,
    offset: <String>
  }]
}]
```

redpajama_set_name: RedPajamaGithub


text:
class Net::LDAP
  def bind_as(args)
    rs = search(args)
    if rs && rs.first && dn = rs.first[:dn]
      password = args[:password]
      password = password.call if password.respond_to?(:call)
      @username = dn
      @password = password
      rs if bind
    end
  end
end


text:
package org.apache.calcite.avatica.remote;

import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.Meta.Frame;
import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
import org.apache.calcite.avatica.proto.Common;
import org.apache.calcite.avatica.proto.Common.ColumnValue;
import org.apache.calcite.avatica.proto.Requests;
import org.apache.calcite.avatica.proto.Responses;
import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
import org.apache.calcite.avatica.remote.Service.FetchRequest;
import org.apache.calcite.avatica.remote.Service.FetchResponse;
import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

/**
 * Test basic serialization of objects with protocol buffers.
 */
public class ProtobufHandlerTest {

  // Mocks
  private Service service;
  private ProtobufTranslation translation;

  // Real objects
  private ProtobufHandler handler;

  @Before
  public void setupMocks() {
    // Mocks
    service = Mockito.mock(Service.class);
    translation = Mockito.mock(ProtobufTranslation.class);

    // Real objects
    handler = new ProtobufHandler(service, translation, NoopMetricsSystem.getInstance());
  }

  @Test
  public void testFetch() throws Exception {
    final String connectionId = "cnxn1";
    final int statementId = 30;
    final long offset = 10;
    final int fetchMaxRowCount = 100;
    final List<Common.TypedValue> values = new ArrayList<>();

    values.add(Common.TypedValue.newBuilder().setType(Common.Rep.BOOLEAN).setBoolValue(true)
        .build());
    values.add(Common.TypedValue.newBuilder().setType(Common.Rep.STRING)
        .setStringValue("my_string").build());

    Requests.FetchRequest protoRequest = Requests.FetchRequest.newBuilder()
        .setConnectionId(connectionId).setStatementId(statementId)
        .setOffset(offset).setFetchMaxRowCount(fetchMaxRowCount)
        .build();
    byte[] serializedRequest = protoRequest.toByteArray();

    FetchRequest request = new FetchRequest().deserialize(protoRequest);

    List<Object> frameRows = new ArrayList<>();
    frameRows.add(new Object[] {true, "my_string"});

    Meta.Frame frame = Frame.create(0, true, frameRows);
    RpcMetadataResponse metadata = new RpcMetadataResponse("localhost:8765");
    FetchResponse response = new FetchResponse(frame, false, false, metadata);

    when(translation.parseRequest(serializedRequest)).thenReturn(request);
    when(service.apply(request)).thenReturn(response);
    when(translation.serializeResponse(response))
        .thenReturn(response.serialize().toByteArray());

    HandlerResponse<byte[]> handlerResponse = handler.apply(serializedRequest);
    byte[] serializedResponse = handlerResponse.getResponse();
    assertEquals(200, handlerResponse.getStatusCode());

    Responses.FetchResponse protoResponse = Responses.FetchResponse.parseFrom(serializedResponse);

    Common.Frame protoFrame = protoResponse.getFrame();

    assertEquals(frame.offset, protoFrame.getOffset());
    assertEquals(frame.done, protoFrame.getDone());

    List<Common.Row> rows = protoFrame.getRowsList();
    assertEquals(1, rows.size());
    Common.Row row = rows.get(0);
    List<Common.ColumnValue> columnValues = row.getValueList();
    assertEquals(2, columnValues.size());

    Iterator<Common.ColumnValue> iter = columnValues.iterator();
    assertTrue(iter.hasNext());
    Common.ColumnValue column = iter.next();
    assertTrue("The Column should have contained a scalar: " + column,
        column.hasField(ColumnValue.getDescriptor()
            .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER)));

    Common.TypedValue value = column.getScalarValue();
    assertEquals(Common.Rep.BOOLEAN, value.getType());
    assertEquals(true, value.getBoolValue());

    assertTrue(iter.hasNext());
    column = iter.next();
    assertTrue("The Column should have contained a scalar: " + column,
        column.hasField(ColumnValue.getDescriptor()
            .findFieldByNumber(ColumnValue.SCALAR_VALUE_FIELD_NUMBER)));
    value = column.getScalarValue();
    assertEquals(Common.Rep.STRING, value.getType());
    assertEquals("my_string", value.getStringValue());
  }

}

// End ProtobufHandlerTest.java


text:
using System;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Features.PoA;
using Stratis.Bitcoin.Features.PoA.Voting;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Utilities.JsonErrors;
using Stratis.Bitcoin.Utilities.ModelStateErrors;

namespace Stratis.Features.FederatedPeg.Controllers
{
    [Route("api/[controller]")]
    public class CollateralVotingController : Controller
    {
        protected readonly IFederationManager fedManager;

        protected readonly VotingManager votingManager;

        protected readonly Network network;

        protected readonly ILogger logger;

        public CollateralVotingController(IFederationManager fedManager, ILoggerFactory loggerFactory, VotingManager votingManager, Network network)
        {
            this.fedManager = fedManager;
            this.votingManager = votingManager;
            this.network = network;

            this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
        }

        [Route("schedulevote-addfedmember")]
        [HttpPost]
        public IActionResult VoteAddFedMember([FromBody]CollateralFederationMemberModel request)
        {
            return this.VoteAddKickFedMember(request, true);
        }

        [Route("schedulevote-kickfedmember")]
        [HttpPost]
        public IActionResult VoteKickFedMember([FromBody]CollateralFederationMemberModel request)
        {
            return this.VoteAddKickFedMember(request, false);
        }

        private IActionResult VoteAddKickFedMember(CollateralFederationMemberModel request, bool addMember)
        {
            Guard.NotNull(request, nameof(request));

            // TODO remove this line when multisig recreation is implemented.
            return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Disabled in current version.", string.Empty);

            if (!this.ModelState.IsValid)
                return ModelStateErrors.BuildErrorResponse(this.ModelState);

            if (!this.fedManager.IsFederationMember)
                return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Only federation members can vote", string.Empty);

            try
            {
                var key = new PubKey(request.PubKeyHex);

                IFederationMember federationMember = new CollateralFederationMember(key, new Money(request.CollateralAmountSatoshis), request.CollateralMainchainAddress);

                byte[] fedMemberBytes = (this.network.Consensus.ConsensusFactory as PoAConsensusFactory).SerializeFederationMember(federationMember);

                this.votingManager.ScheduleVote(new VotingData()
                {
                    Key = addMember ? VoteKey.AddFederationMember : VoteKey.KickFederationMember,
                    Data = fedMemberBytes
                });

                return this.Ok();
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a command.", e.ToString());
            }
        }
    }
}


text:
module Tokenizer where

import Data.Char (isAlphaNum)
import Types.Token

isOperatorChar :: Char -> Bool
isOperatorChar '+' = True
isOperatorChar '*' = True
isOperatorChar _ = False

tokenize :: String -> [Token]
tokenize [] = []
tokenize ('=':xs) = Equals : tokenize xs
tokenize ('(':xs) = BracketOpen : tokenize xs
tokenize (')':xs) = BracketClose : tokenize xs
tokenize ('[':xs) = ArgsBegin : tokenize xs
tokenize (']':xs) = ArgsEnd : tokenize xs
tokenize (' ':xs) = tokenize xs
tokenize ('\n':xs) = tokenize xs
tokenize ('\r':xs) = tokenize xs
tokenize ('\t':xs) = tokenize xs
tokenize ('\v':xs) = tokenize xs
tokenize ('\f':xs) = tokenize xs
tokenize s@(x:_) = if isAlphaNum x
    then case reads ident :: [(Integer, String)] of
        [(lit, "")] -> Literal lit : tokenize identxs
        _ -> Identifier ident : tokenize identxs
    else if isOperatorChar x
        then Operator op : tokenize opxs
        else error $ "unexpected token `" ++ x : "`"
  where
    (ident, identxs) = span isAlphaNum s
    (op, opxs) = span isOperatorChar s


text:
package org.kohsuke.stapler.compression;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Pimps up {@link HttpServletResponse} so that it understands "Content-Encoding: gzip" and compress the response.
 *
 *

 * When exceptions are processed within web applications, different unrelated parts of the webapp can end up calling
 * {@link HttpServletResponse#getOutputStream()}. This fundamentally doesn't work with the notion that the application
 * needs to process "Content-Encoding:gzip" on its own. Such app has to maintain a GZIP output stream on its own,
 * since {@link HttpServletResponse} doesn't know that its output is written through a compressed stream.
 *
 *

 * Another place this break-down can be seen is when a servlet throws an exception that the container handles.
 * It tries to render an error page, but of course it wouldn't know that "Content-Encoding:gzip" is set, so it
 * will fail to write in the correct format.
 *
 *

 * The only way to properly fix this is to make {@link HttpServletResponse} smart enough that it returns
 * the compression-transparent stream from {@link HttpServletResponse#getOutputStream()} (and it would also
 * have to process the exception thrown from the app.) This filter does exactly that.
 *
 * @author Kohsuke Kawaguchi
 */
public class CompressionFilter implements Filter {
    private ServletContext context;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        context = filterConfig.getServletContext();
    }

    @Override
    public void doFilter(ServletRequest _req, ServletResponse _rsp, FilterChain filterChain) throws IOException, ServletException {
        Object old1 = swapAttribute(_req, CompressionFilter.class, true);

        CompressionServletResponse rsp = new CompressionServletResponse((HttpServletResponse) _rsp);
        Object old2 = swapAttribute(_req,CompressionServletResponse.class,rsp);

        try {
            filterChain.doFilter(_req, rsp);
        } catch (IOException | Error | RuntimeException | ServletException e) {
            if (DISABLED)   throw e;
            reportException(e,(HttpServletRequest)_req,rsp);
        } finally {
            rsp.close();

            _req.setAttribute(CompressionFilter.class.getName(),old1);
            _req.setAttribute(CompressionServletResponse.class.getName(),old2);
        }
    }

    private Object swapAttribute(ServletRequest req, Class<?> key, Object value) {
        Object old = req.getAttribute(key.getName());
        req.setAttribute(key.getName(), value);
        return old;
    }

    private void reportException(Throwable e, HttpServletRequest req, HttpServletResponse rsp) throws IOException, ServletException {
        getUncaughtExceptionHandler(context).reportException(e, context, req, rsp);
    }

    @Override
    public void destroy() {
    }

    public static void setUncaughtExceptionHandler(ServletContext context, UncaughtExceptionHandler handler) {
        context.setAttribute(UncaughtExceptionHandler.class.getName(),handler);
    }

    public static UncaughtExceptionHandler getUncaughtExceptionHandler(ServletContext context) {
        UncaughtExceptionHandler h = (UncaughtExceptionHandler) context.getAttribute(UncaughtExceptionHandler.class.getName());
        if (h==null)    h=UncaughtExceptionHandler.DEFAULT;
        return h;
    }

    /**
     * Is this request already wrapped into {@link CompressionFilter}?
     */
    public static boolean has(ServletRequest req) {
        return req.getAttribute(CompressionServletResponse.class.getName())!=null;
    }

    /**
     * Is this request already wrapped into {@link CompressionFilter},
     * activate that, so that {@link ServletResponse#getOutputStream()} will return
     * a stream that automatically handles compression.
     */
    public static boolean activate(ServletRequest req) throws IOException {
        CompressionServletResponse rsp = (CompressionServletResponse) req.getAttribute(CompressionServletResponse.class.getName());
        if (rsp!=null) {
            rsp.activate();
            return true;
        } else {
            return false;
        }
    }

    /**
     * Disables the effect of {@link #setUncaughtExceptionHandler}, letting all errors be rethrown.
     * Despite its name, this flag does not disable {@link CompressionFilter} itself!
     * Rather use {@code DefaultScriptInvoker.COMPRESS_BY_DEFAULT}.
     */
    @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Legacy switch.")
    public static boolean DISABLED = Boolean.getBoolean(CompressionFilter.class.getName()+".disabled");
}


text:
package com.opengamma.engine.view.listener;

import org.threeten.bp.Instant;

import com.opengamma.engine.view.ViewComputationResultModel;
import com.opengamma.engine.view.ViewDeltaResultModel;
import com.opengamma.engine.view.compilation.CompiledViewDefinition;
import com.opengamma.engine.view.cycle.ViewCycleMetadata;
import com.opengamma.engine.view.execution.ViewCycleExecutionOptions;

/**
 * Implementation of {@link ViewResultListener} which does nothing, designed for overriding specific methods.
 */
public abstract class AbstractViewResultListener implements ViewResultListener {

  @Override
  public void viewDefinitionCompiled(final CompiledViewDefinition compiledViewDefinition, final boolean hasMarketDataPermissions) {
  }

  @Override
  public void viewDefinitionCompilationFailed(final Instant valuationTime, final Exception exception) {
  }

  @Override
  public void cycleStarted(final ViewCycleMetadata cycleInfo) {
  }

  @Override
  public void cycleCompleted(final ViewComputationResultModel fullResult, final ViewDeltaResultModel deltaResult) {
  }

  @Override
  public void cycleFragmentCompleted(final ViewComputationResultModel fullResult, final ViewDeltaResultModel deltaResult) {
  }

  @Override
  public void cycleExecutionFailed(final ViewCycleExecutionOptions executionOptions, final Exception exception) {
  }

  @Override
  public void processCompleted() {
  }

  @Override
  public void processTerminated(final boolean executionInterrupted) {
  }

  @Override
  public void clientShutdown(final Exception e) {
  }

}


text:
Description
===========

Installs and configures Nagios server and NRPE client.  Chef nodes are automatically discovered using search, and Nagios host groups are created based on Chef roles and optionally environments as well.  NRPE client commands can be defined by using a LWRP, and Nagios service checks applied to hostgroups using definitions in data bag items.

Requirements
============

Chef
----

Chef version 0.10.10+ and Ohai 0.6.12+ are required.

Because of the heavy use of search, this recipe will not work with Chef Solo, as it cannot do any searches without a server.

This cookbook relies heavily on multiple data bags.  See __Data Bag__ below.

The system running the 'server' recipe should have a role named 'monitoring' so that NRPE clients can authorize monitoring from that system.  This role name is configurable via an attribute. See __Attributes__ below.

Platform
--------

* Debian 6
* Ubuntu 10.04, 12.04
* Red Hat Enterprise Linux (CentOS/Amazon/Scientific/Oracle) 5.8, 6.3

**Notes**: This cookbook has been tested on the listed platforms. It
  may work on other platforms with or without modification.

Cookbooks
---------

* apache2
* build-essential
* nginx
* nginx_simplecgi
* php
* yum

Attributes
==========

default
-------

The following attributes are used by both client and server recipes.

* `node['nagios']['user']` - Nagios user, default 'nagios'.
* `node['nagios']['group']` - Nagios group, default 'nagios'.
* `node['nagios']['plugin_dir']` - location where Nagios plugins go, default '/usr/lib/nagios/plugins'.
* `node['nagios']['multi_environment_monitoring']` - Chef server will monitor hosts in all environments, not just its own, default 'false'

client
------

The following attributes are used for the NRPE client

* `node['nagios']['client']['install_method']` - whether to install from package or source. Default chosen by platform based on known packages available for NRPE: debian/ubuntu 'package', redhat/centos/fedora/scientific: source
* `node['nagios']['plugins']['url']` - url to retrieve the plugins source
* `node['nagios']['plugins']['version']` - version of the plugins source to download
* `node['nagios']['plugins']['checksum']` - checksum of the plugins source tarball
* `node['nagios']['nrpe']['home']` - home directory of nrpe, default /usr/lib/nagios
* `node['nagios']['nrpe']['conf_dir']` - location of the nrpe configuration, default /etc/nagios
* `node['nagios']['nrpe']['url']` - url to retrieve NRPE source
* `node['nagios']['nrpe']['version']` - version of NRPE source to download
* `node['nagios']['nrpe']['checksum']` - checksum of the nrpe source tarball
* `node['nagios']['nrpe']['packages']` - nrpe / plugin packages to install.  The default attribute for RHEL/Fedora platforms contains a bare minimum set of packages.  The full list of available packages is available at: `http://dl.fedoraproject.org/pub/epel/6/x86_64/repoview/letter_n.group.html`
* `node['nagios']['server_role']` - the role that the Nagios server will have in its run list that the clients can search for.

server
------

The following attributes are used for the Nagios server

* `node['nagios']['server']['install_method']` - whether to install from package or source. Default chosen by platform based on known packages available for Nagios: debian/ubuntu 'package', redhat/centos/fedora/scientific: source
* `node['nagios']['server']['service_name']` - name of the service used for Nagios, default chosen by platform, debian/ubuntu "nagios3", redhat family "nagios", all others, "nagios"
* `node['nagios']['home']` - Nagios main home directory, default "/usr/lib/nagios3"
* `node['nagios']['conf_dir']` - location where main Nagios config lives, default "/etc/nagios3"
* `node['nagios']['config_dir']` - location where included configuration files live, default "/etc/nagios3/conf.d"
* `node['nagios']['log_dir']` - location of Nagios logs, default "/var/log/nagios3"
* `node['nagios']['cache_dir']` - location of cached data, default "/var/cache/nagios3"
* `node['nagios']['state_dir']` - Nagios runtime state information, default "/var/lib/nagios3"
* `node['nagios']['run_dir']` - where pidfiles are stored, default "/var/run/nagios3"
* `node['nagios']['docroot']` - Nagios webui docroot, default "/usr/share/nagios3/htdocs"
* `node['nagios']['enable_ssl]` - boolean for whether Nagios web server should be https, default false
* `node['nagios']['http_port']` - port that the Apache server should listen on, determined whether ssl is enabled (443 if so, otherwise 80)
* `node['nagios']['server_name']` - common name to use in a server cert, default "nagios"
* `node['nagios']['ssl_req']` - info to use in a cert, default `/C=US/ST=Several/L=Locality/O=Example/OU=Operations/CN=#{node['nagios']['server_name']}/emailAddress=ops@#{node['nagios']['server_name']}`

*  `node['nagios']['server']['url']` - url to download the server source from if installing from source
*  `node['nagios']['server']['version']` - version of the server source to download
*  `node['nagios']['server']['checksum']` - checksum of the source files
*  `node['nagios']['url']` - URL to host nagios from - defaults to nil and instead uses  FQDN

* `node['nagios']['notifications_enabled']` - set to 1 to enable notification.
* `node['nagios']['check_external_commands']`
* `node['nagios']['default_contact_groups']`
* `node['nagios']['sysadmin_email']` - default notification email.
* `node['nagios']['sysadmin_sms_email']` - default notification sms.
* `node['nagios']['server_auth_method']` - authentication with the server can be done with openid (using `apache2::mod_auth_openid`), cas (using `apache2::mod_auth_cas`),ldap (using `apache2::mod_authnz_ldap`), or htauth (basic). The default is openid, "cas" will utilize cas authentication, "ldap" will utilize LDAP authentication, and any other value will use htauth (basic).
* `node['nagios']['cas_login_url']` - login url for cas if using cas authentication.
* `node['nagios']['cas_validate_url']` - validation url for cas if using cas authentication.
* `node['nagios']['cas_validate_server']` - whether to validate the server cert. Defaults to off.
* `node['nagios']['cas_root_proxy_url']` - if set, sets the url that the cas server redirects to after auth.
* `node['nagios']['ldap_bind_dn']` - DN used to bind to the server when searching for ldap entries.
* `node['nagios']['ldap_bind_password']` - bind password used with the DN provided for searcing ldap.
* `node['nagios']['ldap_url']` - ldap url and search parameters.
* `node['nagios']['ldap_authoritative']` - accepts "on" or "off". controls other authentication modules from authenticating the user if this one fails.
* `node['nagios']['users_databag_group']` - users databag group considered Nagios admins.  defaults to sysadmins
* `node['nagios']['host_name_attribute']` - node attribute to use for naming the host. Must be unique across monitored nodes. Defaults to hostname
* `node['nagios']['templates']`
* `node['nagios']['interval_length']` - minimum interval.

* `node['nagios']['default_host']['check_interval']`
* `node['nagios']['default_host']['retry_interval']`
* `node['nagios']['default_host']['max_check_attempts']`
* `node['nagios']['default_host']['notification_interval']`

* `node['nagios']['default_service']['check_interval']`
* `node['nagios']['default_service']['retry_interval']`
* `node['nagios']['default_service']['max_check_attempts']`
* `node['nagios']['default_service']['notification_interval']`

* `node['nagios']['server']['web_server']` - web server to use. supports Apache or Nginx, default "apache"
* `node['nagios']['server']['nginx_dispatch']` - nginx dispatch method. support cgi or php, default "cgi"
* `node['nagios']['server']['stop_apache']` - stop apache service if using nginx, default false
* `node['nagios']['server']['redirect_root']` - if using Apache, should http://server/ redirect to http://server/nagios3 automatically, default false
* `node['nagios']['server']['normalize_hostname']` - If set to true, normalize all hostnames in hosts.cfg to lowercase.  Defaults to false.

Recipes
=======

default
-------

Includes the `nagios::client` recipe to install NRPE client.

client
------

Includes the correct NRPE client installation recipe based on platform, either `nagios::client_package` or `nagios::client_source`.

The client recipe searches for servers allowed to connect via NRPE that have a role named in the `node['nagios']['server_role']` attribute. The recipe will also install the required packages and start the NRPE service. A custom plugin for checking memory is also added.

Searches are confined to the node's `chef_environment` unless the `multi_environment_monitoring` attribute has been set to true.

Client commands for NRPE can be installed using the nrpecheck lwrp. (See __Resources/Providers__ below.)

RHEL and Fedora default to installation via source, but you can install NRPE via package by changing `node['nagios']['client']['install_method']` to "package".  Note that this will enable the EPEL repository on RHEL systems, which may not be desired.  You will also need to modify `node['nagios']['nrpe']['packages']` to include the appropriate NRPE plugins for your environment.  The complete list is available at `http://dl.fedoraproject.org/pub/epel/6/x86_64/repoview/letter_n.group.html`

client\_package
---------------

Installs the NRPE client and plugins from packages. Default for Debian / Ubuntu systems.

client\_source
--------------

Installs the NRPE client and plugins from source. Default for Redhat and Fedora based systems, as native packages for NRPE are not available in the default repositories.

server
------

Includes the correct client installation recipe based on platform, either `nagios::server_package` or `nagios::server_source`.

The server recipe sets up Apache as the web front end by default. The nagios::client recipe is also included. This recipe also does a number of searches to dynamically build the hostgroups to monitor, hosts that belong to them and admins to notify of events/alerts.

Searches are confined to the node's `chef_environment` unless multi-environment monitoring is enabled.

The recipe does the following:

1. Searches for users in 'users' databag belonging to to 'sysadmins' group and authorizes them to access the Nagios web UI and receive notification e-mails.
2. Searches all available roles/environments and builds a list which will become the Nagios hostgroups.
3. Places nodes in Nagios hostgroups by role / environment membership.
4. Installs various packages required for the server.
5. Sets up configuration directories.
6. Moves the package-installed Nagios configuration to a 'dist' directory.
7. Disables the 000-default VirtualHost present on Debian/Ubuntu Apache2 package installations.
8. Templates configuration files for services, contacts, contact groups, templates, hostgroups and hosts.
9. Enables the Nagios web UI.
10. Starts the Nagios server service

server\_package
---------------

Installs the Nagios server from packages. Default for Debian / Ubuntu systems.

server\_source
--------------

Installs the Nagios server from source. Default for Red Hat / Fedora based systems as native packages for Nagios are not available in the default repositories.

pagerduty
---------

Installs and configures pagerduty plugin for nagios.  You need to set a `node['nagios']['pagerduty_key']` attribute on your server for this to work.  This can be set through environments so that you can use different API keys for servers in production vs staging for instance.

This recipe was written based on the [Nagios Integration Guide](http://www.pagerduty.com/docs/guides/nagios-integration-guide) from PagerDuty which explains how to get an API key for your nagios server.

Data Bags
=========

Users
-----

Create a `users` data bag that will contain the users that will be able to log into the Nagios webui. Each user can use htauth with a specified password, or an openid. Users that should be able to log in should be in the sysadmin group. Example user data bag item:

    {
      "id": "nagiosadmin",
      "groups": "sysadmin",
      "htpasswd": "hashed_htpassword",
      "openid": "http://nagiosadmin.myopenid.com/",
      "nagios": {
        "pager": "nagiosadmin_pager@example.com",
        "email": "nagiosadmin@example.com"
      }
    }

When using `server_auth_method` 'openid' (default), use the openid in the data bag item. Any other value for this attribute (e.g., "htauth", "htpasswd", etc) will use the htpasswd value as the password in `/etc/nagios3/htpasswd.users`.

The openid must have the http:// and trailing /. The htpasswd must be the hashed value. Get this value with htpasswd:

    % htpasswd -n -s nagiosadmin
    New password:
    Re-type new password:
    nagiosadmin:{SHA}oCagzV4lMZyS7jl2Z0WlmLxEkt4=

For example use the `{SHA}oCagzV4lMZyS7jl2Z0WlmLxEkt4=` value in the data bag.

Contacts and Contact Groups
---------------------------

To send alerting notification to contacts that aren't authorized to login to Nagios via the 'users' data bag create `nagios_contacts` and `nagios_contactgroups` data bags.

Example `nagios_contacts` data bag item

    {
	  "id": "devs",
      "alias": "Developers",
	  "use": "default-contact",
      "email": "devs@company.com",
      "pager": "page_the_devs@company.com"
    }

Example `nagios_contactgroup` data bag item

    {
	  "id": "non_admins",
      "alias": "Non-Administrator Contacts",
      "members": "devs helpdesk managers"
    }

Services
--------

To add service checks to Nagios create a `nagios_services` data bag containing definitions for services to be monitored.  This allows you to add monitoring rules without directly editing the services and commands templates in the cookbook.  Each service will be named based on the id of the data bag item and the command will be named using the same id prepended with "check\_".  Just make sure the id in your data bag doesn't conflict with a service or command already defined in the templates.

Here's an example of a service check for sshd that you could apply to all hostgroups:

    {
	  "id": "ssh",
      "hostgroup_name": "linux",
	  "command_line": "$USER1$/check_ssh $HOSTADDRESS$"
    }

You may optionally define the service template for your service by including `service_template` and a valid template name.  Example:  "service_template": "special_service_template".  You may also optionally add a service description that will be displayed in the Nagios UI using "description": "My Service Name".  If this is not present the databag item ID will be used as the description.  You use defined escalations for the service with 'use_escalation'.  See ___Service_Escalations__ for more information.

You may also use an already defined command definition by omitting the command\_line parameter and using use\_existing\_command parameter instead:

    {
    "id": "pingme",
     "hostgroup_name": "all",
     "use_existing_command": "check-host-alive"
    }

Service Groups
--------------

Create a nagios\_servicegroups data bag that will contain definitions for service groups.  Each server group will be named based on the id of the data bag.

    {
    "id": "ops",
    "alias": "Ops",
    "notes": "Services for ops"
    }

You can group your services by using the "servicegroups" keyword in your services data bags. For example, to have your ssh
checks show up under the ops service group, you could define it like this:

    {
    "id": "ssh",
    "hostgroup_name": "all",
    "command_line": "$USER1$/check_ssh $HOSTADDRESS$",
    "servicegroups": "ops"
    }

Templates
---------

Templates are optional, but allow you to specify combinations of attributes to apply to a service.  Create a nagios_templates\ data bag that will contain definitions for templates to be used.  Each template need only specify id and whichever parameters you want to override.

Here's an example of a template that reduces the check frequency to once per day and changes the retry interval to 1 hour.

    {
      "id": "dailychecks",
      "check_interval": "86400",
      "retry": "3600"
    }

You then use the template in your service data bag as follows:

    {
      "id": "expensive_service_check",
      "hostgroup_name": "linux",
      "command_line": "$USER1$/check_example $HOSTADDRESS$",
      "service_template": "dailychecks"
    }

Search Defined Hostgroups
-------------------------

Create a nagios\_hostgroups data bag that will contain definitions for Nagios hostgroups populated via search.  These data bags include a Chef node search query that will populate the Nagios hostgroup with nodes based on the search.

Here's an example to find all HP hardware systems for an "hp_systems" hostgroup:

	{
	  "search_query": "dmi_system_manufacturer:HP",
	  "hostgroup_name": "hp_systems",
	  "id": "hp_systems"
	}

Monitoring Systems Not In Chef
------------------------------

Create a nagios\_unmanagedhosts data bag that will contain definitions for hosts not in Chef that you would like to manage.  "hostgroups" can be an existing Chef role (every Chef role gets a Nagios hostgroup) or a new hostgroup.  Note that "hostgroups" must be an array of hostgroups even if it contains just a single hostgroup.

Here's an example host definition:

	{
	  "address": "webserver1.mydmz.dmz",
	  "hostgroups": ["web_servers","production_servers"],
	  "id": "webserver1",
	  "notifications": 1
	}

Service Escalations
-------------------

You can optionally define service escalations for the data bag defined services.  Doing so involves two steps - creating the escalation data bag and invoking it from the service.  For example, to create an escalation to page managers on a 15 minute period after the 3rd page:

	{
      "name": "15-minute-escalation",
      "contact_groups": "managers",
      "first_notification": "3",
      "last_notification": "0",
      "escalation_period": "24x7",
      "notification_interval": "900"
	}

Then, in the service data bag,

	{
      "id": "my-service",
      ...
      "use_escalation": "15-minute-escalation"
	}

Event Handlers
--------------

You can optionally define event handlers to trigger on service alerts by creating a nagios\_eventhandlers data bag that will contain definitions of event handlers for services monitored via Nagios.

This example event handler data bags restarts chef-client.  Note: This assumes you have already defined a NRPE job restart\_chef-client on the host where this command will run.  You can use the NRPE LWRP to add commands to your local NRPE configs from within your cookbooks.

	{
      "command_line": "$USER1$/check_nrpe -H $HOSTADDRESS$ -t 45 -c restart_chef-client",
      "id": "restart_chef-client"
	}

Once you've defined an event handler you will need to add the event handler to a service definition in order to trigger the action.  See the example service definition below.

	{
      "command_line": "$USER1$/check_nrpe -H $HOSTADDRESS$ -t 45 -c check_chef_client",
      "hostgroup_name": "linux",
      "id": "chef-client",
      "event_handler": "restart_chef-client"
	}

Monitoring Role
===============

Create a role to use for the monitoring server. The role name should match the value of the attribute "`node['nagios']['server_role']`". By default, this is '`monitoring`'. For example:

    % cat roles/monitoring.rb
    name "monitoring"
    description "Monitoring server"
    run_list(
      "recipe[nagios::server]"
    )

    default_attributes(
      "nagios" => {
        "server_auth_method" => "htauth"
      }
    )

    % knife role from file monitoring.rb

Definitions
===========

nagios_conf
-----------

This definition is used to drop in a configuration file in the base Nagios configuration directory's conf.d. This can be used for customized configurations for various services.

Libraries
=========

default
-------

The library included with the cookbook provides some helper methods used in templates.

* `nagios_boolean`
* `nagios_interval` - calculates interval based on interval length and a given number of seconds.
* `nagios_attr` - retrieves a nagios attribute from the node.

Resources/Providers
===================

nrpecheck
---------

The nrpecheck LWRP provides an easy way to add and remove NRPE checks from within cookbooks.

### Actions

- `:add` creates a NRPE configuration file and restart the NRPE process. Default action.
- `:remove` removes the configuration file and restart the NRPE process

### Attribute Parameters

- `command_name`  The name of the check.  This is the command that you will call from your nagios\_service data bag check
- `warning_condition` String that you will pass to the command with the -w flag
- `critical_condition` String that you will pass to the command with the -c flag
- `command` The actual command to execute (including the path). If this is not specified, this will use `node['nagios']['plugin_dir']/command_name` as the path to the command.
- `parameters` Any additional parameters you wish to pass to the plugin.

### Examples

    # Use LWRP to define check_load
    nagios_nrpecheck "check_load" do
      command "#{node['nagios']['plugin_dir']}/check_load"
      warning_condition node['nagios']['checks']['load']['warning']
      critical_condition node['nagios']['checks']['load']['critical']
      action :add
    end

    # Remove the check_load definition
    nagios_nrpecheck "check_load" do
      action :remove
    end

Usage
=====

server setup
------------

Create a role named '`monitoring`', and add the nagios server recipe to the `run_list`.  See __Monitoring Role__ above for an example.

Apply the nagios client recipe to nodes in order to install the NRPE client

By default the Nagios server will only monitor systems in its same environment. To change this set the `multi_environment_monitoring` attribute. See __Attributes__

Create data bag items in the `users` data bag for each administer you would like to be able to login to the Nagios server UI.  Pay special attention to the method you would like to use to authorization users (openid or htauth). See __Users__ and __Atttributes__

At this point you now have a minimally functional Nagios server, however the server will lack any service checks outside of the single Nagios Server health check.

defining checks
---------------

NRPE commands are defined in recipes using the nrpecheck LWRP provider.  For base system monitoring such as load, ssh, memory, etc you may want to create a cookbook in your environment that defines each monitoring command via the LWRP.  See the examples folder for an example of base monitoring.

With NRPE commands created using the LWRP you will need to define nagios Services to use those commands.  These services are defined using the `nagios_services` data bag and applied to roles and/or environments.  See __Services__

enabling notifications
----------------------

You need to set `default['nagios']['notifications_enabled'] = 1` attribute on your nagios server to enable email notifications.

For email notifications to work an appropriate mail program package and local MTA need to be installed so that /usr/bin/mail or /bin/mail is available on the system.

Example:

Include [postfix cookbook](https://github.com/opscode-cookbooks/postfix) to be installed on your nagios server node.

Add override_attributes to your `monitoring` role:

    % cat roles/monitoring.rb

    name "monitoring"
    description "Monitoring Server"
    run_list(
      "recipe[nagios::server]",
      "recipe[postfix]"
    )

    override_attributes(
      "nagios" => { "notifications_enabled" => "1" },
      "postfix" => { "myhostname":"your_hostname", "mydomain":"example.com" }
    )

    default_attributes(
      "nagios" => { "server_auth_method" => "htauth" }
    )

    % knife role from file monitoring.rb

License and Author
==================

Author:: Joshua Sierles <joshua@37signals.com>
Author:: Nathan Haneysmith <nathan@opscode.com>
Author:: Joshua Timberman <joshua@opscode.com>
Author:: Seth Chisamore <schisamo@opscode.com>
Author:: Tim Smith <tsmith84@gmail.com>

Copyright 2009, 37signals
Copyright 2009-2013, Opscode, Inc
Copyright 2012, Webtrends Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


text:
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 317663 $ -->

<phpdoc:classref xml:id="class.yaf-response-http" xmlns:phpdoc="http://php.net/ns/phpdoc" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude">

 <title>The Yaf_Response_Http class</title>
 <titleabbrev>Yaf_Response_Http</titleabbrev>

 <partintro>

<!-- {{{ Yaf_Response_Http intro -->
  <section xml:id="yaf-response-http.intro">
   &reftitle.intro;
   <para>

   </para>
  </section>
<!-- }}} -->

  <section xml:id="yaf-response-http.synopsis">
   &reftitle.classsynopsis;

<!-- {{{ Synopsis -->
   <classsynopsis>
    <ooclass><classname>Yaf_Response_Http</classname></ooclass>

<!-- {{{ Class synopsis -->
    <classsynopsisinfo>
     <ooclass>
      <classname>Yaf_Response_Http</classname>
     </ooclass>

     <ooclass>
      <modifier>extends</modifier>
      <classname>Yaf_Response_Abstract</classname>
     </ooclass>
    </classsynopsisinfo>
<!-- }}} -->
    <classsynopsisinfo role="comment">&Properties;</classsynopsisinfo>
    <fieldsynopsis>
     <modifier>protected</modifier>
     <varname linkend="yaf-response-http.props.sendheader">_sendheader</varname>
    </fieldsynopsis>
    <fieldsynopsis>
     <modifier>protected</modifier>
     <varname linkend="yaf-response-http.props.response-code">_response_code</varname>
    </fieldsynopsis>

    <classsynopsisinfo role="comment">&Methods;</classsynopsisinfo>
    <xi:include xpointer="xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.yaf-response-http')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[1])" />

    <classsynopsisinfo role="comment">&InheritedMethods;</classsynopsisinfo>
    <xi:include xpointer="xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.yaf-response-abstract')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[1])" />

   </classsynopsis>
<!-- }}} -->

  </section>

<!-- {{{ Yaf_Response_Http properties -->
  <section xml:id="yaf-response-http.props">
   &reftitle.properties;
   <variablelist>
    <varlistentry xml:id="yaf-response-http.props.header">
     <term><varname>_header</varname></term>
     <listitem>
      <para></para>
     </listitem>
    </varlistentry>
    <varlistentry xml:id="yaf-response-http.props.body">
     <term><varname>_body</varname></term>
     <listitem>
      <para></para>
     </listitem>
    </varlistentry>
    <varlistentry xml:id="yaf-response-http.props.sendheader">
     <term><varname>_sendheader</varname></term>
     <listitem>
      <para></para>
     </listitem>
    </varlistentry>
    <varlistentry xml:id="yaf-response-http.props.response-code">
     <term><varname>_response_code</varname></term>
     <listitem>
      <para></para>
     </listitem>
    </varlistentry>
   </variablelist>
  </section>
<!-- }}} -->

 </partintro>

 &reference.yaf.entities.yaf-response-http;

</phpdoc:classref>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->


text:
<!DOCTYPE html>

   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width">
   <link rel="stylesheet" type="text/css" href="style/ExifReader.css"/>

   <title>Exif Reader</title>

     <button id="btnMain"></button>

<button id="btnInstall">Install</button>

<button id="btnOpen"></button>

Exif Reader

Photo info

Aperture :

Shutter Speed :

ISO :


text:
#pragma once

#include "il2cpp-config.h"

#ifndef _MSC_VER
# include <alloca.h>
#else
# include <malloc.h>
#endif

#include <stdint.h>
#include <assert.h>
#include <exception>

// Mono.Security.Protocol.Tls.SecurityParameters
struct SecurityParameters_t2935569768;
// Mono.Security.Protocol.Tls.CipherSuite
struct CipherSuite_t580420223;
// System.Byte[]
struct ByteU5BU5D_t4260760469;

#include "codegen/il2cpp-codegen.h"
#include "Mono_Security_Mono_Security_Protocol_Tls_CipherSuit580420223.h"

// System.Void Mono.Security.Protocol.Tls.SecurityParameters::.ctor()
extern "C"  void SecurityParameters__ctor_m711073950 (SecurityParameters_t2935569768 * __this, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// Mono.Security.Protocol.Tls.CipherSuite Mono.Security.Protocol.Tls.SecurityParameters::get_Cipher()
extern "C"  CipherSuite_t580420223 * SecurityParameters_get_Cipher_m2063042602 (SecurityParameters_t2935569768 * __this, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Void Mono.Security.Protocol.Tls.SecurityParameters::set_Cipher(Mono.Security.Protocol.Tls.CipherSuite)
extern "C"  void SecurityParameters_set_Cipher_m1888356967 (SecurityParameters_t2935569768 * __this, CipherSuite_t580420223 * ___value0, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Byte[] Mono.Security.Protocol.Tls.SecurityParameters::get_ClientWriteMAC()
extern "C"  ByteU5BU5D_t4260760469* SecurityParameters_get_ClientWriteMAC_m509205152 (SecurityParameters_t2935569768 * __this, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Void Mono.Security.Protocol.Tls.SecurityParameters::set_ClientWriteMAC(System.Byte[])
extern "C"  void SecurityParameters_set_ClientWriteMAC_m2390402827 (SecurityParameters_t2935569768 * __this, ByteU5BU5D_t4260760469* ___value0, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Byte[] Mono.Security.Protocol.Tls.SecurityParameters::get_ServerWriteMAC()
extern "C"  ByteU5BU5D_t4260760469* SecurityParameters_get_ServerWriteMAC_m451878936 (SecurityParameters_t2935569768 * __this, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Void Mono.Security.Protocol.Tls.SecurityParameters::set_ServerWriteMAC(System.Byte[])
extern "C"  void SecurityParameters_set_ServerWriteMAC_m324273299 (SecurityParameters_t2935569768 * __this, ByteU5BU5D_t4260760469* ___value0, const MethodInfo* method) IL2CPP_METHOD_ATTR;
// System.Void Mono.Security.Protocol.Tls.SecurityParameters::Clear()
extern "C"  void SecurityParameters_Clear_m2412174537 (SecurityParameters_t2935569768 * __this, const MethodInfo* method) IL2CPP_METHOD_ATTR;


text:
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { IFormGroup } from '@rxweb/types';
import { OptionalFieldLocationFormat, TOPDeskOptionalFields, TOPDeskOptionalFieldsMapping } from '../integrations';

@Component({
  selector: 'oca-topdesk-optional-field-mapping',
  templateUrl: './topdesk-optional-field-mapping.component.html',
  styleUrls: ['./topdesk-optional-field-mapping.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TOPDeskOptionalFieldMappingComponent {
  @Input() formGroup: IFormGroup<TOPDeskOptionalFieldsMapping>;
  LOCATION_FORMATS = [
    { type: OptionalFieldLocationFormat.LATITUDE_LONGITUDE, label: 'oca.latitude_and_longitude_in_one_field' },
    { type: OptionalFieldLocationFormat.LATITUDE, label: 'oca.latitude' },
    { type: OptionalFieldLocationFormat.LONGITUDE, label: 'oca.longitude' },
  ];

  TOPDESK_FIELDS = Object.values(TOPDeskOptionalFields);
}


text:
<?xml version="1.0" encoding="utf-8"?>
<!--
	Copyright 2015 Hello Inc.

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:duration="@integer/anime_duration_normal"
    android:shareInterpolator="true">

    <translate
        android:fromYDelta="100%"
        android:toYDelta="0%" />

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />

</set>


text:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keys_layout"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="center_horizontal">

    <!-- White Keys -->
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:orientation="horizontal">

        <RelativeLayout
            android:id="@+id/key_0"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key">

            <TextView
                android:id="@+id/key_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:paddingBottom="5dp"
                android:paddingLeft="10dp"
                android:textColor="#CCC"
                android:textSize="@dimen/font_large" />
        </RelativeLayout>

        <FrameLayout
            android:id="@+id/key_2"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_4"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_5"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_7"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_9"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_11"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />

        <FrameLayout
            android:id="@+id/key_12"
            android:layout_width="@dimen/keyboard_key_width"
            android:layout_height="fill_parent"
            android:background="@drawable/key_white_key" />
    </LinearLayout>

    <!-- Black Keys -->
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="@dimen/keyboard_black_key_height"
        android:orientation="horizontal">

        <FrameLayout
            android:id="@+id/key_1"
            android:layout_width="@dimen/keyboard_black_key_width_1"
            android:layout_height="fill_parent"
            android:layout_marginLeft="@dimen/keyboard_black_key_indent_1"
            android:background="@drawable/key_black" />

        <FrameLayout
            android:id="@+id/key_3"
            android:layout_width="@dimen/keyboard_black_key_width_1"
            android:layout_height="fill_parent"
            android:layout_marginLeft="@dimen/keyboard_black_key_indent_2"
            android:background="@drawable/key_black" />

        <FrameLayout
            android:id="@+id/key_6"
            android:layout_width="@dimen/keyboard_black_key_width_2"
            android:layout_height="fill_parent"
            android:layout_marginLeft="@dimen/keyboard_black_key_indent_3"
            android:background="@drawable/key_black" />

        <FrameLayout
            android:id="@+id/key_8"
            android:layout_width="@dimen/keyboard_black_key_width_2"
            android:layout_height="fill_parent"
            android:layout_marginLeft="@dimen/keyboard_black_key_indent_4"
            android:background="@drawable/key_black" />

        <FrameLayout
            android:id="@+id/key_10"
            android:layout_width="@dimen/keyboard_black_key_width_2"
            android:layout_height="fill_parent"
            android:layout_marginLeft="@dimen/keyboard_black_key_indent_4"
            android:background="@drawable/key_black" />

    </LinearLayout>

</RelativeLayout>


text:
#include "library/sp.h"

#include "forms/radiobutton.h"
#include "framework/event.h"
#include "framework/framework.h"
#include "framework/sound.h"

namespace OpenApoc
{

RadioButton::RadioButton(sp<RadioButtonGroup> Group, sp<Image> ImageChecked,
                         sp<Image> ImageUnchecked)
    : CheckBox(ImageChecked, ImageUnchecked), group(Group)
{
}

RadioButton::~RadioButton() = default;

sp<Control> RadioButton::copyTo(sp<Control> CopyParent)
{
	sp<RadioButton> copy;
	sp<RadioButtonGroup> newGroup;
	if (CopyParent)
	{
		if (this->group)
		{
			auto groupIt = CopyParent->radiogroups.find(this->group->ID);
			if (groupIt == CopyParent->radiogroups.end())
			{
				CopyParent->radiogroups[this->group->ID] = mksp<RadioButtonGroup>(this->group->ID);
			}
			newGroup = CopyParent->radiogroups[this->group->ID];
		}
		copy = CopyParent->createChild<RadioButton>(newGroup, imagechecked, imageunchecked);
	}
	else
	{
		copy = mksp<RadioButton>(newGroup, imagechecked, imageunchecked);
	}
	copyControlData(copy);
	if (newGroup)
	{
		newGroup->radioButtons.push_back(copy);
	}
	return copy;
}

void RadioButton::eventOccured(Event *e)
{
	Control::eventOccured(e);

	if (e->type() == EVENT_FORM_INTERACTION && e->forms().RaisedBy == shared_from_this() &&
	    e->forms().EventFlag == FormEventType::MouseDown)
	{
		if (buttonclick)
		{
			fw().soundBackend->playSample(buttonclick);
		}
	}

	/* We have to handle click events slightly differently in radiobutton to checkbox, as clicking
	 * on a selected radio button shouldn't deselect it, but instead just leave it selected */
	if (e->type() == EVENT_FORM_INTERACTION && e->forms().RaisedBy == shared_from_this() &&
	    e->forms().EventFlag == FormEventType::MouseClick && !isChecked())
	{
		setChecked(true);
	}
}

void RadioButton::setChecked(bool checked)
{
	if (checked != Checked)
	{
		if (group && checked)
		{
			for (auto &c : group->radioButtons)
			{
				auto button = c.lock();
				if (button)
				{
					button->setChecked(false);
				}
			}
		}
		CheckBox::setChecked(checked);
	}
}

RadioButtonGroup::RadioButtonGroup(UString ID) : ID(ID) {}

}; // namespace OpenApoc


text:
// SECTION-START[License Header]
// <editor-fold defaultstate="collapsed" desc=" Generated License ">

// </editor-fold>
// SECTION-END
package org.jomc.standalone.model.modlet.test;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import org.jomc.model.ModelObject;
import org.jomc.model.Module;
import org.jomc.model.Modules;
import org.jomc.model.modlet.ModelHelper;
import org.jomc.modlet.Model;
import org.jomc.modlet.ModelContext;
import org.jomc.modlet.ModelContextFactory;
import org.jomc.modlet.ModelValidationReport;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

// SECTION-START[Documentation]
// <editor-fold defaultstate="collapsed" desc=" Generated Documentation ">
/**
 * Test cases for class {@code org.jomc.standalone.model.modlet.StandaloneModelValidator}.
 *
 *

 *
Identifier:

org.jomc.standalone.model.modlet.test.StandaloneModelValidatorTest

 *
Name:

JOMC Standalone Model

 *
Abstract:

No

 *
Final:

No

 *
Stateless:

No

 *

 *
 * @author Christian Schulte 1.0
 * @version 1.0-beta-3-SNAPSHOT
 */
// </editor-fold>
// SECTION-END
// SECTION-START[Annotations]
// <editor-fold defaultstate="collapsed" desc=" Generated Annotations ">
@javax.annotation.Generated( value = "org.jomc.tools.SourceFileProcessor 1.2", comments = "See http://jomc.sourceforge.net/jomc/1.2/jomc-tools-1.2" )
// </editor-fold>
// SECTION-END
public class StandaloneModelValidatorTest
{
    // SECTION-START[StandaloneModelValidatorTest]

    @Test public void testValidateModel() throws Exception
    {
        final ModelContext context = ModelContextFactory.newInstance().newModelContext();
        final Unmarshaller u = context.createUnmarshaller( ModelObject.MODEL_PUBLIC_ID );

        final Modules modules = new Modules();
        final JAXBElement<Module> module =
            (JAXBElement<Module>) u.unmarshal( this.getClass().getResourceAsStream( "testsuite.xml" ) );

        modules.getModule().add( module.getValue() );

        final Model model = new Model();
        model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
        ModelHelper.setModules( model, modules );

        final ModelValidationReport report = context.validateModel( model );
        assertFalse( report.isModelValid() );

        for ( ModelValidationReport.Detail d : report.getDetails() )
        {
            System.out.println( d );
        }

        assertEquals( 1, report.getDetails( "IMPLEMENTATION_METHODS_DEFAULT_EXCEPTION_TARGET_CLASS_CONSTRAINT" ).size() );
        assertEquals( 1, report.getDetails( "IMPLEMENTATION_METHOD_DEFAULT_EXCEPTION_TARGET_CLASS_CONSTRAINT" ).size() );
    }

    // SECTION-END
    // SECTION-START[Constructors]
    // <editor-fold defaultstate="collapsed" desc=" Generated Constructors ">
    /** Creates a new {@code StandaloneModelValidatorTest} instance. */
    @javax.annotation.Generated( value = "org.jomc.tools.SourceFileProcessor 1.2", comments = "See http://jomc.sourceforge.net/jomc/1.2/jomc-tools-1.2" )
    public StandaloneModelValidatorTest()
    {
        // SECTION-START[Default Constructor]
        super();
        // SECTION-END
    }
    // </editor-fold>
    // SECTION-END
    // SECTION-START[Dependencies]
    // SECTION-END
    // SECTION-START[Properties]
    // SECTION-END
    // SECTION-START[Messages]
    // SECTION-END
}


text:
<?php

namespace Badminton\SiteBundle\Model;

/**
 *
 */

class Event
{

}


text:
import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, SecurityContext } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import * as MediumEditor from 'medium-editor';

@Component({
  selector: '[appEditor]', // tslint:disable-line:component-selector
  templateUrl: './editor.component.html',
  styleUrls: ['./editor.component.scss']
})
export class EditorComponent implements OnInit, OnChanges, OnDestroy {
  @Input() form: FormGroup;
  @Input() value = '';
  @Input() formControlName: string;
  @Input() placeholder = 'Type your text';
  /* Is the component position:fixed? (ensure correct position of medium-editor) */
  @Input() fixed = false;

  editor: any;

  constructor(private el: ElementRef, private sanitizer: DomSanitizer) { }

  ngOnInit() {

    // set the options
    const options: any = {
      toolbar: {
        buttons: [ 'bold', 'italic', 'underline', 'anchor', 'quote'],
        standardizeSelectionStart: true,
      },
      placeholder: {
        text: this.placeholder
      },
      paste: {
        cleanPastedHTML: true
      },
      autoLink: true,
      imageDragging: false
    };
    if (this.fixed) {
      options.elementsContainer = this.el.nativeElement.parentNode;
    }

    // initialize the medium-editor
    this.editor = new MediumEditor(this.el.nativeElement, options);
    this.setEditorContent(this.value);

    // when content changes, update the hidden textarea, too
    this.editor.subscribe('editableInput', (_event, editable) => {
      this.form.controls[this.formControlName].setValue(editable.innerHTML);
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    // when value changes, update the editor content
    if (changes.value && changes.value.currentValue !== changes.value.previousValue) {
      if (this.editor) {
        this.setEditorContent(this.value);
      }
    }
  }

  ngOnDestroy() {
    // clean
    if (this.editor) {
      this.editor.destroy();
    }
  }

  clear() {
    this.setEditorContent('');
  }

  private setEditorContent(value: string) {
    const sanitizedValue: SafeHtml = this.sanitizer.sanitize(SecurityContext.HTML, value);
    this.editor.setContent(sanitizedValue);
  }
}


text:
package collections

import (
	"testing"

	"fmt"

	"github.com/gohugoio/hugo/deps"
	"github.com/gohugoio/hugo/tpl"
	"github.com/stretchr/testify/require"
)

type templateFinder int

func (templateFinder) Lookup(name string) (tpl.Template, bool) {
	return nil, false
}

func (templateFinder) GetFuncs() map[string]interface{} {
	return map[string]interface{}{
		"print": fmt.Sprint,
	}
}

func TestApply(t *testing.T) {
	t.Parallel()

	ns := New(&deps.Deps{Tmpl: new(templateFinder)})

	strings := []interface{}{"a\n", "b\n"}

	result, err := ns.Apply(strings, "print", "a", "b", "c")
	require.NoError(t, err)
	require.Equal(t, []interface{}{"abc", "abc"}, result, "testing variadic")

	_, err = ns.Apply(strings, "apply", ".")
	require.Error(t, err)

	var nilErr *error
	_, err = ns.Apply(nilErr, "chomp", ".")
	require.Error(t, err)

	_, err = ns.Apply(strings, "dobedobedo", ".")
	require.Error(t, err)

	_, err = ns.Apply(strings, "foo.Chomp", "c\n")
	if err == nil {
		t.Errorf("apply with unknown func should fail")
	}

}


text:
from . import _main
from sys import exit
exit(_main())