class Jamf::NetworkSegment

A Network Segment in the JSS

Constants

OBJECT_HISTORY_OBJECT_TYPE

the object type for this object in the object history table. See {APIObject#add_object_history_entry}

RSRC_BASE

the REST resource base

RSRC_LIST_KEY

the hash key used for the JSON list output of all objects in the JSS

RSRC_OBJECT_KEY

The hash key used for the JSON object output. It’s also used in various error messages

Attributes

building[R]

@return [String] building for this segment. Must be one of the buildings in the JSS

department[R]

@return [String] department for this segment. Must be one of the depts in the JSS

distribution_point[R]

@return [String] the name of the distribution point to be used from this network segment

ending_address[R]

@return [IPAddr] ending IP adresss

netboot_server[R]

@return [String] the netboot server for this segment

override_buildings[R]

@return [Boolean] should machines checking in from this segment update their building

override_departments[R]

@return [Boolean] should machines checking in from this segment update their dept

starting_address[R]

@return [IPAddr] starting IP adresss

swu_server[R]

@return [String] the swupdate server for this segment.

url[R]

@return [String] the mount url for the distribution point

Public Class Methods

ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil) click to toggle source

Given a starting address & ending address, mask, or cidr, return a Range object of IPAddr objects.

starting_address: must be provided, and may be a masked address, in which case nothing else is needed.

If starting_address: is an unmasked address, then one of ending_address: cidr: or mask: must be provided.

If given, ending_address: overrides mask:, cidr:, and a masked starting_address:

These give the same result:

ip_range starting_address: ‘192.168.1.0’, ending_address: ‘192.168.1.255’ ip_range starting_address: ‘192.168.1.0’, mask: ‘255.255.255.0’ ip_range starting_address: ‘192.168.1.0’, cidr: 24 ip_range starting_address: ‘192.168.1.0/24’ ip_range starting_address: ‘192.168.1.0/255.255.255.0’

All the above will produce:

#<IPAddr: IPv4:192.168.1.0/255.255.255.255>..#<IPAddr: IPv4:192.168.1.255/255.255.255.255>

An exception is raised if the starting address is above the ending address.

@param starting_address The starting address, possibly masked

@param ending_address The ending address. If given, it overrides mask:,

cidr: and a masked starting_address:

@param mask The subnet mask to apply to the starting address to get

the ending address

@param cidr[String, Integer] he cidr value to apply to the starting address to get

the ending address

@return [Range<IPAddr>] the valid Range

    # File lib/jamf/api/classic/api_objects/network_segment.rb
189 def self.ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil)
190   raise Jamf::MissingDataError, 'starting_address: must be provided' unless starting_address
191 
192   starting_address = masked_starting_address(starting_address: starting_address, mask: mask, cidr: cidr)
193 
194   if ending_address
195     startip = IPAddr.new starting_address.split('/').first
196     endip = IPAddr.new ending_address.to_s
197     validate_ip_range(startip, endip)
198   else
199     raise ArgumentError, 'Must provide ending_address:, mask:, cidr: or a masked starting_address:' unless starting_address.include? '/'
200     subnet = IPAddr.new starting_address
201     startip = subnet.to_range.first.mask 32
202     endip = subnet.to_range.last.mask 32
203   end
204 
205   startip..endip
206 end
ip_range_width(ip1, ip2) click to toggle source

given 2 IPAddr instances, find out how ‘wide’ they are - how many IP addresses exist between them.

    # File lib/jamf/api/classic/api_objects/network_segment.rb
308 def self.ip_range_width(ip1, ip2)
309   raise ArgumentError, 'Parameters must be IPAddr objects' unless ip1.is_a?(IPAddr) && ip2.is_a?(IPAddr)
310 
311   low, high = [ip1, ip2].sort
312   high.to_i - low.to_i
313 end
masked_starting_address(starting_address: nil, mask: nil, cidr: nil) click to toggle source

If we are given a mask or cidr, append them to the starting_address

@param starting The starting address, possibly masked

@param mask The subnet mask to apply to the starting address to get

the ending address

@param cidr[String, Integer] he cidr value to apply to the starting address to get

the ending address

@return [String] the starting with the mask or cidr appended

    # File lib/jamf/api/classic/api_objects/network_segment.rb
220 def self.masked_starting_address(starting_address: nil, mask: nil, cidr: nil)
221   starting_address = "#{starting}/#{mask || cidr}" if mask || cidr
222   starting_address.to_s
223 end
my_network_segment(refresh = false, name: false, api: nil, cnx: Jamf.cnx) click to toggle source

