jss-api 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ --title "The JSS API Ruby Gem - access to the Casper Suite API"
2
+ --readme README.md
3
+ --embed-mixins
4
+ -
5
+ LICENSE.txt
6
+ CHANGES.md
7
+ THANKS.md
data/CHANGES.md CHANGED
@@ -1,4 +1,12 @@
1
1
  # Change History
2
2
 
3
+ v0.5.6 2014-11-04
4
+ - now requires Ruby >= 1.9.3 and rest-client >= 1.7.0. Needed for Casper >= 9.61's lack of support for SSLv3.
5
+ - APIConnection now accepts :ssl_version option in the argument hash. Defaults to 'TLSv1'
6
+ - Configuration now supports the api_ssl_version key, used for the :ssl_version option of the APIConnection.
7
+ - the example programs have been moved to the bin directory, and are now included in the gem installation.
8
+ - many documentation updates as we adjust to being live
9
+ - minor bugfixes
10
+
3
11
  v0.5.0 2014-10-23
4
- - first opensource release
12
+ - first opensource release
data/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## DESCRIPTION
4
4
 
5
- The jss-api gem provides a Ruby module called JSS, which is used for accessing the REST API of the JAMF Software Server (JSS) - the core of the Casper Suite from JAMF Software, LLC. The module abstracts API resources as Ruby objects, and provides methods for interacting with those resources. It also provides some features that aren't a part of the API itself, but come with other Casper-related tools, such as uploading .pkg and .dmg {JSS::Package} data to the master distribution point, and the installation of {JSS::Package} objects on client machines. (See BEYOND THE API)
5
+ The jss-api gem provides a Ruby module called JSS, which is used for accessing the REST API of the JAMF Software Server (JSS), the core of the Casper Suite, an enterprise-level management tool for Apple devices from [JAMF Software, LLC](http://www.jamfsoftware.com/).
6
+
7
+ The module abstracts API resources as Ruby objects, and provides methods for interacting with those resources. It also provides some features that aren't a part of the API itself, but come with other Casper-related tools, such as uploading .pkg and .dmg {JSS::Package} data to the master distribution point, and the installation of {JSS::Package} objects on client machines. (See BEYOND THE API)
6
8
 
7
9
  The module is not a complete implementation of the Casper API. Only some API objects are modeled, some only minimally. Of
8
10
  those, some are read-only, some partially writable, some fully read-write (all implemented objects can be deleted)
@@ -11,6 +13,8 @@ See OBJECTS IMPLEMENTED for a list.
11
13
  We've implemented the things we need in our environment, and as our needs grow, we'll add more.
12
14
  Hopefully others will find it useful, and add more to it as well.
13
15
 
16
+ [Full technical documentation can be found here.](http://www.rubydoc.info/gems/jss-api/)
17
+
14
18
 
15
19
  ## SYNOPSIS
16
20
 
@@ -334,13 +338,14 @@ the JSS gem was written for:
334
338
 
335
339
  * Mac OS X 10.8 and higher
336
340
  * Casper Suite version 9.4 or higher
337
- * Ruby 1.8.7 and 2.0.0 (the two versions that come with OS X 10.9).
341
+ * Casper 9.4 - 9.6 require Ruby 1.8.7 and higher
342
+ * Casper >= 9.61 require Ruby 1.9.3 and higher
338
343
 
339
344
  It also requires these gems, which will be installed automatically if you install JSS with `gem install jss`
340
345
 
341
- * rest-client >=1.6.7 http://rubygems.org/gems/rest-client
346
+ * rest-client >=1.6.7 ( >= 1.7.0 with Casper >= 9.6.1) http://rubygems.org/gems/rest-client
342
347
  * json or json\_pure >= 1.6.5 http://rubygems.org/gems/json or http://rubygems.org/gems/json_pure
343
- * (only in ruby 1.8.7. Ruby 2.0.0 has json in its standard library)
348
+ * (only in ruby 1.8.7. Ruby >= 1.9 has json in its standard library)
344
349
  * ruby-mysql >= 2.9.12 http://rubygems.org/gems/ruby-mysql
345
350
  * (only for a few things that still require direct SQL access to the JSS database)
346
351
  * plist =3.1.0 http://rubygems.org/gems/plist
@@ -356,7 +361,7 @@ In general, you can install the JSS Gem with this command:
356
361
 
357
362
  `gem install jss-api`
358
363
 
359
- If you're using Ruby 1.8.7, install the following gems manually first, since the JSS gem will try to install newer, incompatible versions if they aren't pre-installed.
364
+ If you're using Ruby 1.8.7 (Casper 9.4 - 9.6 only), install the following gems manually first, since the JSS gem will try to install newer, incompatible versions if they aren't pre-installed.
360
365
 
361
366
  `gem install json -v 1.6.5`
362
367
 
@@ -0,0 +1,485 @@
1
+ #!/usr/bin/ruby
2
+
3
+ ### Copyright 2014 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
+ # Create or change the membership of a computer group in the JSS
27
+
28
+
29
+ # Load in the JSS library
30
+ require 'jss-api'
31
+
32
+ # Load other libs
33
+ require 'getoptlong'
34
+ require 'ostruct'
35
+
36
+ class App
37
+
38
+ #####################################
39
+ ###
40
+ ### Constants
41
+ ###
42
+ USAGE = "Usage: #{File.basename($0)} [-LsmcdlarRC] [--help] [-n newname]
43
+ [-S server] [-U user] [-T timeout] [-V] [--debug]
44
+ group [-f /file/path ] [computer [computer ...]]"
45
+
46
+ ACTIONS_NEEDING_GROUP = [ :create_group, :rename_group, :delete_group, :add_members, :remove_members, :remove_all, :list_members]
47
+
48
+ ACTIONS_FOR_STATIC_GROUPS_ONLY = [:create_group, :add_members, :remove_members, :remove_all]
49
+
50
+ #####################################
51
+ ### Attributes
52
+
53
+ attr_reader :debug
54
+
55
+ #####################################
56
+ ###
57
+ ### set up
58
+ ###
59
+ def initialize(args)
60
+
61
+ @debug = false
62
+
63
+ # define the options
64
+ cli_opts = GetoptLong.new(
65
+ [ '--help', '-h', '-H', GetoptLong::NO_ARGUMENT ],
66
+ [ '--list-groups', '-L', GetoptLong::NO_ARGUMENT ],
67
+ [ '--list-static', '-s', GetoptLong::NO_ARGUMENT ],
68
+ [ '--list-smart', '-m', GetoptLong::NO_ARGUMENT ],
69
+ [ '--create-group', '--create', '-c', GetoptLong::NO_ARGUMENT ],
70
+ [ '--rename-group', '--rename', '-n', GetoptLong::REQUIRED_ARGUMENT ],
71
+ [ '--delete-group', '--delete', '-d', GetoptLong::NO_ARGUMENT ],
72
+ [ '--list-members', '--list-computers', '-l', GetoptLong::NO_ARGUMENT ],
73
+ [ '--add-members', '--add', '-a', GetoptLong::NO_ARGUMENT ],
74
+ [ '--remove-members', '--remove', '-r', GetoptLong::NO_ARGUMENT ],
75
+ [ '--remove-all-members', '-R', GetoptLong::NO_ARGUMENT ],
76
+ [ '--file', '-f', GetoptLong::REQUIRED_ARGUMENT ],
77
+ [ '--server', '-S', GetoptLong::OPTIONAL_ARGUMENT],
78
+ [ '--port', '-P', GetoptLong::OPTIONAL_ARGUMENT],
79
+ [ '--user', '-U', GetoptLong::OPTIONAL_ARGUMENT],
80
+ [ '--no-verify-cert', '-V', GetoptLong::NO_ARGUMENT],
81
+ [ '--timeout', '-T', GetoptLong::OPTIONAL_ARGUMENT],
82
+ [ '--no-confirm', '-C', GetoptLong::NO_ARGUMENT],
83
+ [ '--debug', GetoptLong::NO_ARGUMENT]
84
+ )
85
+
86
+ # here's where we hold cmdline args and other user options
87
+ @options = OpenStruct.new
88
+
89
+ # set defaults
90
+ @options.action = :none
91
+
92
+ # if stdin is not a tty, then we must assume
93
+ # we're being passed a password
94
+ @options.getpass = $stdin.tty? ? :prompt : :stdin
95
+
96
+ # parse the options
97
+ cli_opts.each do |opt, arg|
98
+ case opt
99
+ when '--help'
100
+ show_help
101
+
102
+ when '--list-groups'
103
+ @options.action = :list_groups
104
+
105
+ when '--list-static'
106
+ @options.action = :list_static
107
+
108
+ when '--list-smart'
109
+ @options.action = :list_smart
110
+
111
+ when '--list-members'
112
+ @options.action = :list_members
113
+
114
+ when '--create-group'
115
+ @options.action = :create_group
116
+
117
+ when '--rename-group'
118
+ @options.action = :rename_group
119
+ @options.new_name = arg
120
+
121
+ when '--delete-group'
122
+ @options.action = :delete_group
123
+
124
+ when '--add-members'
125
+ @options.action = :add_members
126
+
127
+ when '--remove-members'
128
+ @options.action = :remove_members
129
+
130
+ when '--remove-all-members'
131
+ @options.action = :remove_all
132
+
133
+ when '--file'
134
+ @options.input_file = Pathname.new arg
135
+
136
+ when '--server'
137
+ @options.server = arg
138
+
139
+ when '--port'
140
+ @options.port = arg
141
+
142
+ when '--user'
143
+ @options.user = arg
144
+
145
+ when '--no-verify-cert'
146
+ @options.verify_cert = false
147
+
148
+ when '--timeout'
149
+ @options.timeout = arg
150
+
151
+ when '--no-confirm'
152
+ @options.no_confirm = true
153
+
154
+ when '--debug'
155
+ @debug = true
156
+
157
+ end # case
158
+ end # opts.each
159
+
160
+ @options.group = ARGV.shift
161
+
162
+ # if we were given a file of computer names, read it in
163
+ @options.computers = @options.input_file ? get_computers_from_file : []
164
+
165
+ # and add any computers on the commandline
166
+ @options.computers += ARGV
167
+
168
+ # will we say anything when finished?
169
+ @done_msg = nil
170
+
171
+ end # init
172
+
173
+
174
+
175
+ #####################################
176
+ ###
177
+ ### Do It
178
+ ###
179
+ def run
180
+
181
+ if @options.action == :none
182
+ puts USAGE
183
+ return
184
+ end
185
+
186
+ # use any config settings defined....
187
+ @options.user ||= JSS::CONFIG.api_username
188
+ @options.server ||= JSS::CONFIG.api_server_name
189
+
190
+ raise JSS::MissingDataError, "No JSS Username provided or found in the JSS gem config." unless @options.user
191
+ raise JSS::MissingDataError, "No JSS Server provided or found in the JSS gem config." unless @options.server
192
+
193
+ JSS::API.connect( :server => @options.server,
194
+ :port => @options.port,
195
+ :verify_cert => @options.verify_cert,
196
+ :user => @options.user,
197
+ :pw => @options.getpass,
198
+ :stdin_line => 1,
199
+ :timeout => @options.timeout
200
+ )
201
+
202
+
203
+ if ACTIONS_NEEDING_GROUP.include? @options.action
204
+
205
+ raise JSS::MissingDataError, "Please specify a group name" unless @options.group
206
+
207
+ # get the group from the API
208
+ if @options.action == :create_group
209
+ @group = JSS::ComputerGroup.new :id => :new, :name => @options.group, :type => :static
210
+ else
211
+ @group = JSS::ComputerGroup.new :name => @options.group
212
+ end
213
+
214
+ end # if ACTIONS_NEEDING_GROUP
215
+
216
+ # smart groups can't have some things done to them
217
+ raise InvalidTypeError, "You can't do that to a smart group. Use the JSS WebApp if needed." if ACTIONS_FOR_STATIC_GROUPS_ONLY.include? @options.action and @group.smart?
218
+
219
+
220
+ case @options.action
221
+
222
+ when :list_groups
223
+ list_groups
224
+
225
+ when :list_static
226
+ list_groups :static
227
+
228
+ when :list_smart
229
+ list_groups :smart
230
+
231
+ when :list_members
232
+ list_members
233
+
234
+ when :create_group
235
+ create_group
236
+
237
+ when :rename_group
238
+ rename_group
239
+
240
+ when :delete_group
241
+ delete_group
242
+
243
+ when :add_members
244
+ add_members
245
+
246
+ when :remove_members
247
+ remove_members
248
+
249
+ when :remove_all
250
+ remove_all
251
+
252
+ end # case @options.action
253
+
254
+ puts "Done! #{@done_msg}" if @done_msg
255
+
256
+ end # run
257
+
258
+
259
+ #####################################
260
+ ###
261
+ ### Show Help
262
+ ###
263
+ def show_help
264
+ puts <<-FULLHELP
265
+ A tool for working with computer groups in the JSS.
266
+
267
+ #{USAGE}
268
+
269
+ Options:
270
+ -L, --list-groups - list all computer groups in the JSS
271
+ -s, --list-static - list all static computer groups in the JSS
272
+ -m, --list-smart - list all smart computer groups in the JSS
273
+ -c, --create-group - create a new static computer group in the JSS
274
+ -n, --rename newname - rename the specified computer group to newname
275
+ -d, --delete - delete the specified computer group (static groups only)
276
+ -l, --list-members - list all the computers in the group specified
277
+ -a, --add-members - add the specified computer(s) to the specified group
278
+ -r, --remove-members - remove the specified computer(s) from the specified group
279
+ -R, --remove-all - remove all computers from the specified group
280
+ -f, --file /path/... - read computer names/ids from the file at /path/...
281
+ -S, --server srvr - specify the JSS API server name
282
+ -P, --port portnum - specify the JSS API port
283
+ -U, --user username - specify the JSS API user
284
+ -V, --no-verify-cert - Allow self-signed, unverified SSL certificate
285
+ -T, --timeout secs - specify the JSS API timeout
286
+ -C - don't ask for confirmation before acting
287
+ --debug - show the ruby backtrace when errors occur
288
+ -H, --help - show this help
289
+
290
+ Notes:
291
+
292
+ - If no API settings are provided, they will be read from /etc/jss_gem.conf
293
+ and ~/.jss_gem.conf. See the JSS Gem docs for details.
294
+
295
+ - The password for the connection will be read from STDIN or prompted if needed
296
+
297
+ - Computers can be specified by name or JSS id number. If a name exists
298
+ more than once in the JSS, the machine is skipped. Use IDs to avoid this.
299
+
300
+ - Only static groups can be modified. Use the JSS WebUI for editing smart groups
301
+
302
+ - If a file is used to specify computers, they are combined with any
303
+ specified on the commandline.
304
+
305
+ - Files of computers must be whitespace-separated
306
+ (spaces, tabs, & returns in any number or combination)
307
+
308
+ FULLHELP
309
+ return
310
+ end
311
+
312
+ #####################################
313
+ ###
314
+ ### Spit out a list of all computer groups
315
+ ###
316
+ def list_groups(show = :all)
317
+ case show
318
+ when :all
319
+ label = "All"
320
+ groups_to_show = JSS::ComputerGroup.all
321
+ when :static
322
+ label = "Static"
323
+ groups_to_show = JSS::ComputerGroup.all_static
324
+ when :smart
325
+ label = "Smart"
326
+ groups_to_show = JSS::ComputerGroup.all_smart
327
+ end #case
328
+
329
+ puts "# #{label} computer groups in the JSS"
330
+ puts "#---------------------------------------------"
331
+
332
+ groups_to_show.sort{|a,b| a[:name].downcase <=> b[:name].downcase}.each do |grp|
333
+ puts grp[:name]
334
+ end
335
+ end
336
+
337
+ #####################################
338
+ ###
339
+ ### Spit out a list of all computers in a group
340
+ ###
341
+ def list_members
342
+ puts "# All members of JSS #{@group.smart? ? 'smart' : 'static'} computer group '#{@options.group}'"
343
+ puts "#--- name (id) ---------------------------------"
344
+
345
+ # put them into a tmp array, so that
346
+ # we can sort by computer name, remembering that
347
+ # there can be duplicate names.
348
+ list = []
349
+ @group.members.each{|mem| list << "#{mem[:name]} (#{mem[:id]})" }
350
+ puts list.sort #.join("\n")
351
+ end
352
+
353
+
354
+ #####################################
355
+ ###
356
+ ### Create a new group
357
+ ###
358
+ def create_group
359
+
360
+ return unless confirm "create a new static group named '#{@options.group}'"
361
+ @group.create
362
+
363
+ unless @options.computers.empty?
364
+ add_members
365
+ end
366
+
367
+ end
368
+
369
+ #####################################
370
+ ###
371
+ ### rename a group
372
+ ###
373
+ def rename_group
374
+ return unless confirm "rename group '#{@group.name}' to '#{@options.new_name}'"
375
+ @group.name = @options.new_name
376
+ @group.update
377
+ end
378
+
379
+
380
+ #####################################
381
+ ###
382
+ ### delete a group
383
+ ###
384
+ def delete_group
385
+ return unless confirm "DELETE group '#{@group.name}'"
386
+ @group.delete
387
+ end
388
+
389
+
390
+ #####################################
391
+ ###
392
+ ### add members to a group
393
+ ###
394
+ def add_members
395
+ raise JSS::MissingDataError, "No computer names provided" if @options.computers.empty?
396
+ raise JSS::UnsupportedError, "Smart group members can't be changed." if @group.smart?
397
+ return unless @options.action == :create_group or confirm "add computers to group '#{@group.name}'"
398
+
399
+ @options.computers.each do |c|
400
+ begin
401
+ @group.add_member c
402
+ rescue JSS::NoSuchItemError
403
+ puts "#{$!} - skipping"
404
+ end # begin
405
+ end # each
406
+
407
+ @group.update
408
+ end
409
+
410
+ #####################################
411
+ ###
412
+ ### remove members from a group
413
+ ###
414
+ def remove_members
415
+ raise JSS::MissingDataError, "No computer names provided" if @options.computers.empty?
416
+ raise JSS::UnsupportedError, "Smart group members can't be changed." if @group.smart?
417
+ return unless confirm "remove computers from group '#{@group.name}'"
418
+ @options.computers.each do |c|
419
+ begin
420
+ @group.remove_member c
421
+ rescue JSS::NoSuchItemError
422
+ puts "#{$!} - skipping"
423
+ end
424
+ end
425
+ @group.update
426
+ end
427
+
428
+ #####################################
429
+ ###
430
+ ### remove all members from a group
431
+ ###
432
+ def remove_all
433
+ raise JSS::UnsupportedError, "Smart group members can't be changed." if @group.smart?
434
+ return unless confirm "remove ALL computers from group '#{@group.name}'"
435
+ @group.clear
436
+ @group.update
437
+ end
438
+
439
+
440
+ #####################################
441
+ ###
442
+ ### Read computer names from a file
443
+ ### Generally the names should be one per line, but
444
+ ### they can be separated by any whitespace.
445
+ ### Returns an array of computer names from the file.
446
+ ###
447
+ def get_computers_from_file
448
+ raise JSS::NoSuchItemError "File #{@options.input_file} isn't a file or isn't readable." unless \
449
+ @options.input_file.file? and @options.input_file.readable?
450
+ @options.input_file.read.split(/\s+/)
451
+ end
452
+
453
+ #####################################
454
+ ###
455
+ ### Get confirmation before doing something
456
+ ### Returns true or false
457
+ ###
458
+ def confirm (action)
459
+ return true if @options.no_confirm
460
+
461
+ print "Really #{action}? (y/n): "
462
+ $stdin.reopen '/dev/tty'
463
+ reply = $stdin.gets.strip
464
+ return true if reply =~ /^y/i
465
+ return false
466
+
467
+ end # confirm
468
+
469
+
470
+ end # class App
471
+
472
+ #######################################
473
+ begin
474
+ app = App.new(ARGV)
475
+ app.run
476
+
477
+ rescue
478
+ # handle exceptions not handled elsewhere
479
+ puts "An error occurred: #{$!}"
480
+ puts "Backtrace:" if app.debug
481
+ puts $@ if app.debug
482
+
483
+ ensure
484
+
485
+ end