jss-api 0.5.4

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.
Files changed (65) hide show
  1. data/CHANGES.md +4 -0
  2. data/LICENSE.txt +174 -0
  3. data/README.md +368 -0
  4. data/THANKS.md +6 -0
  5. data/lib/jss-api.rb +549 -0
  6. data/lib/jss-api/api_connection.rb +326 -0
  7. data/lib/jss-api/api_object.rb +590 -0
  8. data/lib/jss-api/api_object/advanced_search.rb +389 -0
  9. data/lib/jss-api/api_object/advanced_search/advanced_computer_search.rb +95 -0
  10. data/lib/jss-api/api_object/advanced_search/advanced_mobile_device_search.rb +96 -0
  11. data/lib/jss-api/api_object/advanced_search/advanced_user_search.rb +95 -0
  12. data/lib/jss-api/api_object/building.rb +92 -0
  13. data/lib/jss-api/api_object/category.rb +147 -0
  14. data/lib/jss-api/api_object/computer.rb +852 -0
  15. data/lib/jss-api/api_object/creatable.rb +98 -0
  16. data/lib/jss-api/api_object/criteriable.rb +189 -0
  17. data/lib/jss-api/api_object/criteriable/criteria.rb +231 -0
  18. data/lib/jss-api/api_object/criteriable/criterion.rb +228 -0
  19. data/lib/jss-api/api_object/department.rb +93 -0
  20. data/lib/jss-api/api_object/distribution_point.rb +490 -0
  21. data/lib/jss-api/api_object/extendable.rb +221 -0
  22. data/lib/jss-api/api_object/extension_attribute.rb +457 -0
  23. data/lib/jss-api/api_object/extension_attribute/computer_extension_attribute.rb +362 -0
  24. data/lib/jss-api/api_object/extension_attribute/mobile_device_extension_attribute.rb +189 -0
  25. data/lib/jss-api/api_object/extension_attribute/user_extension_attribute.rb +117 -0
  26. data/lib/jss-api/api_object/group.rb +380 -0
  27. data/lib/jss-api/api_object/group/computer_group.rb +124 -0
  28. data/lib/jss-api/api_object/group/mobile_device_group.rb +139 -0
  29. data/lib/jss-api/api_object/group/user_group.rb +139 -0
  30. data/lib/jss-api/api_object/ldap_server.rb +535 -0
  31. data/lib/jss-api/api_object/locatable.rb +268 -0
  32. data/lib/jss-api/api_object/matchable.rb +97 -0
  33. data/lib/jss-api/api_object/mobile_device.rb +556 -0
  34. data/lib/jss-api/api_object/netboot_server.rb +148 -0
  35. data/lib/jss-api/api_object/network_segment.rb +414 -0
  36. data/lib/jss-api/api_object/package.rb +760 -0
  37. data/lib/jss-api/api_object/peripheral.rb +335 -0
  38. data/lib/jss-api/api_object/peripheral_type.rb +295 -0
  39. data/lib/jss-api/api_object/policy.rb +882 -0
  40. data/lib/jss-api/api_object/purchasable.rb +316 -0
  41. data/lib/jss-api/api_object/removable_macaddr.rb +98 -0
  42. data/lib/jss-api/api_object/scopable.rb +136 -0
  43. data/lib/jss-api/api_object/scopable/scope.rb +621 -0
  44. data/lib/jss-api/api_object/script.rb +631 -0
  45. data/lib/jss-api/api_object/site.rb +93 -0
  46. data/lib/jss-api/api_object/software_update_server.rb +109 -0
  47. data/lib/jss-api/api_object/updatable.rb +117 -0
  48. data/lib/jss-api/api_object/uploadable.rb +138 -0
  49. data/lib/jss-api/api_object/user.rb +272 -0
  50. data/lib/jss-api/client.rb +500 -0
  51. data/lib/jss-api/compatibility.rb +66 -0
  52. data/lib/jss-api/composer.rb +171 -0
  53. data/lib/jss-api/configuration.rb +301 -0
  54. data/lib/jss-api/db_connection.rb +243 -0
  55. data/lib/jss-api/exceptions.rb +83 -0
  56. data/lib/jss-api/ruby_extensions.rb +35 -0
  57. data/lib/jss-api/ruby_extensions/filetest.rb +43 -0
  58. data/lib/jss-api/ruby_extensions/hash.rb +79 -0
  59. data/lib/jss-api/ruby_extensions/ipaddr.rb +91 -0
  60. data/lib/jss-api/ruby_extensions/pathname.rb +77 -0
  61. data/lib/jss-api/ruby_extensions/string.rb +43 -0
  62. data/lib/jss-api/ruby_extensions/time.rb +63 -0
  63. data/lib/jss-api/server.rb +108 -0
  64. data/lib/jss-api/version.rb +31 -0
  65. metadata +219 -0