Which network segment is seen as current? According to the Jamf Pro Admin Guide, the ‘smallest’ one - the one with fewest IP addrs within it. If multiple ones have the same number of IPs, then its the one with the lowest starting address

@param name [Boolean] return the name of the netsegment, not the id

@return [Integer, String, nil] the id of the current net segment, or nil

    # File lib/jamf/api/classic/api_objects/network_segment.rb
341 def self.my_network_segment(refresh = false, name: false, api: nil, cnx: Jamf.cnx)
342   cnx = api if api
343 
344   my_ip = Jamf::Client.my_ip_address
345   return nil unless my_ip
346 
347   id = network_segment_for_ip(my_ip, refresh: refresh, cnx: cnx)
348   return id unless name
349 
350   map_all_ids_to(:name, cnx: cnx)[id]
351 end
my_network_segments(refresh = false, names: false, api: nil, cnx: Jamf.cnx) click to toggle source

Find the current network segment ids for the machine running this code

See my_network_segment to get the current one according to the server.

@param names [Boolean] the array will contain Network Segment names, not ids

@return [Array<Integer>,Array<String>] the NetworkSegment ids or names for this machine right now.

    # File lib/jamf/api/classic/api_objects/network_segment.rb
323 def self.my_network_segments(refresh = false, names: false, api: nil, cnx: Jamf.cnx)
324   cnx = api if api
325 
326   ids = network_segments_for_ip Jamf::Client.my_ip_address, refresh, cnx: cnx
327   return ids unless names
328 
329   ids_to_names = map_all_ids_to :name, cnx: cnx
330   ids.map { |id| ids_to_names[id] }
331 end
network_ranges(refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

All NetworkSegments in the given API as ruby Ranges of IPAddr instances representing the Segment, e.g. with starting = 10.24.9.1 and ending = 10.24.15.254 the range looks like:

<IPAddr: IPv4:10.24.9.1/255.255.255.255>
 ..
<IPAddr: IPv4:10.24.15.254/255.255.255.255>

Using the include? method on those Ranges is very useful.

Note1: We don’t use the IPAddr#to_range method because that works

best for masked IPAddrs (which are ranges of IPs with widths
determined by the mask) and Jamf Network Segments can have arbitrary
widths.

Note2: See the network_ranges_as_integers method below, which is similar

but much faster.

@param refresh should the data be re-queried?

@param cnx [Jamf::Connection] the API to query

@return [Hash{Integer => Range}] the network segments as IPv4 address Ranges

keyed by id
    # File lib/jamf/api/classic/api_objects/network_segment.rb
 95 def self.network_ranges(refresh = false, api: nil, cnx: Jamf.cnx)
 96   cnx = api if api
 97 
 98   @network_ranges = nil if refresh
 99   return @network_ranges if @network_ranges
100 
101   @network_ranges = {}
102   all(refresh, cnx: cnx).each do |ns|
103     @network_ranges[ns[:id]] = IPAddr.new(ns[:starting_address])..IPAddr.new(ns[:ending_address])
104   end
105   @network_ranges
106 end
network_ranges_as_integers(refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

An IPv4 Address is really just a 32-bit integer, displayed as four 8-bit integers. e.g. ‘10.0.69.1’ is really the integer 167789825 The to_i method of IPAddr objects returns that integer (or the first of them if the IPAddr is masked).

Using ranges made of those integers is far faster than using ranges if IPAddr objects, so that’s what this method returns.

See also: the network_ranges method above

@param refresh should the data be re-queried?

@param cnx [Jamf::Connection] the APIConnection to query

@return [Hash{Integer => Range}] the network segments as Integer Ranges

keyed by id
    # File lib/jamf/api/classic/api_objects/network_segment.rb
125 def self.network_ranges_as_integers(refresh = false, api: nil, cnx: Jamf.cnx)
126   cnx = api if api
127 
128   @network_ranges_as_integers = nil if refresh
129   return @network_ranges_as_integers if @network_ranges_as_integers
130 
131   @network_ranges_as_integers = {}
132   all(refresh, cnx: cnx).each do |ns|
133     first = IPAddr.new(ns[:starting_address]).to_i
134     last = IPAddr.new(ns[:ending_address]).to_i
135     @network_ranges_as_integers[ns[:id]] = first..last
136   end
137   @network_ranges_as_integers
138 end
network_segment_for_ip(ipaddr, refresh: false, api: nil, cnx: Jamf.cnx) click to toggle source

Which network segment is seen as current for a given IP addr?

According to the Jamf Pro Admin Guide, if an IP is in more than one network segment, it uses the ‘smallest’ (narrowest) one - the one with fewest IP addrs within it.

If multiple ones have the same width, then it uses the one of those with the lowest starting address

@return [Integer, nil] the id of the current net segment, or nil

    # File lib/jamf/api/classic/api_objects/network_segment.rb
273 def self.network_segment_for_ip(ipaddr, refresh: false, api: nil, cnx: Jamf.cnx)
274   cnx = api if api
275 
276   # get the ip as a 32bit interger
277   ip = IPAddr.new(ipaddr.to_s).to_i
278   # a hash of NetSeg ids => Range<Integer>
279   ranges = network_ranges_as_integers(refresh, cnx: cnx).select { |_id, range| range.include? ip }
280 
281   # we got nuttin
282   return nil if ranges.empty?
283 
284   # if we got only one, its the one
285   return ranges.keys.first if ranges.size == 1
286 
287   # got more than one, sort by range size/width, asc.
288   sorted_by_size = ranges.sort_by { |_i, r| r.size }.to_h
289 
290   # the first one is the smallest/narrowest.
291   _smallest_range_id, smallest_range = sorted_by_size.first
292 
293   smallest_range_size = smallest_range.size
294 
295   # select all of them that are the same size
296   all_of_small_size = sorted_by_size.select { |_i, r| r.size == smallest_range_size }
297 
298   # sort them by the start of each range (r.first)
299   # and return the lowest start (returned by min_by)
300   my_range_id, _my_range = all_of_small_size.min_by { |_i, r| r.first }
301 
302   # and return the id
303   my_range_id
304 end
network_segments_for_ip(ipaddr, refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

Find the ids of the network segments that contain a given IP address.

Even tho IPAddr.include? will take a String or an IPAddr I convert the ip to an IPAddr so that an exception will be raised if the ip isn’t a valid ip.

@param ip[String, IPAddr] the IP address to locate

@param refresh should the data be re-queried?

@param cnx [Jamf::Connection] The API connection to query

@return [Array<Integer>] the ids of the NetworkSegments containing the given ip

    # File lib/jamf/api/classic/api_objects/network_segment.rb
253 def self.network_segments_for_ip(ipaddr, refresh = false, api: nil, cnx: Jamf.cnx)
254   cnx = api if api
255 
256   # get the ip as a 32bit interger
257   ip = IPAddr.new(ipaddr.to_s).to_i
258   # a hash of NetSeg ids => Range<Integer>
259   network_ranges_as_integers(refresh, cnx: cnx).select { |_id, range| range.include? ip }.keys
260 end
new(**args) click to toggle source

Instantiate a NetworkSegment

@see_also Jamf::NetworkSegment.ip_range for how starting and ending addresses can be provided when using id: :new

Calls superclass method Jamf::APIObject::new
    # File lib/jamf/api/classic/api_objects/network_segment.rb
391 def initialize(**args)
392   super
393 
394   if args[:id] == :new
395     range = self.class.ip_range(
396       starting_address: args[:starting_address],
397       ending_address: args[:ending_address],
398       mask: args[:mask],
399       cidr: args[:cidr]
400     )
401     @init_data[:starting_address] = range.begin.to_s
402     @init_data[:ending_address] = range.end.to_s
403   end
404 
405   @starting_address = IPAddr.new @init_data[:starting_address]
406   @ending_address = IPAddr.new @init_data[:ending_address]
407 
408   @building = @init_data[:building]
409   @department = @init_data[:department]
410   @distribution_point = @init_data[:distribution_point]
411   @netboot_server = @init_data[:netboot_server]
412   @override_buildings = @init_data[:override_buildings]
413   @override_departments = @init_data[:override_departments]
414   @swu_server = @init_data[:swu_server]
415   @url = @init_data[:url]
416 end
subnets(refresh = false, api: nil, cnx: Jamf.cnx) click to toggle source

An alias for {NetworkSegment.network_ranges}

DEPRECATED: This will be going away in a future release.

@see {NetworkSegment::network_ranges}

    # File lib/jamf/api/classic/api_objects/network_segment.rb
146 def self.subnets(refresh = false, api: nil, cnx: Jamf.cnx)
147   cnx = api if api
148 
149   network_ranges refresh, cnx: cnx
150 end
validate_ip_range(startip, endip) click to toggle source

Raise an exception if a given starting ip is higher than a given ending ip

@param startip The starting ip

@param endip The ending ip

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
233 def self.validate_ip_range(startip, endip)
234   return nil if IPAddr.new(startip.to_s) <= IPAddr.new(endip.to_s)
235 
236   raise Jamf::InvalidDataError, "Starting IP #{startip} is higher than ending ip #{endip} "
237 end

Public Instance Methods

==(other) click to toggle source

Does this network segment equal another? equality means the ranges are equal

@param other_segment the other segment to check

@return [Boolean] Does this segment include the other?

    # File lib/jamf/api/classic/api_objects/network_segment.rb
464 def ==(other)
465   raise TypeError, 'Argument must be a Jamf::NetworkSegment' unless \
466     other.is_a? Jamf::NetworkSegment
467   range == other.range
468 end
building=(newval) click to toggle source

Set the building

@param newval[String, Integer] the new building by name or id, must be in the JSS

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
476 def building=(newval)
477   new =
478     if newval.to_s.empty?
479       Jamf::BLANK
480     else
481       id = Jamf::Building.valid_id newval
482       raise Jamf::MissingDataError, "No building matching '#{newval}'" unless id
483 
484       Jamf::Building.map_all_ids_to(:name)[id]
485     end
486 
487   @building = new
488   @need_to_update = true
489 end
cidr=(newval) click to toggle source

set the ending address by applying a new cidr (e.g. 24) or mask (e.g. 255.255.255.0)

@param newval[String, Integer] the new cidr or mask

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
630 def cidr=(newval)
631   new_end = IPAddr.new("#{@starting_address}/#{newval}").to_range.end.mask 32
632   self.class.validate_ip_range(@starting_address, new_end)
633   @ending_address = new_end
634   @need_to_update = true
635 end
Also aliased as: mask=
cover?(thing)
Alias for: include?
department=(newval) click to toggle source

set the department

@param newval[String, Integer] the new dept by name or id, must be in the JSS

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
509 def department=(newval)
510   new =
511     if newval.to_s.empty?
512       Jamf::BLANK
513     else
514       id = Jamf::Department.valid_id newval
515       raise Jamf::MissingDataError , "No department matching '#{newval}' in the JSS" unless id
516 
517       Jamf::Department.map_all_ids_to(:name)[id]
518     end
519   @department = new
520   @need_to_update = true
521 end
distribution_point=(newval) click to toggle source

set the distribution_point

@param newval[String, Integer, nil] the new dist. point by name or id, must be in the JSS, or nil or blank to unset

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
542 def distribution_point=(newval)
543   new =
544     if newval.to_s.empty?
545       Jamf::BLANK
546     else
547       id = Jamf::DistributionPoint.valid_id newval
548       raise Jamf::MissingDataError, "No distribution_point matching '#{newval}' in the JSS" unless id
549 
550       Jamf::DistributionPoint.map_all_ids_to(:name)[id]
551     end
552 
553   @distribution_point = new
554   @need_to_update = true
555 end
ending_address=(newval) click to toggle source

set the ending address

@param newval[String, IPAddr] the new ending address

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
617 def ending_address=(newval)
618   self.class.validate_ip_range(@starting_address, newval)
619   @ending_address = IPAddr.new newval.to_s
620   @need_to_update = true
621 end
include?(thing) click to toggle source

Does this network segment include an address or another segment? Inclusion means the other is completely inside this one.

@param thing[Jamf::NetworkSegment, String, IPAddr] the other thing to check

@return [Boolean] Does this segment include the other?

    # File lib/jamf/api/classic/api_objects/network_segment.rb
447 def include?(thing)
448   if thing.is_a? Jamf::NetworkSegment
449     @starting_address <= thing.range.begin && @ending_address >= thing.range.end
450   else
451     thing = IPAddr.new thing.to_s
452     range.cover? thing
453   end
454 end
Also aliased as: cover?
mask=(newval)

aliases

Alias for: cidr=
netboot_server=(newval) click to toggle source

set the netboot_server

@param newval[String, Integer] the new netboot server by name or id, must be in the JSS

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
563 def netboot_server=(newval)
564   new =
565     if newval.to_s.empty?
566       Jamf::BLANK
567     else
568       id = Jamf::NetBootServer.valid_id newval
569       raise Jamf::MissingDataError, "No netboot_server matching '#{newval}' in the JSS" unless id
570 
571       Jamf::NetbootServer.map_all_ids_to(:name)[id]
572     end
573 
574   @netboot_server = new
575   @need_to_update = true
576 end
overlap?(other_segment) click to toggle source

Does this network segment overlap with another?

@param other_segment the other segment to check

@return [Boolean] Does the other segment overlap this one?

    # File lib/jamf/api/classic/api_objects/network_segment.rb
433 def overlap?(other_segment)
434   raise TypeError, 'Argument must be a Jamf::NetworkSegment' unless \
435     other_segment.is_a? Jamf::NetworkSegment
436   other_range = other_segment.range
437   range.include?(other_range.begin) || range.include?(other_range.end)
438 end
override_buildings=(newval) click to toggle source

set the override buildings option

@param newval the new override buildings option

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
497 def override_buildings=(newval)
498   raise Jamf::InvalidDataError, 'New value must be boolean true or false' unless Jamf::TRUE_FALSE.include? newval
499   @override_buildings = newval
500   @need_to_update = true
501 end
override_departments=(newval) click to toggle source

set the override depts option

@param newval the new setting

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
530 def override_departments=(newval)
531   raise Jamf::InvalidDataError, 'New value must be boolean true or false' unless Jamf::TRUE_FALSE.include? newval
532   @override_departments = newval
533   @need_to_update = true
534 end
range() click to toggle source

a Range built from the start and end addresses. To be used for finding inclusion and overlaps.

@return [Range<IPAddr>] the range of IPAddrs for this segment.

    # File lib/jamf/api/classic/api_objects/network_segment.rb
423 def range
424   @starting_address..@ending_address
425 end
Also aliased as: to_range
set_ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil) click to toggle source

set a new starting and ending addr at the same time.

@see_also NetworkSegment.ip_range for how to specify the starting and ending addresses.

@param starting_address The starting address, possibly masked

@param ending_address The ending address

@param mask The subnet mask to apply to the starting address to get

the ending address

@param cidr[String, Integer] he cidr value to apply to the starting address to get

the ending address

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
654 def set_ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil)
655   range = self.class.ip_range(
656     starting_address: starting_address,
657     ending_address: ending_address,
658     mask: mask,
659     cidr: cidr
660   )
661   @starting_address = range.first
662   @ending_address = range.last
663   @need_to_update = true
664 end
starting_address=(newval) click to toggle source

set the starting address

@param newval[String, IPAddr] the new starting address

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
605 def starting_address=(newval)
606   self.class.validate_ip_range(newval, @ending_address)
607   @starting_address = IPAddr.new newval.to_s
608   @need_to_update = true
609 end
swu_server=(newval) click to toggle source

set the sw update server

@param newval[String, Integer] the new server by name or id, must be in the JSS

@return [void]

    # File lib/jamf/api/classic/api_objects/network_segment.rb
584 def swu_server=(newval)
585   new =
586     if newval.to_s.empty?
587       Jamf::BLANK
588     else
589       id = Jamf::SoftwareUpdateServer.valid_id newval
590       raise Jamf::MissingDataError, "No swu_server matching '#{newval}' in the JSS" unless id
591 
592       Jamf::SoftwareUpdateServer.map_all_ids_to(:name)[id]
593     end
594 
595   @swu_server = new
596   @need_to_update = true
597 end
to_range()
Alias for: range

Private Instance Methods

rest_xml() click to toggle source

the xml formated data for adding or updating this in the JSS

    # File lib/jamf/api/classic/api_objects/network_segment.rb
677 def rest_xml
678   doc = REXML::Document.new Jamf::Connection::XML_HEADER
679   ns = doc.add_element 'network_segment'
680   ns.add_element('building').text = @building
681   ns.add_element('department').text = @department
682   ns.add_element('distribution_point').text = @distribution_point
683   ns.add_element('ending_address').text = @ending_address.to_s
684   ns.add_element('name').text = @name
685   ns.add_element('netboot_server').text = @netboot_server
686   ns.add_element('override_buildings').text = @override_buildings
687   ns.add_element('override_departments').text = @override_departments
688   ns.add_element('starting_address').text = @starting_address.to_s
689   ns.add_element('swu_server').text = @swu_server
690   doc.to_s
691 end