Class: Jamf::NetworkSegment

Inherits:
APIObject show all
Includes:
Comparable, Creatable, Updatable
Defined in:
lib/jamf/api/classic/api_objects/network_segment.rb

Overview

A Network Segment in the JSS

Constant Summary collapse

RSRC_BASE =

the REST resource base

'networksegments'.freeze
RSRC_LIST_KEY =

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

:network_segments
RSRC_OBJECT_KEY =

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

:network_segment
OBJECT_HISTORY_OBJECT_TYPE =

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

43

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**args) ⇒ NetworkSegment

Instantiate a NetworkSegment

addresses can be provided when using id: :new



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 391

def initialize(**args)
  super

  if args[:id] == :new
    range = self.class.ip_range(
      starting_address: args[:starting_address],
      ending_address: args[:ending_address],
      mask: args[:mask],
      cidr: args[:cidr]
    )
    @init_data[:starting_address] = range.begin.to_s
    @init_data[:ending_address] = range.end.to_s
  end

  @starting_address = IPAddr.new @init_data[:starting_address]
  @ending_address = IPAddr.new @init_data[:ending_address]

  @building = @init_data[:building]
  @department = @init_data[:department]
  @distribution_point = @init_data[:distribution_point]
  @netboot_server = @init_data[:netboot_server]
  @override_buildings = @init_data[:override_buildings]
  @override_departments = @init_data[:override_departments]
  @swu_server = @init_data[:swu_server]
  @url = @init_data[:url]
end

Instance Attribute Details

#buildingString

Returns building for this segment. Must be one of the buildings in the JSS.

Returns:

  • (String)

    building for this segment. Must be one of the buildings in the JSS



363
364
365
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 363

def building
  @building
end

#departmentString

Returns department for this segment. Must be one of the depts in the JSS.

Returns:

  • (String)

    department for this segment. Must be one of the depts in the JSS



366
367
368
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 366

def department
  @department
end

#distribution_pointString

Returns the name of the distribution point to be used from this network segment.

Returns:

  • (String)

    the name of the distribution point to be used from this network segment



369
370
371
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 369

def distribution_point
  @distribution_point
end

#ending_addressIPAddr

Returns ending IP adresss.

Returns:

  • (IPAddr)

    ending IP adresss



360
361
362
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 360

def ending_address
  @ending_address
end

#need_to_updateBoolean (readonly) Originally defined in module Updatable

Returns do we have unsaved changes?.

Returns:

  • (Boolean)

    do we have unsaved changes?

#netboot_serverString

Returns the netboot server for this segment.

Returns:

  • (String)

    the netboot server for this segment



375
376
377
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 375

def netboot_server
  @netboot_server
end

#override_buildingsBoolean

Returns should machines checking in from this segment update their building.

Returns:

  • (Boolean)

    should machines checking in from this segment update their building



384
385
386
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 384

def override_buildings
  @override_buildings
end

#override_departmentsBoolean

Returns should machines checking in from this segment update their dept.

Returns:

  • (Boolean)

    should machines checking in from this segment update their dept



381
382
383
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 381

def override_departments
  @override_departments
end

#starting_addressIPAddr

Returns starting IP adresss.

Returns:

  • (IPAddr)

    starting IP adresss



357
358
359
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 357

def starting_address
  @starting_address
end

#swu_serverString

Returns the swupdate server for this segment.

Returns:

  • (String)

    the swupdate server for this segment.



378
379
380
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 378

def swu_server
  @swu_server
end

#urlString (readonly)

Returns the mount url for the distribution point.

Returns:

  • (String)

    the mount url for the distribution point



372
373
374
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 372

def url
  @url
end

Class Method Details

.ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil) ⇒ Range<IPAddr>

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.