@@ -0,0 +1,760 @@
1
+ ### Copyright 2014 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
+ ### Module Constants
30
+ #####################################
31
+
32
+ #####################################
33
+ ### Module Variables
34
+ #####################################
35
+
36
+ #####################################
37
+ ### Module Methods
38
+ #####################################
39
+
40
+ #####################################
41
+ ### Classes
42
+ #####################################
43
+
44
+ ###
45
+ ### A Package in the JSS
46
+ ###
47
+ ### Also the API provides no access to the package's
48
+ ### file list (index), so indexing must be done separately (usually via Casper Admin)
49
+ ###
50
+ ###
51
+ ### @see JSS::APIObject
52
+ ###
53
+ class Package < JSS::APIObject
54
+
55
+ #####################################
56
+ ### Mix-Ins
57
+ #####################################
58
+
59
+ include JSS::Creatable
60
+ include JSS::Updatable
61
+
62
+ #####################################
63
+ ### Class Methods
64
+ #####################################
65
+
66
+ #####################################
67
+ ### Class Constants
68
+ #####################################
69
+
70
+ ### The base for REST resources of this class
71
+ RSRC_BASE = "packages"
72
+
73
+ ### the hash key used for the JSON list output of all objects in the JSS
74
+ RSRC_LIST_KEY = :packages
75
+
76
+ ### The hash key used for the JSON object output.
77
+ ### It's also used in various error messages
78
+ RSRC_OBJECT_KEY = :package
79
+
80
+ ### these keys, as well as :id and :name, are present in valid API JSON data for this class
81
+ VALID_DATA_KEYS = [:fill_existing_users, :fill_user_template, :reboot_required ]
82
+
83
+ ### The pkg storage folder on the distribution point
84
+ DIST_POINT_PKGS_FOLDER = "Packages"
85
+
86
+ ### The possible values for cpu_type (required_processor) in a JSS package
87
+ CPU_TYPES = ["None", "x86", "ppc"]
88
+
89
+ ### which is default? there must be one to make a new pkg
90
+ DEFAULT_CPU_TYPE = "None"
91
+
92
+ ### the possible priorities
93
+ PRIORITIES = (1..20)
94
+
95
+ ### the default priority, since one is needed for making new pkgs
96
+ DEFAULT_PRIORITY = 10
97
+
98
+ ### by default, no processor requirement
99
+ DEFAULT_PROCESSOR = "None"
100
+
101
+ ### When we shouldn't install anything (e.g. switch w/package)
102
+ DO_NOT_INSTALL = "Do Not Install"
103
+
104
+ #####################################
105
+ ### Class Variables
106
+ #####################################
107
+
108
+ #####################################
109
+ ### Class Methods
110
+ #####################################
111
+
112
+ #####################################
113
+ ### Attributes
114
+ #####################################
115
+
116
+ ### @return [String] the filename of the .pkg, .mpkg, or .dmg on the Casper server
117
+ attr_reader :filename
118
+
119
+ ### @return [Pathname] the local receipt when this pkg is installed
120
+ attr_reader :receipt
121
+
122
+ ### @return [Boolean] does this item 'Fill Existing Users' when jamf installs it?
123
+ attr_reader :fill_existing_users
124
+
125
+ ### @return [Boolean] does this pkg also get install in the OS user homedir template
126
+ attr_reader :fill_user_template
127
+
128
+ ### @return [Boolean] does this item require a reboot after installation? If so, it'll be a puppy-install in d3
129
+ attr_reader :reboot_required
130
+
131
+ ### @return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
132
+ attr_reader :os_requirements
133
+
134
+ ### @return [String] limit installation to these architectures: 'x86', 'ppc', 'None'
135
+ attr_reader :required_processor
136
+
137
+ ### @return [String] the name of a pkg to install (or "Do Not Install") when this pkg can't be installed
138
+ attr_reader :switch_with_package
139
+
140
+ ### @return [Boolean] can this item be uninstalled? Some, e.g. OS Updates, can't
141
+ attr_reader :allow_uninstalled
142
+
143
+ ### @return [String] the category of this pkg, stored in the JSS as the id number from the categories table
144
+ attr_reader :category
145
+
146
+ ### @return [String] the info field for this pkg - stores d3's basename & swupdate values
147
+ attr_reader :info
148
+
149
+ ### @return [String] the notes field for this pkg
150
+ attr_reader :notes
151
+
152
+ ### @return [Boolean] only install this pkg if it's available in the commandline softwareupdate.
153
+ attr_reader :install_if_reported_available
154
+
155
+ ### @return [Boolean] should this pkg be installed on the boot volume during imaging
156
+ attr_reader :boot_volume_required
157
+
158
+ ### @return [Integer] Priority to use for deploying or uninstalling the package
159
+ attr_reader :priority
160
+
161
+ ### @return [Boolean] does this pkg cause a notification to be sent on self-heal?
162
+ attr_reader :send_notification
163
+
164
+
165
+ ###
166
+ ### @see JSS::APIObject#initialize
167
+ ###
168
+ def initialize (args = {})
169
+
170
+ super
171
+
172
+ ### now we have pkg_data with something in it, so fill out the instance vars
173
+ @allow_uninstalled = @init_data[:allow_uninstalled]
174
+ @boot_volume_required = @init_data[:boot_volume_required]
175
+ @category = JSS::APIObject.get_name(@init_data[:category])
176
+ @filename = @init_data[:filename] || @init_data[:name]
177
+ @fill_existing_users = @init_data[:fill_existing_users]
178
+ @fill_user_template = @init_data[:fill_user_template]
179
+ @info = @init_data[:info]
180
+ @install_if_reported_available = @init_data[:install_if_reported_available]
181
+ @notes = @init_data[:notes]
182
+ @os_requirements = @init_data[:os_requirements].split(/\s*,\s*/) if @init_data[:os_requirements]
183
+ @priority = @init_data[:priority] || DEFAULT_PRIORITY
184
+ @reboot_required = @init_data[:reboot_required]
185
+ @required_processor = @init_data[:required_processor] || DEFAULT_CPU_TYPE
186
+ @send_notification = @init_data[:send_notification]
187
+ @switch_with_package = @init_data[:switch_with_package] || DO_NOT_INSTALL
188
+
189
+ # the receipt is the filename with any .zip extension removed.
190
+ @receipt = @filname ? (JSS::Client::RECEIPTS_FOLDER + @filname.to_s.sub(/.zip$/, '')) : nil
191
+
192
+ end # init
193
+
194
+
195
+
196
+ ###
197
+ ### Change the 'allow to be uninstalled' field in the JSS
198
+ ### NOTE The package must be indexed before this works. Right now, that means
199
+ ### using CasperAdmin.app
200
+ ###
201
+ ### @param new_val[Boolean]
202
+ ###
203
+ ### @return [void]
204
+ ###
205
+ def allow_uninstalled= (new_val)
206
+ return nil if new_val == @allow_uninstalled
207
+
208
+ ### removable? defaults to false
209
+ ### even though we usually want to be able to ununstall things, it would be
210
+ ### dangerous to do on things like OS updates, so it must be turned on explicitly.
211
+ ### packages must be indexed with Casper Admin in order to be uninstalled.
212
+ new_val = false if new_val.to_s.empty?
213
+ raise JSS::InvalidDataError, "allow_uninstalled must be boolean 'true' or 'false'" unless JSS::TRUE_FALSE.include? new_val
214
+
215
+ @allow_uninstalled= new_val
216
+ @need_to_update = true
217
+
218
+ end
219
+
220
+
221
+ ###
222
+ ### Change the boot volume required field in the JSS
223
+ ###
224
+ ### @param new_val[Boolean]
225
+ ###
226
+ ### @return [void]
227
+ ###
228
+ def boot_volume_required=(new_val)
229
+ return nil if new_val == @boot_volume_required
230
+ new_val = false if new_val.to_s.empty?
231
+ raise JSS::InvalidDataError, "install_if_reported_available must be boolean true or false" unless JSS::TRUE_FALSE.include? new_val
232
+ @boot_volume_required = new_val
233
+ @need_to_update = true
234
+ end
235
+
236
+
237
+ ###
238
+ ### Change the category in the JSS
239
+ ###
240
+ ### @param new_val[String] must be one listed by 'JSS::Category.all_names'
241
+ ###
242
+ ### @return [void]
243
+ ###
244
+ def category= (new_val)
245
+ return nil if new_val == @category
246
+ new_val = nil if new_val == ''
247
+ new_val ||= JSS::Category::DEFAULT_CATEGORY
248
+ raise JSS::InvalidDataError, "Category #{new_val} is not known to the JSS" unless JSS::Category.all_names.include? new_val
249
+ @category = new_val
250
+ @need_to_update = true
251
+ end
252
+
253
+ ###
254
+ ### Change the package filename.
255
+ ### Setting it to nil or empty will make it match the display name
256
+ ###
257
+ ### @param new_val[String]
258
+ ###
259
+ ### @return [void]
260
+ ###
261
+ def filename= (new_val)
262
+ new_val = nil if new_val == ''
263
+ new_val ||= @name
264
+ return nil if new_val == @filename
265
+ $stderr.puts "WARNING: you must manualy change the filename on the Distribution Point(s)" if @in_jss
266
+ @filename = new_val
267
+ @need_to_update = true
268
+ end
269
+
270
+
271
+ ###
272
+ ### Change the Fill Existing Users value
273
+ ###
274
+ ### @param new_val[Boolean]
275
+ ###
276
+ ### @return [void]
277
+ ###
278
+ def fill_existing_users= (new_val)
279
+ return nil if new_val == @fill_existing_users
280
+ new_val = false if new_val.to_s.empty?
281
+ raise JSS::InvalidDataError, "fill_existing_users must be boolean 'true' or 'false'" unless JSS::TRUE_FALSE.include? new_val
282
+ @fill_existing_users = new_val
283
+ @need_to_update = true
284
+ end
285
+
286
+ ###
287
+ ### Change the fill_user_template value
288
+ ###
289
+ ### @param new_val[Boolean]
290
+ ###
291
+ ### @return [void]
292
+ ###
293
+ def fill_user_template= (new_val)
294
+ return nil if new_val == @fill_user_template
295
+ new_val = false if new_val.to_s.empty?
296
+ raise JSS::InvalidDataError, "fill_user_template must be boolean 'true' or 'false'" unless JSS::TRUE_FALSE.include? new_val
297
+ @fill_user_template = new_val
298
+ @need_to_update = true
299
+ end
300
+
301
+
302
+
303
+ ###
304
+ ### Change the info field in the JSS.
305
+ ###
306
+ ### @param new_val[String]
307
+ ###
308
+ ### @return [void]
309
+ ###
310
+ def info= (new_val)
311
+ return nil if new_val == @info
312
+ ### line breaks should be \r
313
+ new_val = new_val.to_s.gsub(/\n/, "\r")
314
+ @info = new_val
315
+ @need_to_update = true
316
+ end
317
+
318
+
319
+ ###
320
+ ### Change the if_in_swupdate field in the JSS
321
+ ###
322
+ ### @param new_val[Boolean]
323
+ ###
324
+ ### @return [void]
325
+ ###
326
+ def install_if_reported_available= (new_val)
327
+ return nil if new_val == @install_if_reported_available
328
+ new_val = false if new_val.to_s.empty?
329
+ raise JSS::InvalidDataError, "install_if_reported_available must be boolean true or false" unless JSS::TRUE_FALSE.include? new_val
330
+ @install_if_reported_available = new_val
331
+ @need_to_update = true
332
+ end
333
+
334
+
335
+
336
+ ###
337
+ ### Change the notes field in the JSS.NewLines are converted \r.
338
+ ###
339
+ ### @param new_val[String]
340
+ ###
341
+ ### @return [void]
342
+ ###
343
+ def notes= (new_val)
344
+ return nil if new_val == @notes
345
+ ### line breaks should be \r
346
+ new_val = new_val.to_s.gsub(/\n/, "\r")
347
+ @notes = new_val
348
+ @need_to_update = true
349
+ end
350
+
351
+ ###
352
+ ### Change the os_requirements field in the JSS
353
+ ### E.g. 10.5, 10.5.3, 10.6.x
354
+ ###
355
+ ### @param new_val[String,Array] comma-separated string, or array of os versions
356
+ ###
357
+ ### @return [void]
358
+ ###
359
+ ### Extra feature: Minumum OS's can now be specified as a
360
+ ### string using the notation ">=10.6.7".
361
+ ###
362
+ ### @see JSS.expand_min_os
363
+ ###
364
+ def os_requirements= (new_val)
365
+ ### nil should be an empty array
366
+ new_val = [] if new_val.to_s.empty?
367
+
368
+ ### if any value starts with >=, expand it
369
+ case new_val
370
+ when String
371
+ new_val = JSS.expand_min_os(new_val) if new_val =~ /^>=/
372
+ when Array
373
+ new_val.map!{|a| a =~ /^>=/ ? JSS.expand_min_os(a) : a }
374
+ new_val.flatten!
375
+ new_val.uniq!
376
+ else
377
+ raise JSS::InvalidDataError, "os_requirements must be a String or an Array of strings"
378
+ end
379
+ ### get the array version
380
+ @os_requirements = JSS.to_s_and_a(new_val)[:arrayform]
381
+ @need_to_update = true
382
+ end
383
+
384
+
385
+ ###
386
+ ### Change the priority field in the JSS
387
+ ###
388
+ ### @param new_val[Integer] one of PRIORITIES
389
+ ###
390
+ ### @return [void]
391
+ ###
392
+ def priority= (new_val)
393
+ return nil if new_val == @priority
394
+ new_val = DEFAULT_PRIORITY if new_val.to_s.empty?
395
+ raise JSS::InvalidDataError, ":priority must be an integer from 1-20" unless PRIORITIES.include? new_val
396
+ @priority = new_val
397
+ @need_to_update = true
398
+ end
399
+
400
+ ###
401
+ ### Change the reboot-required field in the JSS
402
+ ###
403
+ ### @param new_val[Boolean]
404
+ ###
405
+ ### @return [void]
406
+ ###
407
+ def reboot_required= (new_val)
408
+ return nil if new_val == @reboot_required
409
+ new_val = false if new_val.to_s.empty?
410
+ raise JSS::InvalidDataError, "reboot must be boolean 'true' or 'false'" unless JSS::TRUE_FALSE.include? new_val
411
+ @reboot_required = new_val
412
+ @need_to_update = true
413
+ end
414
+
415
+
416
+
417
+ ###
418
+ ### Change the required processor field in the JSS
419
+ ###
420
+ ### @param new_val[String] one of {CPU_TYPES}
421
+ ###
422
+ ### @return [void]
423
+ ###
424
+ def required_processor= (new_val)
425
+ return nil if new_val == @required_processor
426
+ new_val = validate_for_server(:required_processor => new_val)[:required_processor]
427
+
428
+ new_val = DEFAULT_PROCESSOR if new_val.to_s.empty?
429
+ raise JSS::InvalidDataError, "Required_processor must be one of: #{CPU_TYPES.join ', '}" unless CPU_TYPES.include? new_val
430
+
431
+ @required_processor = new_val
432
+ @need_to_update = true
433
+ end
434
+
435
+
436
+ ###
437
+ ### Change the notify field in the JSS
438
+ ###
439
+ ### @param new_val[Boolean]
440
+ ###
441
+ ### @return [void]
442
+ ###
443
+ def send_notification= (new_val)
444
+ return nil if new_val == @send_notification
445
+ new_val = false if new_val.to_s.empty?
446
+ raise JSS::InvalidDataError, "send_notification must be boolean true or false" unless JSS::TRUE_FALSE.include? new_val
447
+ @send_notification = new_val
448
+ @need_to_update = true
449
+ end
450
+
451
+
452
+
453
+ ###
454
+ ### Change which pkg should be installed if this one can't.
455
+ ###
456
+ ### @param new_val[String] the name of an existing package or "Do Not Install"
457
+ ###
458
+ ### @return [void]
459
+ ###
460
+ def switch_with_package= (new_val)
461
+ return nil if new_val == @switch_with_package
462
+ new_val = nil if new_val.to_s.empty?
463
+
464
+ raise JSS::NoSuchItemError, "No package named '#{new_val}' exists in the JSS" if new_val and not self.class.all_names.include? new_val
465
+
466
+ new_val ||= DO_NOT_INSTALL
467
+ @switch_with_package = new_val
468
+ @need_to_update = true
469
+ end
470
+
471
+ ###
472
+ ### Is this packaged installed on the current machine (via casper)?
473
+ ### We just look for the receipt, which is the @filename less any possible .zip extension.
474
+ ###
475
+ ### @return [Boolean]
476
+ ###
477
+ def installed?
478
+ @receipt.file?
479
+ end
480
+
481
+ ###
482
+ ### Upload a locally-readable file to the master distribution point.
483
+ ### If the file is a directory (like a bundle .pk/.mpkg) it will be zipped before
484
+ ### uploading and the @filename will be adjusted accordingly
485
+ ###
486
+ ### If you'll be uploading several files you can specify unmount as false, and do it manually when all
487
+ ### are finished with JSS::DistributionPoint.master_distribution_point.unmount
488
+ ###
489
+ ### @param local_file_path[String,Pathname] the local path to the file to be uploaded
490
+ ###
491
+ ### @param rw_pw[String,Symbol] the password for the read/write account on the master Distribution Point,
492
+ ### or :prompt, or :stdin# where # is the line of stdin containing the password See {JSS::DistributionPoint#mount}
493
+ ###
494
+ ### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
495
+ ###
496
+ ### @return [void]
497
+ ###
498
+ def upload_master_file (local_file_path, rw_pw, unmount = true)
499
+
500
+ raise JSS::NoSuchItemError, "Please create this package in the JSS before uploading it." unless @in_jss
501
+
502
+ mdp = JSS::DistributionPoint.master_distribution_point
503
+ destination = mdp.mount(rw_pw, :rw) +"#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
504
+
505
+ local_path = Pathname.new local_file_path
506
+ raise JSS::NoSuchItemError, "Local file '#{@local_file}' doesn't exist" unless local_path.exist?
507
+
508
+ ### should we zip it?
509
+ if local_path.directory?
510
+ begin
511
+ zipdir = Pathname.new "/tmp/jssgemtmp-#{Time.new.strftime "%Y%m%d%H%M%S"}-#{$$}"
512
+ zipdir.mkpath
513
+ zipdir.chmod 0700
514
+ zipfile = zipdir + (local_path.basename.to_s + ".zip")
515
+
516
+ ### go to the same dir as the local file
517
+ wd = Dir.pwd
518
+ Dir.chdir local_path.parent
519
+
520
+ ### the contents of the zip file have to have the same name as the zip file itself (minus the .zip)
521
+ ### so temporarily rename the source
522
+ local_path.rename(local_path.parent + @filename)
523
+ raise "There was a problem zipping the pkg bundle" unless system "/usr/bin/zip -qr '#{zipfile}' '#{@filename}'"
524
+
525
+ ensure
526
+ ### rename the source to the original name
527
+ (local_path.parent + @filename).rename local_path
528
+ ### go back where we started
529
+ Dir.chdir wd
530
+ end # begin
531
+
532
+ ### update our info
533
+ local_path = zipfile
534
+
535
+ self.filename = zipfile.basename.to_s
536
+ self.update
537
+ end # if directory
538
+
539
+
540
+
541
+ FileUtils.copy_entry local_path, destination
542
+
543
+ mdp.unmount if unmount
544
+ end # upload
545
+
546
+ ###
547
+ ### Delete the filename from the master distribution point, if it exists.
548
+ ###
549
+ ### If you'll be uploading several files you can specify unmount as false, and do it manually when all
550
+ ### are finished.
551
+ ###
552
+ ### @param rw_pw[String] the password for the read/write account on the master Distribution Point
553
+ ### or :prompt, or :stdin# where # is the line of stdin containing the password. See {JSS::DistributionPoint#mount}
554
+ ###
555
+ ### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
556
+ ###
557
+ ### @return [Boolean] was the file deleted?
558
+ ###
559
+ def delete_master_file (rw_pw, unmount = true)
560
+ mdp = JSS::DistributionPoint.master_distribution_point
561
+ file = mdp.mount(rw_pw, :rw) +"#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
562
+ if file.exist?
563
+ file.delete
564
+ did_it = true
565
+ else
566
+ did_it = false
567
+ end # if exists
568
+ mdp.unmount if unmount
569
+ return did_it
570
+ end # delete master file
571
+
572
+
573
+ ###
574
+ ### @note This code must be run as root to install packages
575
+ ###
576
+ ### Causes the pkg/dmg to be installed via the jamf binary 'install' command from the
577
+ ### distribution point for this machine. See {JSS::DistributionPoint.my_distribution_point}
578
+ ###
579
+ ### The read-only or http passwd for the dist. point must be provided, except for
580
+ ### non-authenticated http downloads)
581
+ ###
582
+ ### @param args[Hash] the arguments for installation
583
+ ###
584
+ ### @option args :ro_pw[String] the read-only or http password for the distribution point for the
585
+ ### local machine (http will be used if available, and may not need a pw)
586
+ ###
587
+ ### @option args :target[String,Pathname] The drive on which to install the package, defaults to '/'
588
+ ###
589
+ ### @option args :verbose [Boolean] be verbose to stdout, defaults to false
590
+ ###
591
+ ### @option args :feu[Boolean] fill existing users, defaults to false
592
+ ###
593
+ ### @option args :fut[Boolean] fill user template, defaults to false
594
+ ###
595
+ ### @option args :unmount[Boolean] unmount the distribution point when finished?(if we mounted it),
596
+ ### defaults to false
597
+ ###
598
+ ### @option args :no_http[Boolean] don't use http downloads even if they are enabled for the dist. point.
599
+ ###
600
+ ### @return [Process::Status] the final status of the jamf binary command
601
+ ###
602
+ ### @todo deal with cert-based https authentication
603
+ ###
604
+ def install (args = {})
605
+
606
+ raise JSS::UnsupportedError, "You must have root privileges to install packages" unless JSS.superuser?
607
+
608
+ args[:target] ||= '/'
609
+
610
+ ro_pw = args[:ro_pw]
611
+
612
+ mdp = JSS::DistributionPoint.my_distribution_point
613
+
614
+ ### how do we access our dist. point? with http?
615
+ if mdp.http_downloads_enabled and (not args[:no_http])
616
+ using_http = true
617
+ src_path = mdp.http_url
618
+ if mdp.username_password_required
619
+ raise JSS::MissingDataError, "No password provided for http download" unless ro_pw
620
+ raise JSS::InvaldDatatError, "Incorrect password for http access to distribution point." unless mdp.check_pw(:http, ro_pw)
621
+ # insert the name and pw into the uri
622
+ reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]") # we'll escape all the chars that aren't unreserved
623
+ src_path = src_path.sub(%r{(https?://)(\S)}, "#{$1}#{URI.escape mdp.http_username,reserved_chars}:#{URI.escape ro_pw, reserved_chars}@#{$2}")
624
+ end
625
+
626
+ # or with filesharing?
627
+ else
628
+ using_http = false
629
+ src_path = mdp.mount(ro_pw)
630
+ end
631
+
632
+ # look at the pkgs folder
633
+ src_path += "/#{DIST_POINT_PKGS_FOLDER}"
634
+
635
+ ### are we doing "fill existing users" or "fill user template"?
636
+ do_feu = args[:feu] ? "-feu" : ""
637
+ do_fut = args[:fut] ? "-fut" : ""
638
+
639
+ ### the install args for jamf
640
+ command_args = "-package '#{@filename}' -path '#{src_path}' -target '#{args[:target]}' #{do_feu} #{do_fut} -showProgress -verbose ; echo jamfexit $?"
641
+
642
+ ### run it via a client
643
+ install_out = JSS::Client.run_jamf :install, command_args, args[:verbose]
644
+
645
+ install_exit = $?
646
+
647
+ if (args.include? :unmount)
648
+ mdp.unmount unless using_http
649
+ end
650
+
651
+ return install_exit
652
+ end
653
+
654
+ ###
655
+ ### @note This code must be run as root to uninstall packages
656
+ ###
657
+ ### Causes the pkg to be uninstalled via the jamf command.
658
+ ###
659
+ ### @param args[Hash] the arguments for installation
660
+ ###
661
+ ### @option args :target[String,Pathname] The drive from which to uninstall the package, defaults to '/'
662
+ ###
663
+ ### @option args :verbose[Boolean] be verbose to stdout, defaults to false
664
+ ###
665
+ ### @option args :feu[Boolean] fill existing users, defaults to false
666
+ ###
667
+ ### @option args :fut[Boolean] fill user template, defaults to false
668
+ ###
669
+ ### @return [Process::Status] the result of the 'jamf uninstall' command
670
+ ###
671
+ def uninstall (args = {})
672
+
673
+ raise JSS::UnsupportedError, \
674
+ "This package cannot be uninstalled. Please use CasperAdmin to index it and allow uninstalls" unless removable?
675
+ raise JSS::UnsupportedError, "You must have root privileges to uninstall packages" unless JSS.superuser?
676
+ args[:target] ||= '/'
677
+
678
+ ### are we doing "fill existing users" or "fill user template"?
679
+ do_feu = args[:feu] ? "-feu" : ""
680
+ do_fut = args[:fut] ? "-fut" : ""
681
+
682
+ ### use jamf binary to uninstall the pkg
683
+ jamf_opts = "-target '#{args[:target]}' -id '#{@id}' #{do_feu} #{do_fut}"
684
+
685
+ ### run it via a client
686
+ uninstall_out = JSS::Client.run_jamf "uninstall", jamf_opts, args[:verbose]
687
+
688
+ return $?
689
+ end
690
+
691
+
692
+
693
+
694
+ ################################
695
+ ### Private Instance Methods
696
+ ################################
697
+
698
+ private
699
+
700
+
701
+ ###
702
+ ### Return the REST XML for this pkg, with the current values,
703
+ ### for saving or updating
704
+ ###
705
+ def rest_xml
706
+ doc = REXML::Document.new APIConnection::XML_HEADER
707
+ pkg = doc.add_element "package"
708
+ pkg.add_element('allow_uninstalled').text = @allow_uninstalled
709
+ pkg.add_element('boot_volume_required').text = @boot_volume_required
710
+ pkg.add_element('category').text = @category
711
+ pkg.add_element('filename').text = @filename
712
+ pkg.add_element('fill_existing_users').text = @fill_existing_users
713
+ pkg.add_element('fill_user_template').text = @fill_user_template
714
+ pkg.add_element('info').text = @info
715
+ pkg.add_element('install_if_reported_available').text = @install_if_reported_available
716
+ pkg.add_element('name').text = @name
717
+ pkg.add_element('notes').text = @notes
718
+ pkg.add_element('os_requirements').text = JSS.to_s_and_a(@os_requirements)[:stringform]
719
+ pkg.add_element('priority').text = @priority
720
+ pkg.add_element('reboot_required').text = @reboot_required
721
+ pkg.add_element('required_processor').text = @required_processor
722
+ pkg.add_element('send_notification').text = @send_notification
723
+ pkg.add_element('switch_with_package').text = @switch_with_package
724
+ return doc.to_s
725
+ end # rest xml
726
+
727
+ public
728
+
729
+ # aliases under their methods seem to confuse the YARD documenter, so I'm putting them all here.
730
+ alias feu fill_existing_users
731
+ alias feu? fill_existing_users
732
+ alias fut fill_user_template
733
+ alias fut? fill_user_template
734
+ alias reboot reboot_required
735
+ alias reboot? reboot_required
736
+ alias oses os_requirements
737
+ alias cpu_type required_processor
738
+ alias removable allow_uninstalled
739
+ alias removable? allow_uninstalled
740
+ alias if_in_swupdate install_if_reported_available
741
+ alias if_in_swupdate? install_if_reported_available
742
+ alias boot boot_volume_required
743
+ alias boot? boot_volume_required
744
+ alias notify send_notification
745
+
746
+ alias removable= allow_uninstalled=
747
+ alias boot= boot_volume_required=
748
+ alias feu= fill_existing_users=
749
+ alias fut= fill_user_template=
750
+ alias if_in_swupdate= install_if_reported_available=
751
+ alias oses= os_requirements=
752
+ alias reboot= reboot_required=
753
+ alias cpu_type= required_processor=
754
+ alias notify= send_notification=
755
+
756
+
757
+
758
+ end # class Package
759
+
760
+ end # module jss