class Jamf::DistributionPoint
A FileShare Distribution Point in the JSS
As well as the normal Class and Instance methods for {APIObject} subclasses, the DistributionPoint
class provides more interaction with other parts of the API.
Beyond the standard listing methods DistributionPoint.all
, .all_ids, etc, every JSS
has a single “master” distribution point. The Class method {DistributionPoint.master_distribution_point} will return the Jamf::DistributionPoint
object for that master.
Also, some network segments have specific DistributionPoints assigned to them. Calling the Class method {DistributionPoint.my_distribution_point} will return a Jamf::DistributionPoint
object for your local IP address.
Once you have an instance of Jamf::DistributionPoint
, you can mount it (on a Mac) by calling its {#mount} method and unmount it with {#unmount}. The {Jamf::Package} and possibly {Jamf::Script} classes use this to upload items to the master.
NOTE: This class only deals with FileShare Distribution Points. There is no access to the Cloud Distribution Point in the classic API. See the .master_distribution_point and .my_distribution_point class methods for how they handle things when the Cloud DP is the master.
@see Jamf::APIObject
Constants
- DEFAULT_MOUNTPOINT_DIR
Set default local mount for distribution point
- DEFAULT_MOUNTPOINT_PREFIX
- EMPTY_PW_256
An empty SHA256 digest
- MOUNT_OPTIONS
what are the mount options? these are comma-separated, and are passed with -o
- OBJECT_HISTORY_OBJECT_TYPE
the object type for this object in the object history table. See {APIObject#add_object_history_entry}
- RSRC_BASE
The base for REST resources of this class
- RSRC_LIST_KEY
the hash key used for the JSON list output of all objects in the
JSS
its also used in various error messages- RSRC_OBJECT_KEY
The hash key used for the JSON object output. It’s also used in various error messages
Attributes
@return [String] the name of the cert. used for http cert. auth.
@return [Boolean] do http downloads use cert. authentication?
@return [String] Protocol for fileservice access (e.g. AFP, SMB)
@return [String] the “context” for http downloads (what goes after the hostname part of the URL)
@return [String] load balanacing enabled?
@return [Integer] the id of the DP to use for failover
@return [String] the URL to use if this one doesn’t work
@return [String] the hostname of this DP
@return [Boolean] are http downloads available from this DP?
@return [String] the password for http downloads, if needed, as a SHA256 digest
@return [String] the URL for http downloads
@return [String] the username to use for http downloads if needed for user/pw auth
@return [String] the hostname of this DP
@return [Boolean] is this the master DP?
@return [String] the local path on the server to the distribution point directory
@return [Boolean] is this the master DP?
@return [Boolean] do http downloads work without auth?
@return [Integer] the port for http access
@return [String] the protocol to use for http downloads (http/https)
@return [String] read-only password as a SHA256 digest
@return [String] read-only username for fileservice
@return [String] the read-write password as a SHA256 digest
@return [String] the read-write username for fileservice access
@return [String] the ssh password as a SHA256 digest
@return [String] ssh username
@return [Boolean] do http downloads use user/pw auth?
@return [String] work group or domain for SMB
Public Class Methods
Get the DistributionPoint
instance for the master distribution point.
If the Cloud Dist Point is master, then the classic API has no way to know that or access it. In that case you can provide the ‘default:’ parameter. Give it the name or id of any dist. point to be used instead, or give it :random to randomly choose one.
If there are no fileshare dist points defined (the cloud is the only one) then this whole class can’t really be used.
@param refresh should the distribution point be re-queried?
@param default[String, Integer, Symbol] Name or ID of a dist point to use
if no master is found, or :random to randomly choose one.
@param cnx [Jamf::Connection] which API connection should we query?
@return [Jamf::DistributionPoint]
# File lib/jamf/api/classic/api_objects/distribution_point.rb 107 def self.master_distribution_point(refresh = false, default: nil, api: nil, cnx: Jamf.cnx) 108 cnx = api if api 109 110 all_ids(refresh, cnx: cnx).each do |dp_id| 111 dp = fetch id: dp_id, cnx: cnx 112 return dp if dp.master? 113 end 114 115 case default 116 when :random 117 fetch id: all_ids.sample, cnx: cnx 118 when nil 119 raise Jamf::NoSuchItemError, 'No Master FileShare Distribtion Point. Use the default: parameter if needed.' 120 else 121 fetch default, cnx: cnx 122 end 123 end
Get the DistributionPoint
instance for the machine running this code, based on its IP address. If none is defined for this IP address, use the name or id provided as default. If no default: is provided, the master dp is used. If no master dp available (meaning its the cloud dp) then use a randomly chosen dp.
@param refresh should the distribution point be re-queried?
@param default[String, Integer, Symbol] the name or id of a Dist Point
to use if none is specified for this IP addr. Or :master, to use the master DP, or :random to use a randomly chosen one. If :master is specified and there is no master (master is cloud) then a random one is used.
@param cnx [Jamf::Connection] which API connection should we query?
@return [Jamf::DistributionPoint]
# File lib/jamf/api/classic/api_objects/distribution_point.rb 142 def self.my_distribution_point(refresh = false, default: :master, api: nil, cnx: Jamf.cnx) 143 cnx = api if api 144 145 @my_distribution_point = nil if refresh 146 return @my_distribution_point if @my_distribution_point 147 148 my_net_seg_id = Jamf::NetworkSegment.my_network_segment refresh, cnx: cnx 149 150 if my_net_seg_id 151 my_net_seg = Jamf::NetworkSegment.fetch id: my_net_seg_id, cnx: cnx 152 my_dp_name = my_net_seg.distribution_point 153 @my_distribution_point = fetch name: my_dp_name, cnx: cnx if my_dp_name 154 end # if my_net_seg_id 155 156 return @my_distribution_point if @my_distribution_point 157 158 @my_distribution_point = 159 case default 160 when String 161 fetch name: default, cnx: cnx 162 when Integer 163 fetch id: default, cnx: cnx 164 when :master 165 master_distribution_point refresh, default: :random, cnx: cnx 166 when :random 167 fetch id: all_ids(refresh).sample, cnx: cnx 168 end 169 end
Jamf::APIObject::new
# File lib/jamf/api/classic/api_objects/distribution_point.rb 261 def initialize(**args) 262 super 263 264 @ip_address = @init_data[:ip_address] 265 @local_path = @init_data[:local_path] 266 @enable_load_balancing = @init_data[:enable_load_balancing] 267 @failover_point = @init_data[:failover_point] 268 @is_master = @init_data[:is_master] 269 270 @connection_type = @init_data[:connection_type] 271 @share_port = @init_data[:share_port] 272 @share_name = @init_data[:share_name] 273 @workgroup_or_domain = @init_data[:workgroup_or_domain] 274 275 @read_write_username = @init_data[:read_write_username] 276 @read_write_password_sha256 = @init_data[:read_write_password_sha256] 277 @read_only_username = @init_data[:read_only_username] 278 @read_only_password_sha256 = @init_data[:read_only_password_sha256] 279 @ssh_username = @init_data[:ssh_username] 280 @ssh_password_sha256 = @init_data[:ssh_password_sha256] 281 @http_username = @init_data[:http_username] 282 @http_password_sha256 = @init_data[:http_password_sha256] 283 284 @http_downloads_enabled = @init_data[:http_downloads_enabled] 285 @protocol = @init_data[:protocol] 286 @port = @init_data[:port] 287 @context = @init_data[:context] 288 @no_authentication_required = @init_data[:no_authentication_required] 289 @certificate_required = @init_data[:certificate_required] 290 @username_password_required = @init_data[:username_password_required] 291 @certificate = @init_data[:certificate] 292 @http_url = @init_data[:http_url] 293 @failover_point_url = @init_data[:failover_point_url] 294 295 @port = @init_data[:ssh_password] 296 297 # if we mount for fileservice, where's the mountpoint? 298 @mountpoint = DEFAULT_MOUNTPOINT_DIR + "#{DEFAULT_MOUNTPOINT_PREFIX}#{@id}" 299 end
Public Instance Methods
@deprecated The API no longer sends SHA256 hashed password data, and instead
only has a string of asterisks, meaning we can no longer use it to validate passwords before attempting to use them. Instead, the processes that use them, e.g. mounting a Dist. Point, will fail on their own if the pw is not valid. This method remains defined for backward-compatibility with any existing code that calls it. but it will always return true. It will be removed in a future version
@param user ignored
@param pw ignored
@return [TrueClass] Allow the process calling this to continue.
# File lib/jamf/api/classic/api_objects/distribution_point.rb 317 def check_pw(_user = nil, _pw = nil) 318 true 319 end
Mount this distribution point locally.
@param pw the read-only or read-write password for this DistributionPoint
If :prompt, the user is promted on the commandline to enter the password for the :user. If :stdin#, the password is read from a line of std in represented by the digits at #, so :stdin3 reads the passwd from the third line of standard input. defaults to line 2, if no digit is supplied. see {JSS.stdin}
@param access how to mount the DistributionPoint
, and which password to expect.
:ro (or anything else) = read-only, :rw = read-write
@return [Pathname] the mountpoint.
# File lib/jamf/api/classic/api_objects/distribution_point.rb 382 def mount(pw = nil, access = :ro) 383 return @mountpoint if mounted? 384 385 access = :ro unless access == :rw 386 387 password = if pw == :prompt 388 JSS.prompt_for_password "Enter the password for the #{access} user '#{access == :ro ? @read_only_username : @read_write_username}':" 389 elsif pw.is_a?(Symbol) && pw.to_s.start_with?('stdin') 390 pw.to_s =~ /^stdin(\d+)$/ 391 line = Regexp.last_match(1) 392 line ||= 2 393 JSS.stdin line 394 else 395 pw 396 end 397 398 username = access == :ro ? @read_only_username : @read_write_username 399 400 safe_pw = CGI.escape password.to_s 401 402 @mount_url = "#{@connection_type.downcase}://#{username}:#{safe_pw}@#{@ip_address}/#{@share_name}" 403 @mnt_cmd = case @connection_type.downcase 404 when 'smb' then '/sbin/mount_smbfs' 405 when 'afp' then '/sbin/mount_afp' 406 else raise "Can't mount distribution point #{@name}: no known connection type." 407 end 408 409 @mountpoint.mkpath 410 411 mount_out = `#{@mnt_cmd} -o '#{MOUNT_OPTIONS}' '#{@mount_url}' '#{@mountpoint}' 2>&1` 412 if ($CHILD_STATUS.exitstatus == 0) && @mountpoint.mountpoint? 413 # if system @mnt_cmd.to_s, *['-o', MOUNT_OPTIONS, @mount_url, @mountpoint.to_s] 414 @mounted = access 415 else 416 @mountpoint.rmdir if @mountpoint.directory? 417 @mounted = nil 418 raise Jamf::FileServiceError, "Can't mount #{@ip_address}: #{mount_out}" 419 end 420 @mountpoint 421 end
Is this thing mounted right now?
@return [Boolean]
# File lib/jamf/api/classic/api_objects/distribution_point.rb 446 def mounted? 447 @mountpoint.directory? && @mountpoint.mountpoint? 448 end
Check to see if this dist point is reachable for downloads (read-only) via either http, if available, or filesharing.
@param pw the read-only password to use for checking the connection
If http downloads are enabled, and no http password is required this can be omitted.
@param check_http should we try the http download first, if enabled?
If you're intentionally using the ro password for filesharing, and want to check only filesharing, then set this to false.
@return [FalseClass, Symbol] false if not reachable, otherwise :http or :mountable
# File lib/jamf/api/classic/api_objects/distribution_point.rb 334 def reachable_for_download?(pw = '', check_http = true) 335 return :http if check_http && http_reachable?(pw) 336 return :mountable if mounted? 337 338 begin 339 mount pw, :ro 340 :mountable 341 rescue 342 false 343 ensure 344 unmount 345 end 346 end
Check to see if this dist point is reachable for uploads (read-write) via filesharing.
@param pw the read-write password to use for checking the connection
@return [FalseClass, Symbol] false if not reachable, otherwise :mountable
# File lib/jamf/api/classic/api_objects/distribution_point.rb 355 def reachable_for_upload?(pw) 356 return :mountable if mounted? 357 358 begin 359 mount pw, :rw 360 :mountable 361 rescue 362 false 363 ensure 364 unmount 365 end 366 end
Unmount the distribution point.
Does nothing if it wasn’t mounted with mount
.
@return [void]
# File lib/jamf/api/classic/api_objects/distribution_point.rb 429 def unmount 430 return nil unless mounted? 431 432 if system "/sbin/umount '#{@mountpoint}'" 433 sleep 1 # the umount takes time. 434 @mountpoint.rmdir if @mountpoint.directory? && !@mountpoint.mountpoint? 435 @mounted = false 436 else 437 raise Jamf::FileServiceError, "There was a problem unmounting #{@mountpoint}" 438 end 439 nil 440 end
Private Instance Methods
can the dp be reached for http downloads?
# File lib/jamf/api/classic/api_objects/distribution_point.rb 460 def http_reachable?(pw = nil) 461 return false unless http_downloads_enabled 462 463 url = 464 if @username_password_required 465 user_pass = "#{CGI.escape @http_username.to_s}:#{CGI.escape pw.to_s}@" 466 @http_url.sub "://#{@ip_address}", "://#{user_pass}#{@ip_address}" 467 else 468 @http_url 469 end 470 URI.parse(url).read 471 true 472 rescue 473 false 474 end
Unused - until I get around to making DP’s updatable
the XML representation of the current state of this object, for POSTing or PUTting back to the JSS
via the API Will be supported for Dist Points some day, I’m sure.
# File lib/jamf/api/classic/api_objects/distribution_point.rb 482 def rest_xml 483 doc = REXML::Document.new 484 dp = doc.add_element 'distribution_point' 485 dp.add_element(:name.to_s).text = @name 486 dp.add_element(:ip_address.to_s).text = @ip_address 487 dp.add_element(:local_path.to_s).text = @local_path 488 dp.add_element(:enable_load_balancing.to_s).text = @enable_load_balancing 489 dp.add_element(:failover_point.to_s).text = @failover_point 490 dp.add_element(:is_master.to_s).text = @is_master 491 492 dp.add_element(:connection_type.to_s).text = @connection_type 493 dp.add_element(:share_port.to_s).text = @share_port 494 dp.add_element(:share_name.to_s).text = @share_name 495 dp.add_element(:read_write_username.to_s).text = @read_write_username 496 dp.add_element(:read_write_password.to_s).text = @read_write_password 497 dp.add_element(:read_only_username.to_s).text = @read_only_username 498 dp.add_element(:read_only_password.to_s).text = @read_only_password 499 dp.add_element(:workgroup_or_domain.to_s).text = @workgroup_or_domain 500 501 dp.add_element(:http_downloads_enabled.to_s).text = @http_downloads_enabled 502 dp.add_element(:protocol.to_s).text = @protocol 503 dp.add_element(:port.to_s).text = @port 504 dp.add_element(:context.to_s).text = @context 505 dp.add_element(:no_authentication_required.to_s).text = @no_authentication_required 506 dp.add_element(:certificate_required.to_s).text = @certificate_required 507 dp.add_element(:username_password_required.to_s).text = @username_password_required 508 dp.add_element(:http_username.to_s).text = @http_username 509 dp.add_element(:certificate.to_s).text = @certificate 510 dp.add_element(:http_url.to_s).text = @http_url 511 dp.add_element(:failover_point_url.to_s).text = @failover_point_url 512 513 dp.add_element(:ssh_username.to_s).text = @ssh_username 514 dp.add_element(:ssh_password.to_s).text = @ssh_password if @ssh_password 515 516 doc.to_s 517 end