Parameters:

  • starting_address (String) (defaults to: nil)

    The starting address, possibly masked

  • ending_address (String) (defaults to: nil)

    The ending address. If given, it overrides mask:, cidr: and a masked starting_address:

  • mask (String) (defaults to: nil)

    The subnet mask to apply to the starting address to get the ending address

  • cidr (String, Integer) (defaults to: nil)

    he cidr value to apply to the starting address to get the ending address

Returns:

  • (Range<IPAddr>)

    the valid Range

Raises:



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 189

def self.ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil)
  raise Jamf::MissingDataError, 'starting_address: must be provided' unless starting_address

  starting_address = masked_starting_address(starting_address: starting_address, mask: mask, cidr: cidr)

  if ending_address
    startip = IPAddr.new starting_address.split('/').first
    endip = IPAddr.new ending_address.to_s
    validate_ip_range(startip, endip)
  else
    raise ArgumentError, 'Must provide ending_address:, mask:, cidr: or a masked starting_address:' unless starting_address.include? '/'
    subnet = IPAddr.new starting_address
    startip = subnet.to_range.first.mask 32
    endip = subnet.to_range.last.mask 32
  end

  startip..endip
end

.ip_range_width(ip1, ip2) ⇒ Object

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

Raises:

  • (ArgumentError)


308
309
310
311
312
313
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 308

def self.ip_range_width(ip1, ip2)
  raise ArgumentError, 'Parameters must be IPAddr objects' unless ip1.is_a?(IPAddr) && ip2.is_a?(IPAddr)

  low, high = [ip1, ip2].sort
  high.to_i - low.to_i
end

.masked_starting_address(starting_address: nil, mask: nil, cidr: nil) ⇒ String

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

Parameters:

  • starting (String)

    The starting address, possibly masked

  • mask (String) (defaults to: nil)

    The subnet mask to apply to the starting address to get the ending address

  • cidr (String, Integer) (defaults to: nil)

    he cidr value to apply to the starting address to get the ending address

Returns:

  • (String)

    the starting with the mask or cidr appended



220
221
222
223
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 220

def self.masked_starting_address(starting_address: nil, mask: nil, cidr: nil)
  starting_address = "#{starting}/#{mask || cidr}" if mask || cidr
  starting_address.to_s
end

.my_network_segment(refresh = false, name: false, api: nil, cnx: Jamf.cnx) ⇒ Integer, ...

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

Parameters:

  • name (Boolean) (defaults to: false)

    return the name of the netsegment, not the id

Returns:

  • (Integer, String, nil)

    the id of the current net segment, or nil



341
342
343
344
345
346
347
348
349
350
351
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 341

