Class: JSS::NetworkSegment

Inherits:
APIObject show all
Includes:
Comparable, Creatable, Updatable
Defined in:
lib/jss/api_object/network_segment.rb,
lib/jss.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


377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/jss/api_object/network_segment.rb', line 377

def initialize(args = {})
  super args

  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


349
350
351
# File 'lib/jss/api_object/network_segment.rb', line 349

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


352
353
354
# File 'lib/jss/api_object/network_segment.rb', line 352

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


355
356
357
# File 'lib/jss/api_object/network_segment.rb', line 355

def distribution_point
  @distribution_point
end

#ending_addressIPAddr

Returns ending IP adresss.

Returns:

  • (IPAddr)

    ending IP adresss


346
347
348
# File 'lib/jss/api_object/network_segment.rb', line 346

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


361
362
363
# File 'lib/jss/api_object/network_segment.rb', line 361

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


370
371
372
# File 'lib/jss/api_object/network_segment.rb', line 370

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


367
368
369
# File 'lib/jss/api_object/network_segment.rb', line 367

def override_departments
  @override_departments
end

#starting_addressIPAddr

Returns starting IP adresss.

Returns:

  • (IPAddr)

    starting IP adresss


343
344
345
# File 'lib/jss/api_object/network_segment.rb', line 343

def starting_address
  @starting_address
end

#swu_serverString

Returns the swupdate server for this segment.

Returns:

  • (String)

    the swupdate server for this segment.


364
365
366
# File 'lib/jss/api_object/network_segment.rb', line 364

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


358
359
360
# File 'lib/jss/api_object/network_segment.rb', line 358

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:


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/jss/api_object/network_segment.rb', line 183

def self.ip_range(starting_address: nil, ending_address: nil, mask: nil, cidr: nil)
  raise JSS::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)

298
299
300
301
302
303
# File 'lib/jss/api_object/network_segment.rb', line 298

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


214
215
216
217
# File 'lib/jss/api_object/network_segment.rb', line 214

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: JSS.api) ⇒ 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


329
330
331
332
333
334
335
336
337
# File 'lib/jss/api_object/network_segment.rb', line 329

def self.my_network_segment(refresh = false, name: false, api: JSS.api)
  my_ip = JSS::Client.my_ip_address
  return nil unless my_ip

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

  map_all_ids_to(:name)[id]
end

.my_network_segments(refresh = false, names: false, api: JSS.api) ⇒ 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.


313
314
315
316
317
318
319
# File 'lib/jss/api_object/network_segment.rb', line 313

def self.my_network_segments(refresh = false, names: false, api: JSS.api)
  ids = network_segments_for_ip JSS::Client.my_ip_address, refresh, api: api
  return ids unless names

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

.network_ranges(refresh = false, api: JSS.api) ⇒ 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?

  • api (JSS::APIConnection) (defaults to: JSS.api)

    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
# File 'lib/jss/api_object/network_segment.rb', line 95

def self.network_ranges(refresh = false, api: JSS.api)
  @network_ranges = nil if refresh
  return @network_ranges if @network_ranges

  @network_ranges = {}
  all(refresh, api: api).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: JSS.api) ⇒ 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?

  • api (JSS::APIConnection) (defaults to: JSS.api)

    the APIConnection to query

Returns:

  • (Hash{Integer => Range})

    the network segments as Integer Ranges keyed by id


123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/jss/api_object/network_segment.rb', line 123

def self.network_ranges_as_integers(refresh = false, api: JSS.api)
  @network_ranges_as_integers = nil if refresh
  return @network_ranges_as_integers if @network_ranges_as_integers

  @network_ranges_as_integers = {}
  all(refresh, api: api).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: JSS.api) ⇒ 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


265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/jss/api_object/network_segment.rb', line 265

def self.network_segment_for_ip(ipaddr, refresh: false, api: JSS.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, api: api).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: JSS.api) ⇒ 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?

  • api (JSS::APIConnection) (defaults to: JSS.api)

    The API connection to query

Returns:

  • (Array<Integer>)

    the ids of the NetworkSegments containing the given ip


247
248
249
250
251
252
# File 'lib/jss/api_object/network_segment.rb', line 247

def self.network_segments_for_ip(ipaddr, refresh = false, api: JSS.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, api: api).select { |_id, range| range.include? ip }.keys
end

.subnets(refresh = false, api: JSS.api) ⇒ Object

An alias for network_ranges

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

See Also:

  • {NetworkSegment{NetworkSegment::network_ranges}

142
143
144
# File 'lib/jss/api_object/network_segment.rb', line 142

def self.subnets(refresh = false, api: JSS.api)
  network_ranges refresh, api: api
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:


227
228
229
230
231
# File 'lib/jss/api_object/network_segment.rb', line 227

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

  raise JSS::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)

450
451
452
453
454
# File 'lib/jss/api_object/network_segment.rb', line 450

def ==(other)
  raise TypeError, 'Argument must be a JSS::NetworkSegment' unless \
    other.is_a? JSS::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


616
617
618
619
620
621
# File 'lib/jss/api_object/network_segment.rb', line 616

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) ⇒ 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

  • api (JSS::APIConnection) (defaults to: nil)

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

Returns:

  • (APIObject)

    An uncreated clone of this APIObject with the given name

Raises:

#createInteger Originally defined in module Creatable

Create a new object in the JSS.

Parameters:

  • api (JSS::APIConnection)

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

Returns:

  • (Integer)

    the jss ID of the newly created object

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?


433
434
435
436
437
438
439
440
# File 'lib/jss/api_object/network_segment.rb', line 433

def include?(thing)
  if thing.is_a? JSS::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)

419
420
421
422
423
424
# File 'lib/jss/api_object/network_segment.rb', line 419

def overlap?(other_segment)
  raise TypeError, 'Argument must be a JSS::NetworkSegment' unless \
    other_segment.is_a? JSS::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.


409
410
411
# File 'lib/jss/api_object/network_segment.rb', line 409

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


640
641
642
643
644
645
646
647
648
649
650
# File 'lib/jss/api_object/network_segment.rb', line 640

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

#updateBoolean Originally defined in module Updatable

Save changes to the JSS

Returns:

  • (Boolean)

    success

Raises: