jss-api 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/jss-api.rb +1 -191
- metadata +16 -146
- data/.yardopts +0 -7
- data/CHANGES.md +0 -88
- data/LICENSE.txt +0 -174
- data/README.md +0 -396
- data/THANKS.md +0 -6
- data/bin/cgrouper +0 -485
- data/bin/subnet-update +0 -400
- data/lib/jss-api/api_connection.rb +0 -400
- data/lib/jss-api/api_object.rb +0 -616
- data/lib/jss-api/api_object/advanced_search.rb +0 -389
- data/lib/jss-api/api_object/advanced_search/advanced_computer_search.rb +0 -95
- data/lib/jss-api/api_object/advanced_search/advanced_mobile_device_search.rb +0 -96
- data/lib/jss-api/api_object/advanced_search/advanced_user_search.rb +0 -95
- data/lib/jss-api/api_object/building.rb +0 -92
- data/lib/jss-api/api_object/category.rb +0 -147
- data/lib/jss-api/api_object/computer.rb +0 -852
- data/lib/jss-api/api_object/creatable.rb +0 -98
- data/lib/jss-api/api_object/criteriable.rb +0 -189
- data/lib/jss-api/api_object/criteriable/criteria.rb +0 -231
- data/lib/jss-api/api_object/criteriable/criterion.rb +0 -228
- data/lib/jss-api/api_object/department.rb +0 -93
- data/lib/jss-api/api_object/distribution_point.rb +0 -560
- data/lib/jss-api/api_object/extendable.rb +0 -221
- data/lib/jss-api/api_object/extension_attribute.rb +0 -457
- data/lib/jss-api/api_object/extension_attribute/computer_extension_attribute.rb +0 -362
- data/lib/jss-api/api_object/extension_attribute/mobile_device_extension_attribute.rb +0 -189
- data/lib/jss-api/api_object/extension_attribute/user_extension_attribute.rb +0 -117
- data/lib/jss-api/api_object/group.rb +0 -380
- data/lib/jss-api/api_object/group/computer_group.rb +0 -124
- data/lib/jss-api/api_object/group/mobile_device_group.rb +0 -139
- data/lib/jss-api/api_object/group/user_group.rb +0 -139
- data/lib/jss-api/api_object/ldap_server.rb +0 -535
- data/lib/jss-api/api_object/locatable.rb +0 -286
- data/lib/jss-api/api_object/matchable.rb +0 -97
- data/lib/jss-api/api_object/mobile_device.rb +0 -556
- data/lib/jss-api/api_object/netboot_server.rb +0 -148
- data/lib/jss-api/api_object/network_segment.rb +0 -414
- data/lib/jss-api/api_object/osx_configuration_profile.rb +0 -261
- data/lib/jss-api/api_object/package.rb +0 -812
- data/lib/jss-api/api_object/peripheral.rb +0 -335
- data/lib/jss-api/api_object/peripheral_type.rb +0 -295
- data/lib/jss-api/api_object/policy.rb +0 -898
- data/lib/jss-api/api_object/purchasable.rb +0 -316
- data/lib/jss-api/api_object/removable_macaddr.rb +0 -98
- data/lib/jss-api/api_object/scopable.rb +0 -136
- data/lib/jss-api/api_object/scopable/scope.rb +0 -621
- data/lib/jss-api/api_object/script.rb +0 -631
- data/lib/jss-api/api_object/self_servable.rb +0 -355
- data/lib/jss-api/api_object/site.rb +0 -93
- data/lib/jss-api/api_object/software_update_server.rb +0 -109
- data/lib/jss-api/api_object/updatable.rb +0 -117
- data/lib/jss-api/api_object/uploadable.rb +0 -138
- data/lib/jss-api/api_object/user.rb +0 -272
- data/lib/jss-api/client.rb +0 -504
- data/lib/jss-api/compatibility.rb +0 -66
- data/lib/jss-api/composer.rb +0 -171
- data/lib/jss-api/configuration.rb +0 -306
- data/lib/jss-api/db_connection.rb +0 -298
- data/lib/jss-api/exceptions.rb +0 -95
- data/lib/jss-api/ruby_extensions.rb +0 -35
- data/lib/jss-api/ruby_extensions/filetest.rb +0 -43
- data/lib/jss-api/ruby_extensions/hash.rb +0 -79
- data/lib/jss-api/ruby_extensions/ipaddr.rb +0 -91
- data/lib/jss-api/ruby_extensions/pathname.rb +0 -77
- data/lib/jss-api/ruby_extensions/string.rb +0 -59
- data/lib/jss-api/ruby_extensions/time.rb +0 -63
- data/lib/jss-api/server.rb +0 -108
- data/lib/jss-api/utility.rb +0 -416
- data/lib/jss-api/version.rb +0 -31
data/bin/subnet-update
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/ruby
|
|
2
|
-
|
|
3
|
-
### Copyright 2016 Pixar
|
|
4
|
-
###
|
|
5
|
-
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
6
|
-
### with the following modification; you may not use this file except in
|
|
7
|
-
### compliance with the Apache License and the following modification to it:
|
|
8
|
-
### Section 6. Trademarks. is deleted and replaced with:
|
|
9
|
-
###
|
|
10
|
-
### 6. Trademarks. This License does not grant permission to use the trade
|
|
11
|
-
### names, trademarks, service marks, or product names of the Licensor
|
|
12
|
-
### and its affiliates, except as required to comply with Section 4(c) of
|
|
13
|
-
### the License and to reproduce the content of the NOTICE file.
|
|
14
|
-
###
|
|
15
|
-
### You may obtain a copy of the Apache License at
|
|
16
|
-
###
|
|
17
|
-
### http://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
-
###
|
|
19
|
-
### Unless required by applicable law or agreed to in writing, software
|
|
20
|
-
### distributed under the Apache License with the above modification is
|
|
21
|
-
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
22
|
-
### KIND, either express or implied. See the Apache License for the specific
|
|
23
|
-
### language governing permissions and limitations under the Apache License.
|
|
24
|
-
|
|
25
|
-
##############################
|
|
26
|
-
# == Synopsis
|
|
27
|
-
# Add, remove, or change the Network Segments in the JSS based on data from an input file
|
|
28
|
-
# in CSV, tab, or other delimited format.
|
|
29
|
-
#
|
|
30
|
-
# == Usage
|
|
31
|
-
# subnet-update [-t | -d delimiter] [-h] file
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# == Author
|
|
35
|
-
# Chris Lasell <chrisl@pixar.com>
|
|
36
|
-
#
|
|
37
|
-
# == Copyright
|
|
38
|
-
# Copyright (c) 2014 Pixar Animation Studios
|
|
39
|
-
|
|
40
|
-
##############################
|
|
41
|
-
# Libraries
|
|
42
|
-
require 'jss-api'
|
|
43
|
-
require 'getoptlong'
|
|
44
|
-
|
|
45
|
-
##############################
|
|
46
|
-
# The app object
|
|
47
|
-
class App
|
|
48
|
-
|
|
49
|
-
##############################
|
|
50
|
-
# Constants
|
|
51
|
-
|
|
52
|
-
USAGE = "Usage: #{File.basename($0)} [-d delim] [--header] [-c col1,col2,col3 ] [-m manual-prefix] [--help] /path/to/file"
|
|
53
|
-
|
|
54
|
-
POTENTIAL_COLUMNS = [:name, :starting, :ending, :cidr]
|
|
55
|
-
|
|
56
|
-
# Whenever we process a file, we store it here. The next time we
|
|
57
|
-
# run, if the input file is identical to this, we exit witout doing anything.
|
|
58
|
-
DEFAULT_CACHE_FILE = Pathname.new("~/.last_subnet_update").expand_path
|
|
59
|
-
|
|
60
|
-
DEFAULT_DELIMITER = "\t"
|
|
61
|
-
DEFAULT_COLUMNS = [:name, :starting, :ending]
|
|
62
|
-
DEFAULT_MANUAL_PREFIX = "Manual-"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
attr_reader :debug
|
|
66
|
-
|
|
67
|
-
###############
|
|
68
|
-
# set up
|
|
69
|
-
def initialize(args)
|
|
70
|
-
|
|
71
|
-
# set defaults
|
|
72
|
-
@debug = false
|
|
73
|
-
|
|
74
|
-
@delim = DEFAULT_DELIMITER
|
|
75
|
-
@header = false
|
|
76
|
-
@columns = DEFAULT_COLUMNS
|
|
77
|
-
@cache_file = DEFAULT_CACHE_FILE
|
|
78
|
-
@manual_prefix = DEFAULT_MANUAL_PREFIX
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
#define the cli opts
|
|
82
|
-
cli_opts = GetoptLong.new(
|
|
83
|
-
[ '--help', '-H', GetoptLong::NO_ARGUMENT ],
|
|
84
|
-
[ '--delimiter', '--delim', '-d', GetoptLong::REQUIRED_ARGUMENT],
|
|
85
|
-
[ '--header', '-h', GetoptLong::NO_ARGUMENT],
|
|
86
|
-
[ '--columns', '-c', GetoptLong::OPTIONAL_ARGUMENT],
|
|
87
|
-
[ '--manual-prefix', '-m', GetoptLong::OPTIONAL_ARGUMENT],
|
|
88
|
-
[ '--cache', GetoptLong::REQUIRED_ARGUMENT ],
|
|
89
|
-
[ '--debug', GetoptLong::NO_ARGUMENT],
|
|
90
|
-
[ '--server', '-S', GetoptLong::OPTIONAL_ARGUMENT],
|
|
91
|
-
[ '--port', '-P', GetoptLong::OPTIONAL_ARGUMENT],
|
|
92
|
-
[ '--user', '-U', GetoptLong::OPTIONAL_ARGUMENT],
|
|
93
|
-
[ '--no-verify-cert', '-V', GetoptLong::NO_ARGUMENT],
|
|
94
|
-
[ '--timeout', '-T', GetoptLong::OPTIONAL_ARGUMENT]
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
# parse the cli opts
|
|
98
|
-
cli_opts.each do |opt, arg|
|
|
99
|
-
case opt
|
|
100
|
-
when '--help' then show_help
|
|
101
|
-
when '--delimiter' then @delim = arg
|
|
102
|
-
when '--header' then @header = true
|
|
103
|
-
when '--columns' then @columns = arg.split(',').map{|c| c.to_sym}
|
|
104
|
-
when '--manual-prefix' then @manual_prefix = arg
|
|
105
|
-
when '--cache' then @cache_file = Pathname.new arg
|
|
106
|
-
when '--debug' then @debug = true
|
|
107
|
-
when '--server'
|
|
108
|
-
@server = arg
|
|
109
|
-
|
|
110
|
-
when '--port'
|
|
111
|
-
@port = arg
|
|
112
|
-
|
|
113
|
-
when '--user'
|
|
114
|
-
@user = arg
|
|
115
|
-
|
|
116
|
-
when '--no-verify-cert'
|
|
117
|
-
@verify_cert = false
|
|
118
|
-
|
|
119
|
-
when '--timeout'
|
|
120
|
-
@timeout = arg
|
|
121
|
-
|
|
122
|
-
end # case
|
|
123
|
-
end # each opt arg
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@columns = nil if @columns and @columns.empty?
|
|
127
|
-
|
|
128
|
-
@file = args.shift
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
end # init
|
|
132
|
-
|
|
133
|
-
###############
|
|
134
|
-
# Go!
|
|
135
|
-
def run
|
|
136
|
-
|
|
137
|
-
unless @file
|
|
138
|
-
puts "No input file specified."
|
|
139
|
-
puts USAGE
|
|
140
|
-
return
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
@file = Pathname.new @file
|
|
144
|
-
|
|
145
|
-
unless parse_file
|
|
146
|
-
puts "File hasn't changed since last time, no changes to make!"
|
|
147
|
-
return
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# use any config settings defined....
|
|
151
|
-
@user ||= JSS::CONFIG.api_username
|
|
152
|
-
@server ||= JSS::CONFIG.api_server_name
|
|
153
|
-
@getpass = $stdin.tty? ? :prompt : :stdin
|
|
154
|
-
|
|
155
|
-
raise JSS::MissingDataError, "No JSS Username provided or found in the JSS gem config." unless @user
|
|
156
|
-
raise JSS::MissingDataError, "No JSS Server provided or found in the JSS gem config." unless @server
|
|
157
|
-
|
|
158
|
-
JSS::API.connect( :server => @server,
|
|
159
|
-
:port => @port,
|
|
160
|
-
:verify_cert => @verify_cert,
|
|
161
|
-
:user => @user,
|
|
162
|
-
:pw => @getpass,
|
|
163
|
-
:stdin_line => 1,
|
|
164
|
-
:timeout => @timeout
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
update_network_segments
|
|
168
|
-
|
|
169
|
-
end # run
|
|
170
|
-
|
|
171
|
-
#####################################
|
|
172
|
-
###
|
|
173
|
-
### Show Help
|
|
174
|
-
###
|
|
175
|
-
def show_help
|
|
176
|
-
puts <<-FULLHELP
|
|
177
|
-
Update the JSS Network Segments from a delimited file of subnet information.
|
|
178
|
-
|
|
179
|
-
#{USAGE}
|
|
180
|
-
|
|
181
|
-
Options:
|
|
182
|
-
-d, --delimiter - The field delimiter in the file, defaults to tab.
|
|
183
|
-
-c, --columns [col1,col2,col3]
|
|
184
|
-
- The column order in file, must include 'name', 'starting',
|
|
185
|
-
and either 'ending' or 'cidr'
|
|
186
|
-
-h, --header - The first line of the file is a header line,
|
|
187
|
-
possibly defining the columns
|
|
188
|
-
-m, --manual-prefix - Network Segment names in the JSS with this prefix are ignored.
|
|
189
|
-
Defaults to 'Manual-'
|
|
190
|
-
--cache /path/.. - Where read/save the input data for comparison between runs.
|
|
191
|
-
Defaults to ~/.last_subnet_update
|
|
192
|
-
-S, --server srvr - specify the JSS API server name
|
|
193
|
-
-P, --port portnum - specify the JSS API port
|
|
194
|
-
-U, --user username - specify the JSS API user
|
|
195
|
-
-V, --no-verify-cert - Allow self-signed, unverified SSL certificate
|
|
196
|
-
-T, --timeout secs - specify the JSS API timeout
|
|
197
|
-
-H, --help - show this help
|
|
198
|
-
--debug - show the ruby backtrace when errors occur
|
|
199
|
-
|
|
200
|
-
This program parses the input file line by line (possibly accounting for a header line).
|
|
201
|
-
Each line defines the name and IP-range of a subnet/network segment.
|
|
202
|
-
|
|
203
|
-
- If a segment doesn't exist in the JSS, it is created.
|
|
204
|
-
- If a segment's range has changed, it is updated in the JSS.
|
|
205
|
-
- If a JSS segment doesn't exist in the file, it is deleted from the JSS
|
|
206
|
-
unless its name starts with the --manual-prefix
|
|
207
|
-
|
|
208
|
-
Input File:
|
|
209
|
-
- The file must contain three columns, separated by the --delimiter,
|
|
210
|
-
with these names, in any order:
|
|
211
|
-
- 'name' (the network segment name)
|
|
212
|
-
- 'starting' (the starting IP address of the network segment)
|
|
213
|
-
- EITHER of:
|
|
214
|
-
- 'ending' (the ending IP address of the network segment)
|
|
215
|
-
- 'cidr' (the network range of the segment as a CIDR bitmask, e.g. '24')
|
|
216
|
-
Notes:
|
|
217
|
-
- The --columns option is a comma-separted list of the three
|
|
218
|
-
column names aboveindicating the column-order in the file.
|
|
219
|
-
|
|
220
|
-
- If --columns are not provided, and --header is specified, the first line
|
|
221
|
-
is assumed to contain the column names, separated by the delimiter
|
|
222
|
-
|
|
223
|
-
- If --header is provided with --columns, the first line of the file is ignored.
|
|
224
|
-
|
|
225
|
-
- The raw data from the file is cached and compared to the input file at
|
|
226
|
-
the next run. If the data is identical, no JSS connection is made.
|
|
227
|
-
|
|
228
|
-
- If no API settings are provided, they will be read from /etc/jss_gem.conf
|
|
229
|
-
and ~/.jss_gem.conf. See the JSS Gem docs for details.
|
|
230
|
-
|
|
231
|
-
- The password for the connection will be read from STDIN or prompted if needed
|
|
232
|
-
|
|
233
|
-
FULLHELP
|
|
234
|
-
exit 0
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
########################
|
|
238
|
-
# parse the incoming data file.
|
|
239
|
-
# If the file hasn't changed from the last time we processed it
|
|
240
|
-
# then return false
|
|
241
|
-
# otherwise parse it into @parsed_data and return true
|
|
242
|
-
# @parsed_data is an array of hashes, each with :name, :starting, and :ending
|
|
243
|
-
#
|
|
244
|
-
def parse_file
|
|
245
|
-
|
|
246
|
-
raise "'#{@file}' is not readable, or not a regular file" unless @file.readable? and @file.file?
|
|
247
|
-
|
|
248
|
-
# read in the file
|
|
249
|
-
@raw_data = @file.read
|
|
250
|
-
|
|
251
|
-
# compare it to the one we used last time
|
|
252
|
-
if @cache_file.readable?
|
|
253
|
-
return false if @raw_data == @cache_file.read
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
# split the data into an array by newlines
|
|
257
|
-
lines = @raw_data.split "\n"
|
|
258
|
-
|
|
259
|
-
# remove the first line if its a header, and parse it into the columns
|
|
260
|
-
# if needed
|
|
261
|
-
if @header
|
|
262
|
-
header = lines.shift
|
|
263
|
-
@columns ||= header.split(/\s*#{@delim}\s*/).map{|c| c.to_sym}
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# check some state
|
|
267
|
-
raise "Columns must include 'name' and 'starting'" unless @columns.include?(:name) and @columns.include?(:starting)
|
|
268
|
-
raise "Columns must include either 'ending' or 'cidr'" unless @columns.include?(:ending) or @columns.include?(:cidr)
|
|
269
|
-
|
|
270
|
-
@use_cidr = @columns.include? :cidr
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
# which columns are which in the file?
|
|
274
|
-
name = @columns.index :name
|
|
275
|
-
starting = @columns.index :starting
|
|
276
|
-
ending = @use_cidr ? @columns.index(:cidr) : @columns.index(:ending)
|
|
277
|
-
|
|
278
|
-
# split each line and convert it into a hash
|
|
279
|
-
@parsed_data = lines.map do |line|
|
|
280
|
-
|
|
281
|
-
parts = line.split(@delim).map{|f| f.strip }
|
|
282
|
-
|
|
283
|
-
unless parts[name] and parts[starting] and parts[ending]
|
|
284
|
-
puts "Skipping invalid line: #{line}"
|
|
285
|
-
next
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
{:name => parts[name], :starting => parts[starting], :ending => parts[ending]}
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
# parsed data is now an array of hashes
|
|
293
|
-
return true
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
#############################################
|
|
297
|
-
#############################################
|
|
298
|
-
# Update the JSS Network Segments from GIT_NETBLOCKS_URL, q.v.
|
|
299
|
-
def update_network_segments
|
|
300
|
-
|
|
301
|
-
# CREATE any that are in the parsed data but not yet in the JSS,
|
|
302
|
-
# and UPDATE any that exist but have modified ranges.
|
|
303
|
-
# While looping through, make a hash of JSS::NetworkSegment objects, keyed by their name.
|
|
304
|
-
segs_from_data = {}
|
|
305
|
-
|
|
306
|
-
@parsed_data.each do |pd|
|
|
307
|
-
|
|
308
|
-
# skip anthing with the manual prefix
|
|
309
|
-
next if pd[:name].start_with? @manual_prefix
|
|
310
|
-
|
|
311
|
-
ender = @use_cidr ? :cidr : :ending_address
|
|
312
|
-
|
|
313
|
-
begin
|
|
314
|
-
this_seg = JSS::NetworkSegment.new(:id => :new, :name => pd[:name], :starting_address => pd[:starting], ender => pd[:ending])
|
|
315
|
-
|
|
316
|
-
# If the new netsegment should have other settings (dist. point, netboot server, etc...)
|
|
317
|
-
# here's where you should apply those settings.
|
|
318
|
-
|
|
319
|
-
this_seg.create
|
|
320
|
-
puts "Added Network Segment '#{this_seg.name}' to the JSS"
|
|
321
|
-
|
|
322
|
-
# it already exists, so see if it needs any changes
|
|
323
|
-
rescue JSS::AlreadyExistsError
|
|
324
|
-
|
|
325
|
-
# there's already one with this name, so just grab it.
|
|
326
|
-
this_seg = JSS::NetworkSegment.new( :name => pd[:name])
|
|
327
|
-
|
|
328
|
-
# does the startng addres need to be changed?
|
|
329
|
-
needs_update = this_seg.starting_address.to_s != pd[:starting].to_s
|
|
330
|
-
|
|
331
|
-
# even if we don't need to update the starting, we might need to update
|
|
332
|
-
# the ending...
|
|
333
|
-
unless needs_update
|
|
334
|
-
if @use_cidr
|
|
335
|
-
needs_update = this_seg.cidr.to_i != pd[:ending].to_i
|
|
336
|
-
else
|
|
337
|
-
needs_update = this_seg.ending_address.to_s != pd[:ending].to_s
|
|
338
|
-
end # if @use_cidr
|
|
339
|
-
end #unless needs update
|
|
340
|
-
|
|
341
|
-
# did we decide we need an update?
|
|
342
|
-
if needs_update
|
|
343
|
-
this_seg.starting_address = pd[:starting]
|
|
344
|
-
if @use_cidr
|
|
345
|
-
this_seg.cidr = pd[:ending].to_i
|
|
346
|
-
else
|
|
347
|
-
this_seg.ending_address = pd[:ending]
|
|
348
|
-
end # if @use_cidr
|
|
349
|
-
this_seg.update
|
|
350
|
-
puts "Updated IP range for Network Segment '#{this_seg.name}'"
|
|
351
|
-
|
|
352
|
-
else # doesn't need update
|
|
353
|
-
puts "Network Segment '#{this_seg.name}' doesn't have any changes."
|
|
354
|
-
end # if needs update
|
|
355
|
-
|
|
356
|
-
# rescue other errors
|
|
357
|
-
rescue
|
|
358
|
-
raise "There was an error with NetworkSegment #{pd[:name]}: #{$!}"
|
|
359
|
-
end # begin
|
|
360
|
-
|
|
361
|
-
segs_from_data[this_seg.name] = this_seg
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
# DELETE those in jss, but not in parsed data,
|
|
366
|
-
# unless the name starts with @manual_prefix
|
|
367
|
-
JSS::NetworkSegment.map_all_ids_to(:name).each do |id,name|
|
|
368
|
-
|
|
369
|
-
next if name.start_with? @manual_prefix
|
|
370
|
-
|
|
371
|
-
unless segs_from_data.keys.include? name
|
|
372
|
-
JSS::NetworkSegment.new(:id => id).delete
|
|
373
|
-
puts "Deleted Network Segment '#{name}' from the JSS"
|
|
374
|
-
end # unless
|
|
375
|
-
|
|
376
|
-
end # jss_uids.each seg
|
|
377
|
-
|
|
378
|
-
# save the data into a file for comparison next time
|
|
379
|
-
@cache_file.jss_save @raw_data
|
|
380
|
-
|
|
381
|
-
# all done
|
|
382
|
-
return true
|
|
383
|
-
end # update_network_segments
|
|
384
|
-
|
|
385
|
-
end # app
|
|
386
|
-
|
|
387
|
-
##############################
|
|
388
|
-
# create the app and go
|
|
389
|
-
begin
|
|
390
|
-
app = App.new(ARGV)
|
|
391
|
-
app.run
|
|
392
|
-
rescue
|
|
393
|
-
# handle exceptions not handled elsewhere
|
|
394
|
-
puts "An error occurred: #{$!}"
|
|
395
|
-
puts "Backtrace:" if app.debug
|
|
396
|
-
puts $@ if app.debug
|
|
397
|
-
|
|
398
|
-
ensure
|
|
399
|
-
|
|
400
|
-
end
|
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
### Copyright 2016 Pixar
|
|
2
|
-
###
|
|
3
|
-
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
4
|
-
### with the following modification; you may not use this file except in
|
|
5
|
-
### compliance with the Apache License and the following modification to it:
|
|
6
|
-
### Section 6. Trademarks. is deleted and replaced with:
|
|
7
|
-
###
|
|
8
|
-
### 6. Trademarks. This License does not grant permission to use the trade
|
|
9
|
-
### names, trademarks, service marks, or product names of the Licensor
|
|
10
|
-
### and its affiliates, except as required to comply with Section 4(c) of
|
|
11
|
-
### the License and to reproduce the content of the NOTICE file.
|
|
12
|
-
###
|
|
13
|
-
### You may obtain a copy of the Apache License at
|
|
14
|
-
###
|
|
15
|
-
### http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
###
|
|
17
|
-
### Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
### distributed under the Apache License with the above modification is
|
|
19
|
-
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
20
|
-
### KIND, either express or implied. See the Apache License for the specific
|
|
21
|
-
### language governing permissions and limitations under the Apache License.
|
|
22
|
-
###
|
|
23
|
-
###
|
|
24
|
-
|
|
25
|
-
###
|
|
26
|
-
module JSS
|
|
27
|
-
|
|
28
|
-
#####################################
|
|
29
|
-
### Constants
|
|
30
|
-
#####################################
|
|
31
|
-
|
|
32
|
-
#####################################
|
|
33
|
-
### Module Variables
|
|
34
|
-
#####################################
|
|
35
|
-
|
|
36
|
-
#####################################
|
|
37
|
-
### Module Methods
|
|
38
|
-
#####################################
|
|
39
|
-
|
|
40
|
-
#####################################
|
|
41
|
-
### Module Classes
|
|
42
|
-
#####################################
|
|
43
|
-
|
|
44
|
-
###
|
|
45
|
-
### An API connection to the JSS.
|
|
46
|
-
###
|
|
47
|
-
### This is a singleton class, only one can exist at a time.
|
|
48
|
-
### Its one instance is created automatically when the module loads, but it
|
|
49
|
-
### isn't connected to anything at that time.
|
|
50
|
-
###
|
|
51
|
-
### Use it via the {JSS::API} constant to call the #connect
|
|
52
|
-
### method, and the {#get_rsrc}, {#put_rsrc}, {#post_rsrc}, & {#delete_rsrc}
|
|
53
|
-
### methods, q.v. below.
|
|
54
|
-
###
|
|
55
|
-
### To access the underlying RestClient::Resource instance,
|
|
56
|
-
### use JSS::API.cnx
|
|
57
|
-
###
|
|
58
|
-
class APIConnection
|
|
59
|
-
include Singleton
|
|
60
|
-
|
|
61
|
-
#####################################
|
|
62
|
-
### Class Constants
|
|
63
|
-
#####################################
|
|
64
|
-
|
|
65
|
-
### The base API path in the jss URL
|
|
66
|
-
RSRC_BASE = "JSSResource"
|
|
67
|
-
|
|
68
|
-
### A url path to load to see if there's an API available at a host.
|
|
69
|
-
### This just loads the API resource docs page
|
|
70
|
-
TEST_PATH = "api"
|
|
71
|
-
|
|
72
|
-
### If the test path loads correctly from a casper server, it'll contain
|
|
73
|
-
### this text
|
|
74
|
-
TEST_CONTENT = "<title>JSS REST API Resource Documentation</title>"
|
|
75
|
-
|
|
76
|
-
### The Default port
|
|
77
|
-
HTTP_PORT = 9006
|
|
78
|
-
|
|
79
|
-
### The SSL port
|
|
80
|
-
SSL_PORT = 8443
|
|
81
|
-
|
|
82
|
-
### The top line of an XML doc for submitting data via API
|
|
83
|
-
XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
|
|
84
|
-
|
|
85
|
-
### Default timeouts in seconds
|
|
86
|
-
DFT_OPEN_TIMEOUT = 60
|
|
87
|
-
DFT_TIMEOUT = 60
|
|
88
|
-
|
|
89
|
-
### The Default SSL Version
|
|
90
|
-
DFT_SSL_VERSION = 'TLSv1'
|
|
91
|
-
|
|
92
|
-
#####################################
|
|
93
|
-
### Attributes
|
|
94
|
-
#####################################
|
|
95
|
-
|
|
96
|
-
### @return [String] the username who's connected to the JSS API
|
|
97
|
-
attr_reader :jss_user
|
|
98
|
-
|
|
99
|
-
### @return [RestClient::Resource] the underlying connection resource
|
|
100
|
-
attr_reader :cnx
|
|
101
|
-
|
|
102
|
-
### @return [Boolean] are we connected right now?
|
|
103
|
-
attr_reader :connected
|
|
104
|
-
|
|
105
|
-
### @return [JSS::Server] the details of the JSS to which we're connected.
|
|
106
|
-
attr_reader :server
|
|
107
|
-
|
|
108
|
-
### @return [String] the hostname of the JSS to which we're connected.
|
|
109
|
-
attr_reader :server_host
|
|
110
|
-
|
|
111
|
-
#####################################
|
|
112
|
-
### Constructor
|
|
113
|
-
#####################################
|
|
114
|
-
|
|
115
|
-
###
|
|
116
|
-
### To connect, use JSS::APIConnection.instance.connect
|
|
117
|
-
### or a shortcut, JSS::API.connect
|
|
118
|
-
###
|
|
119
|
-
def initialize ()
|
|
120
|
-
@connected = false
|
|
121
|
-
end # init
|
|
122
|
-
|
|
123
|
-
#####################################
|
|
124
|
-
### Class Methods
|
|
125
|
-
#####################################
|
|
126
|
-
|
|
127
|
-
###
|
|
128
|
-
### Connect to the JSS API.
|
|
129
|
-
###
|
|
130
|
-
### @param args[Hash] the keyed arguments for connection.
|
|
131
|
-
###
|
|
132
|
-
### @option args :server[String] the hostname of the JSS API server, required if not defined in JSS::CONFIG
|
|
133
|
-
###
|
|
134
|
-
### @option args :port[Integer] the port number to connect with, defaults to 8443
|
|
135
|
-
###
|
|
136
|
-
### @option args :use_ssl[Boolean] should the connection be made over SSL? Defaults to true.
|
|
137
|
-
###
|
|
138
|
-
### @option args :verify_cert[Boolean] should HTTPS SSL certificates be verified. Defaults to true.
|
|
139
|
-
### If your connection raises RestClient::SSLCertificateNotVerified, and you don't care about the
|
|
140
|
-
### validity of the SSL cert. just set this explicitly to false.
|
|
141
|
-
###
|
|
142
|
-
### @option args :user[String] a JSS user who has API privs, required if not defined in JSS::CONFIG
|
|
143
|
-
###
|
|
144
|
-
### @option args :pw[String,Symbol] Required, the password for that user, or :prompt, or :stdin
|
|
145
|
-
### If :prompt, the user is promted on the commandline to enter the password for the :user.
|
|
146
|
-
### If :stdin#, the password is read from a line of std in represented by the digit at #,
|
|
147
|
-
### so :stdin3 reads the passwd from the third line of standard input. defaults to line 1,
|
|
148
|
-
### if no digit is supplied. see {JSS.stdin}
|
|
149
|
-
###
|
|
150
|
-
### @option args :open_timeout[Integer] the number of seconds to wait for an initial response, defaults to 60
|
|
151
|
-
###
|
|
152
|
-
### @option args :timeout[Integer] the number of seconds before an API call times out, defaults to 60
|
|
153
|
-
###
|
|
154
|
-
### @return [true]
|
|
155
|
-
###
|
|
156
|
-
def connect (args = {})
|
|
157
|
-
|
|
158
|
-
# the server, if not specified, might come from a couple places.
|
|
159
|
-
# see #hostname
|
|
160
|
-
args[:server] ||= hostname
|
|
161
|
-
|
|
162
|
-
# settings from config if they aren't in the args
|
|
163
|
-
args[:server] ||= JSS::CONFIG.api_server_name
|
|
164
|
-
args[:port] ||= JSS::CONFIG.api_server_port
|
|
165
|
-
args[:user] ||= JSS::CONFIG.api_username
|
|
166
|
-
args[:timeout] ||= JSS::CONFIG.api_timeout
|
|
167
|
-
args[:open_timeout] ||= JSS::CONFIG.api_timeout_open
|
|
168
|
-
args[:ssl_version] ||= JSS::CONFIG.api_ssl_version
|
|
169
|
-
|
|
170
|
-
# if verify cert given was NOT in the args....
|
|
171
|
-
if args[:verify_cert].nil?
|
|
172
|
-
# set it from the prefs
|
|
173
|
-
args[:verify_cert] = JSS::CONFIG.api_verify_cert
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# settings from client jamf plist if needed
|
|
177
|
-
args[:port] ||= JSS::Client.jss_port
|
|
178
|
-
|
|
179
|
-
# default settings if needed
|
|
180
|
-
args[:port] ||= SSL_PORT
|
|
181
|
-
args[:timeout] ||= DFT_TIMEOUT
|
|
182
|
-
args[:open_timeout] ||= DFT_OPEN_TIMEOUT
|
|
183
|
-
|
|
184
|
-
# As of Casper 9.61 we can't use SSL, must use TLS, since SSLv3 was susceptible to poodles.
|
|
185
|
-
# NOTE - this requires rest-client v 1.7.0 or higher
|
|
186
|
-
# which requires mime-types 2.0 or higher, which requires ruby 1.9.2 or higher!
|
|
187
|
-
# That means that support for ruby 1.8.7 stops with Casper 9.6
|
|
188
|
-
args[:ssl_version] ||= DFT_SSL_VERSION
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
# must have server, user, and pw
|
|
192
|
-
raise JSS::MissingDataError, "No JSS :server specified, or in configuration." unless args[:server]
|
|
193
|
-
raise JSS::MissingDataError, "No JSS :user specified, or in configuration." unless args[:user]
|
|
194
|
-
raise JSS::MissingDataError, "Missing :pw for user '#{args[:user]}'" unless args[:pw]
|
|
195
|
-
|
|
196
|
-
# we're using ssl if 1) args[:use_ssl] is anything but false
|
|
197
|
-
# or 2) the port is the default casper ssl port.
|
|
198
|
-
args[:use_ssl] = (not args[:use_ssl] == false) or (args[:port] == SSL_PORT)
|
|
199
|
-
|
|
200
|
-
# and here's the URL
|
|
201
|
-
ssl = args[:use_ssl] ? "s" : ''
|
|
202
|
-
@rest_url = URI::encode "http#{ssl}://#{args[:server]}:#{args[:port]}/#{RSRC_BASE}"
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
# prep the args for passing to RestClient::Resource
|
|
206
|
-
# if verify_cert is anything but false, we will verify
|
|
207
|
-
args[:verify_ssl] = (args[:verify_cert] == false) ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
|
|
208
|
-
|
|
209
|
-
args[:password] = if args[:pw] == :prompt
|
|
210
|
-
JSS.prompt_for_password "Enter the password for JSS user #{args[:user]}@#{args[:server]}:"
|
|
211
|
-
elsif args[:pw].is_a?(Symbol) and args[:pw].to_s.start_with?('stdin')
|
|
212
|
-
args[:pw].to_s =~ /^stdin(\d+)$/
|
|
213
|
-
line = $1
|
|
214
|
-
line ||= 1
|
|
215
|
-
JSS.stdin line
|
|
216
|
-
else
|
|
217
|
-
args[:pw]
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# heres our connection
|
|
221
|
-
@cnx = RestClient::Resource.new("#{@rest_url}", args)
|
|
222
|
-
|
|
223
|
-
@jss_user = args[:user]
|
|
224
|
-
@server_host = args[:server]
|
|
225
|
-
@connected = true
|
|
226
|
-
@server = JSS::Server.new
|
|
227
|
-
|
|
228
|
-
if @server.version < JSS.parse_jss_version(JSS::MINIMUM_SERVER_VERSION)[:version]
|
|
229
|
-
raise JSS::UnsupportedError, "Your JSS Server version, #{@server.raw_version}, is to low. Must be #{JSS::MINIMUM_SERVER_VERSION} or higher."
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
return @connected ? @server_host : nil
|
|
233
|
-
end # connect
|
|
234
|
-
|
|
235
|
-
###
|
|
236
|
-
### Reset the response timeout for the rest connection
|
|
237
|
-
###
|
|
238
|
-
### @param timeout[Integer] the new timeout in seconds
|
|
239
|
-
###
|
|
240
|
-
### @return [void]
|
|
241
|
-
###
|
|
242
|
-
def timeout= (timeout)
|
|
243
|
-
@cnx.options[:timeout] = timeout
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
###
|
|
247
|
-
### Reset the open-connection timeout for the rest connection
|
|
248
|
-
###
|
|
249
|
-
### @param timeout[Integer] the new timeout in seconds
|
|
250
|
-
###
|
|
251
|
-
### @return [void]
|
|
252
|
-
###
|
|
253
|
-
def open_timeout= (timeout)
|
|
254
|
-
@cnx.options[:open_timeout] = timeout
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
###
|
|
259
|
-
### With a REST connection, there isn't any real "connection" to disconnect from
|
|
260
|
-
### So to disconnect, we just unset all our credentials.
|
|
261
|
-
###
|
|
262
|
-
### @return [void]
|
|
263
|
-
###
|
|
264
|
-
def disconnect
|
|
265
|
-
@jss_user = nil
|
|
266
|
-
@rest_url = nil
|
|
267
|
-
@server_host = nil
|
|
268
|
-
@cnx = nil
|
|
269
|
-
@connected = false
|
|
270
|
-
end # disconnect
|
|
271
|
-
|
|
272
|
-
###
|
|
273
|
-
### Get an arbitrary JSS resource
|
|
274
|
-
###
|
|
275
|
-
### The first argument is the resource to get (the part of the API url
|
|
276
|
-
### after the 'JSSResource/' )
|
|
277
|
-
###
|
|
278
|
-
### By default we get the data in JSON, and parse it
|
|
279
|
-
### into a ruby data structure (arrays, hashes, strings, etc)
|
|
280
|
-
### with symbolized Hash keys.
|
|
281
|
-
###
|
|
282
|
-
### @param rsrc[String] the resource to get
|
|
283
|
-
### (the part of the API url after the 'JSSResource/' )
|
|
284
|
-
###
|
|
285
|
-
### @param format[Symbol] either ;json or :xml
|
|
286
|
-
### If the second argument is :xml, the XML data is returned as a String.
|
|
287
|
-
###
|
|
288
|
-
### @return [Hash,String] the result of the get
|
|
289
|
-
###
|
|
290
|
-
def get_rsrc (rsrc, format = :json)
|
|
291
|
-
raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
|
|
292
|
-
rsrc = URI::encode rsrc
|
|
293
|
-
data = @cnx[rsrc].get(:accept => format)
|
|
294
|
-
return JSON.parse(data, :symbolize_names => true) if format == :json
|
|
295
|
-
data
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
###
|
|
299
|
-
### Change an existing JSS resource
|
|
300
|
-
###
|
|
301
|
-
### @param rsrc[String] the API resource being changed, the URL part after 'JSSResource/'
|
|
302
|
-
###
|
|
303
|
-
### @param xml[String] the xml specifying the changes.
|
|
304
|
-
###
|
|
305
|
-
### @return [String] the xml response from the server.
|
|
306
|
-
###
|
|
307
|
-
def put_rsrc(rsrc,xml)
|
|
308
|
-
raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
|
|
309
|
-
|
|
310
|
-
### convert CRs & to
|
|
311
|
-
xml.gsub!(/\r/, ' ')
|
|
312
|
-
|
|
313
|
-
### send the data
|
|
314
|
-
@cnx[rsrc].put(xml, :content_type => 'text/xml')
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
###
|
|
318
|
-
### Create a new JSS resource
|
|
319
|
-
###
|
|
320
|
-
### @param rsrc[String] the API resource being created, the URL part after 'JSSResource/'
|
|
321
|
-
###
|
|
322
|
-
### @param xml[String] the xml specifying the new object.
|
|
323
|
-
###
|
|
324
|
-
### @return [String] the xml response from the server.
|
|
325
|
-
###
|
|
326
|
-
def post_rsrc(rsrc,xml)
|
|
327
|
-
raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
|
|
328
|
-
|
|
329
|
-
### convert CRs & to
|
|
330
|
-
xml.gsub!(/\r/, ' ')
|
|
331
|
-
|
|
332
|
-
### send the data
|
|
333
|
-
@cnx[rsrc].post xml, :content_type => 'text/xml', :accept => :json
|
|
334
|
-
end #post_rsrc
|
|
335
|
-
|
|
336
|
-
### Delete a resource from the JSS
|
|
337
|
-
###
|
|
338
|
-
### @param rsrc[String] the resource to create, the URL part after 'JSSResource/'
|
|
339
|
-
###
|
|
340
|
-
### @return [String] the xml response from the server.
|
|
341
|
-
###
|
|
342
|
-
def delete_rsrc(rsrc)
|
|
343
|
-
raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
|
|
344
|
-
raise MissingDataError, "Missing :rsrc" if rsrc.nil?
|
|
345
|
-
|
|
346
|
-
### delete the resource
|
|
347
|
-
@cnx[rsrc].delete
|
|
348
|
-
|
|
349
|
-
end #delete_rsrc
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
### Test that a given hostname & port is a JSS API server
|
|
353
|
-
###
|
|
354
|
-
### @param server[String] The hostname to test,
|
|
355
|
-
###
|
|
356
|
-
### @param port[Integer] The port to try connecting on
|
|
357
|
-
###
|
|
358
|
-
### @return [Boolean] does the server host a JSS API?
|
|
359
|
-
###
|
|
360
|
-
def valid_server? (server, port = SSL_PORT)
|
|
361
|
-
# try ssl first
|
|
362
|
-
begin
|
|
363
|
-
return true if open("https://#{server}:#{port}/#{TEST_PATH}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read.include? TEST_CONTENT
|
|
364
|
-
rescue
|
|
365
|
-
# then regular http
|
|
366
|
-
begin
|
|
367
|
-
return true if open("http://#{server}:#{port}/#{TEST_PATH}").read.include? TEST_CONTENT
|
|
368
|
-
rescue
|
|
369
|
-
# any errors = no API
|
|
370
|
-
return false
|
|
371
|
-
end # begin
|
|
372
|
-
end #begin
|
|
373
|
-
# if we're here, no API
|
|
374
|
-
return false
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
### The server to which we are connected, or will
|
|
378
|
-
### try connecting to if none is specified with the
|
|
379
|
-
### call to #connect
|
|
380
|
-
###
|
|
381
|
-
### @return [String] the hostname of the server
|
|
382
|
-
###
|
|
383
|
-
def hostname
|
|
384
|
-
return @server_host if @server_host
|
|
385
|
-
srvr = JSS::CONFIG.api_server_name
|
|
386
|
-
srvr ||= JSS::Client.jss_server
|
|
387
|
-
return srvr
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
### aliases
|
|
391
|
-
alias connected? connected
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
end # class JSSAPIConnection
|
|
395
|
-
|
|
396
|
-
### The single instance of the APIConnection
|
|
397
|
-
API = APIConnection.instance
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
end # module
|