def self.my_network_segment(refresh = false, name: false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  my_ip = Jamf::Client.my_ip_address
  return nil unless my_ip

  id = network_segment_for_ip(my_ip, refresh: refresh, cnx: cnx)
  return id unless name

  map_all_ids_to(:name, cnx: cnx)[id]
end

.my_network_segments(refresh = false, names: false, api: nil, cnx: Jamf.cnx) ⇒ Array<Integer>, Array<String>

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.

Parameters:

  • names (Boolean) (defaults to: false)

    the array will contain Network Segment names, not ids

Returns:

  • (Array<Integer>, Array<String>)

    the NetworkSegment ids or names for this machine right now.



323
324
325
326
327
328
329
330
331
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 323

def self.my_network_segments(refresh = false, names: false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  ids = network_segments_for_ip Jamf::Client.my_ip_address, refresh, cnx: cnx
  return ids unless names

  ids_to_names = map_all_ids_to :name, cnx: cnx
  ids.map { |id| ids_to_names[id] }
end

.network_ranges(refresh = false, api: nil, cnx: Jamf.cnx) ⇒ Hash{Integer => Range}

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.

Parameters:

  • refresh (Boolean) (defaults to: false)

    should the data be re-queried?

  • cnx (Jamf::Connection) (defaults to: Jamf.cnx)

    the API to query

Returns:

  • (Hash{Integer => Range})

    the network segments as IPv4 address Ranges keyed by id



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 95

def self.network_ranges(refresh = false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  @network_ranges = nil if refresh
  return @network_ranges if @network_ranges

  @network_ranges = {}
  all(refresh, cnx: cnx).each do |ns|
    @network_ranges[ns[:id]] = IPAddr.new(ns[:starting_address])..IPAddr.new(ns[:ending_address])
  end
  @network_ranges
end

.network_ranges_as_integers(refresh = false, api: nil, cnx: Jamf.cnx) ⇒ Hash{Integer => Range}

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

Parameters:

  • refresh (Boolean) (defaults to: false)

    should the data be re-queried?

  • cnx (Jamf::Connection) (defaults to: Jamf.cnx)

    the APIConnection to query

Returns:

  • (Hash{Integer => Range})

    the network segments as Integer Ranges keyed by id



125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 125

def self.network_ranges_as_integers(refresh = false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  @network_ranges_as_integers = nil if refresh
  return @network_ranges_as_integers if @network_ranges_as_integers

  @network_ranges_as_integers = {}
  all(refresh, cnx: cnx).each do |ns|
    first = IPAddr.new(ns[:starting_address]).to_i
    last = IPAddr.new(ns[:ending_address]).to_i
    @network_ranges_as_integers[ns[:id]] = first..last
  end
  @network_ranges_as_integers
end

.network_segment_for_ip(ipaddr, refresh: false, api: nil, cnx: Jamf.cnx) ⇒ Integer?

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

Returns:

  • (Integer, nil)

    the id of the current net segment, or nil



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 273

def self.network_segment_for_ip(ipaddr, refresh: false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  # get the ip as a 32bit interger
  ip = IPAddr.new(ipaddr.to_s).to_i
  # a hash of NetSeg ids => Range<Integer>
  ranges = network_ranges_as_integers(refresh, cnx: cnx).select { |_id, range| range.include? ip }

  # we got nuttin
  return nil if ranges.empty?

  # if we got only one, its the one
  return ranges.keys.first if ranges.size == 1

  # got more than one, sort by range size/width, asc.
  sorted_by_size = ranges.sort_by { |_i, r| r.size }.to_h

  # the first one is the smallest/narrowest.
  _smallest_range_id, smallest_range = sorted_by_size.first

  smallest_range_size = smallest_range.size

  # select all of them that are the same size
  all_of_small_size = sorted_by_size.select { |_i, r| r.size == smallest_range_size }

  # sort them by the start of each range (r.first)
  # and return the lowest start (returned by min_by)
  my_range_id, _my_range = all_of_small_size.min_by { |_i, r| r.first }

  # and return the id
  my_range_id
end

.network_segments_for_ip(ipaddr, refresh = false, api: nil, cnx: Jamf.cnx) ⇒ Array<Integer>

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.

Parameters:

  • ip (String, IPAddr)

    the IP address to locate

  • refresh (Boolean) (defaults to: false)

    should the data be re-queried?

  • cnx (Jamf::Connection) (defaults to: Jamf.cnx)

    The API connection to query

Returns:

  • (Array<Integer>)

    the ids of the NetworkSegments containing the given ip



253
254
255
256
257
258
259
260
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 253

def self.network_segments_for_ip(ipaddr, refresh = false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  # get the ip as a 32bit interger
  ip = IPAddr.new(ipaddr.to_s).to_i
  # a hash of NetSeg ids => Range<Integer>
  network_ranges_as_integers(refresh, cnx: cnx).select { |_id, range| range.include? ip }.keys
end

.subnets(refresh = false, api: nil, cnx: Jamf.cnx) ⇒ Object

An alias for network_ranges

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

See Also:

  • {NetworkSegment{NetworkSegment::network_ranges}


146
147
148
149
150
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 146

def self.subnets(refresh = false, api: nil, cnx: Jamf.cnx)
  cnx = api if api

  network_ranges refresh, cnx: cnx
end

.validate_ip_range(startip, endip) ⇒ void

This method returns an undefined value.

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

Parameters:

  • startip (String)

    The starting ip

  • endip (String)

    The ending ip

Raises:



233
234
235
236
237
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 233

def self.validate_ip_range(startip, endip)
  return nil if IPAddr.new(startip.to_s) <= IPAddr.new(endip.to_s)

  raise Jamf::InvalidDataError, "Starting IP #{startip} is higher than ending ip #{endip} "
end

Instance Method Details

#==(other) ⇒ Boolean

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

Parameters:

Returns:

  • (Boolean)

    Does this segment include the other?

Raises:

  • (TypeError)


464
465
466
467
468
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 464

def ==(other)
  raise TypeError, 'Argument must be a Jamf::NetworkSegment' unless \
    other.is_a? Jamf::NetworkSegment
  range == other.range
end

#cidr=(newval) ⇒ void Also known as: mask=

This method returns an undefined value.

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

Parameters:

  • newval (String, Integer)

    the new cidr or mask



630
631
632
633
634
635
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 630

def cidr=(newval)
  new_end = IPAddr.new("#{@starting_address}/#{newval}").to_range.end.mask 32
  self.class.validate_ip_range(@starting_address, new_end)
  @ending_address = new_end
  @need_to_update = true
end

#clone(new_name, api: nil, cnx: nil) ⇒ APIObject Originally defined in module Creatable

make a clone of this API object, with a new name. The class must be creatable

Parameters:

  • name (String)

    the name for the new object

  • cnx (Jamf::Connection) (defaults to: nil)

    the API in which to create the object Defaults to the API used to instantiate this object

Returns:

  • (APIObject)

    An unsaved clone of this APIObject with the given name

Raises:

#include?(thing) ⇒ Boolean Also known as: cover?

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

Parameters:

Returns:

  • (Boolean)

    Does this segment include the other?



447
448
449
450
451
452
453
454
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 447

def include?(thing)
  if thing.is_a? Jamf::NetworkSegment
    @starting_address <= thing.range.begin && @ending_address >= thing.range.end
  else
    thing = IPAddr.new thing.to_s
    range.cover? thing
  end
end

#name=(newname) ⇒ void Originally defined in module Updatable

This method returns an undefined value.

Change the name of this item Remember to #update to push changes to the server.

Parameters:

  • newname (String)

    the new name

Raises:

#overlap?(other_segment) ⇒ Boolean

Does this network segment overlap with another?

Parameters:

Returns:

  • (Boolean)

    Does the other segment overlap this one?

Raises:

  • (TypeError)


433
434
435
436
437
438
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 433

def overlap?(other_segment)
  raise TypeError, 'Argument must be a Jamf::NetworkSegment' unless \
    other_segment.is_a? Jamf::NetworkSegment
  other_range = other_segment.range
  range.include?(other_range.begin) || range.include?(other_range.end)
end

#rangeRange<IPAddr> Also known as: to_range

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

Returns:

  • (Range<IPAddr>)

    the range of IPAddrs for this segment.



423
424
425
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 423

def range
  @starting_address..@ending_address
end

#set_ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil) ⇒ void

This method returns an undefined value.

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

and ending addresses.

Parameters:

  • starting_address (String) (defaults to: nil)

    The starting address, possibly masked

  • ending_address (String) (defaults to: nil)

    The ending address

  • mask (String) (defaults to: nil)

    The subnet mask to apply to the starting address to get the ending address

  • cidr (String, Integer) (defaults to: nil)

    he cidr value to apply to the starting address to get the ending address



654
655
656
657
658
659
660
661
662
663
664
# File 'lib/jamf/api/classic/api_objects/network_segment.rb', line 654

def set_ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil)
  range = self.class.ip_range(
    starting_address: starting_address,
    ending_address: ending_address,
    mask: mask,
    cidr: cidr
  )
  @starting_address = range.first
  @ending_address = range.last
  @need_to_update = true
end