ruby-jss 0.6.3
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.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.yardopts +7 -0
- data/CHANGES.md +112 -0
- data/LICENSE.txt +174 -0
- data/README.md +426 -0
- data/THANKS.md +6 -0
- data/bin/cgrouper +485 -0
- data/bin/subnet-update +400 -0
- data/lib/jss-api.rb +2 -0
- data/lib/jss.rb +190 -0
- data/lib/jss/api_connection.rb +410 -0
- data/lib/jss/api_object.rb +616 -0
- data/lib/jss/api_object/advanced_search.rb +389 -0
- data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +95 -0
- data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +96 -0
- data/lib/jss/api_object/advanced_search/advanced_user_search.rb +95 -0
- data/lib/jss/api_object/building.rb +92 -0
- data/lib/jss/api_object/category.rb +147 -0
- data/lib/jss/api_object/computer.rb +852 -0
- data/lib/jss/api_object/creatable.rb +98 -0
- data/lib/jss/api_object/criteriable.rb +189 -0
- data/lib/jss/api_object/criteriable/criteria.rb +231 -0
- data/lib/jss/api_object/criteriable/criterion.rb +228 -0
- data/lib/jss/api_object/department.rb +93 -0
- data/lib/jss/api_object/distribution_point.rb +560 -0
- data/lib/jss/api_object/extendable.rb +221 -0
- data/lib/jss/api_object/extension_attribute.rb +466 -0
- data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +362 -0
- data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +189 -0
- data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +117 -0
- data/lib/jss/api_object/group.rb +380 -0
- data/lib/jss/api_object/group/computer_group.rb +124 -0
- data/lib/jss/api_object/group/mobile_device_group.rb +139 -0
- data/lib/jss/api_object/group/user_group.rb +139 -0
- data/lib/jss/api_object/ldap_server.rb +535 -0
- data/lib/jss/api_object/locatable.rb +286 -0
- data/lib/jss/api_object/matchable.rb +97 -0
- data/lib/jss/api_object/mobile_device.rb +556 -0
- data/lib/jss/api_object/netboot_server.rb +148 -0
- data/lib/jss/api_object/network_segment.rb +414 -0
- data/lib/jss/api_object/osx_configuration_profile.rb +262 -0
- data/lib/jss/api_object/package.rb +839 -0
- data/lib/jss/api_object/peripheral.rb +335 -0
- data/lib/jss/api_object/peripheral_type.rb +295 -0
- data/lib/jss/api_object/policy.rb +898 -0
- data/lib/jss/api_object/purchasable.rb +316 -0
- data/lib/jss/api_object/removable_macaddr.rb +98 -0
- data/lib/jss/api_object/scopable.rb +136 -0
- data/lib/jss/api_object/scopable/scope.rb +621 -0
- data/lib/jss/api_object/script.rb +631 -0
- data/lib/jss/api_object/self_servable.rb +356 -0
- data/lib/jss/api_object/site.rb +93 -0
- data/lib/jss/api_object/software_update_server.rb +109 -0
- data/lib/jss/api_object/updatable.rb +117 -0
- data/lib/jss/api_object/uploadable.rb +138 -0
- data/lib/jss/api_object/user.rb +272 -0
- data/lib/jss/client.rb +504 -0
- data/lib/jss/compatibility.rb +66 -0
- data/lib/jss/composer.rb +185 -0
- data/lib/jss/configuration.rb +306 -0
- data/lib/jss/db_connection.rb +298 -0
- data/lib/jss/exceptions.rb +95 -0
- data/lib/jss/ruby_extensions.rb +35 -0
- data/lib/jss/ruby_extensions/filetest.rb +43 -0
- data/lib/jss/ruby_extensions/hash.rb +79 -0
- data/lib/jss/ruby_extensions/ipaddr.rb +91 -0
- data/lib/jss/ruby_extensions/pathname.rb +77 -0
- data/lib/jss/ruby_extensions/string.rb +59 -0
- data/lib/jss/ruby_extensions/time.rb +63 -0
- data/lib/jss/server.rb +108 -0
- data/lib/jss/utility.rb +478 -0
- data/lib/jss/version.rb +31 -0
- metadata +187 -0
@@ -0,0 +1,631 @@
|
|
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
|
+
### Module Constants
|
30
|
+
#####################################
|
31
|
+
|
32
|
+
#####################################
|
33
|
+
### Module Variables
|
34
|
+
#####################################
|
35
|
+
|
36
|
+
#####################################
|
37
|
+
### Module Methods
|
38
|
+
#####################################
|
39
|
+
|
40
|
+
#####################################
|
41
|
+
### Classes
|
42
|
+
#####################################
|
43
|
+
|
44
|
+
###
|
45
|
+
### A Script in the JSS.
|
46
|
+
###
|
47
|
+
### As of Casper 9.4, the script contents as stored in the database are
|
48
|
+
### accessible via the API
|
49
|
+
###
|
50
|
+
### This class will save the script contents back to the database with
|
51
|
+
### the {#create} or {#update} methods
|
52
|
+
###
|
53
|
+
### If your scripts are stored on the master distribution point instead of
|
54
|
+
### the database, you can use {#upload_master_file} to save it to the server,
|
55
|
+
### and {#delete_master_file} to delete it from the server.
|
56
|
+
###
|
57
|
+
### Use the {#run} method to run the script on the local machine via the 'jamf runScript' command
|
58
|
+
###
|
59
|
+
### @see JSS::APIObject
|
60
|
+
###
|
61
|
+
class Script < JSS::APIObject
|
62
|
+
|
63
|
+
#####################################
|
64
|
+
### Mix-Ins
|
65
|
+
#####################################
|
66
|
+
|
67
|
+
include JSS::Creatable
|
68
|
+
include JSS::Updatable
|
69
|
+
|
70
|
+
#####################################
|
71
|
+
### Class Methods
|
72
|
+
#####################################
|
73
|
+
|
74
|
+
#####################################
|
75
|
+
### Class Constants
|
76
|
+
#####################################
|
77
|
+
|
78
|
+
### The base for REST resources of this class
|
79
|
+
RSRC_BASE = "scripts"
|
80
|
+
|
81
|
+
### the hash key used for the JSON list output of all objects in the JSS
|
82
|
+
RSRC_LIST_KEY = :scripts
|
83
|
+
|
84
|
+
### The hash key used for the JSON object output.
|
85
|
+
### It's also used in various error messages
|
86
|
+
RSRC_OBJECT_KEY = :script
|
87
|
+
|
88
|
+
### these keys, as well as :id and :name, are present in valid API JSON data for this class
|
89
|
+
VALID_DATA_KEYS = [:parameters, :filename, :os_requirements ]
|
90
|
+
|
91
|
+
### The script storage folder on the distribution point, if used
|
92
|
+
DIST_POINT_SCRIPTS_FOLDER = "Scripts"
|
93
|
+
|
94
|
+
### Priority to use for running the script in relation to other actions during imaging
|
95
|
+
PRIORITIES = [ 'Before', 'After','At Reboot']
|
96
|
+
|
97
|
+
### which is default?
|
98
|
+
DEFAULT_PRIORITY = "After"
|
99
|
+
|
100
|
+
### The keys used in the @parameters Hash
|
101
|
+
PARAMETER_KEYS = [:parameter4, :parameter5, :parameter6,:parameter7, :parameter8, :parameter9, :parameter10, :parameter11]
|
102
|
+
|
103
|
+
#####################################
|
104
|
+
### Attributes
|
105
|
+
#####################################
|
106
|
+
|
107
|
+
### @return [String] the file name of the script, if stored in a dist. point
|
108
|
+
attr_reader :filename
|
109
|
+
|
110
|
+
### @return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
|
111
|
+
attr_reader :os_requirements
|
112
|
+
|
113
|
+
### @return [String] either 'Before' or 'After' or "At Reboot".
|
114
|
+
attr_reader :priority
|
115
|
+
|
116
|
+
### @return [String] the info field for this script
|
117
|
+
attr_reader :info
|
118
|
+
|
119
|
+
### @return [String] the notes field for this script
|
120
|
+
attr_reader :notes
|
121
|
+
|
122
|
+
### @return [String] the category of this script, stored in the JSS as the id number from the categories table
|
123
|
+
attr_reader :category
|
124
|
+
|
125
|
+
### @return [Hash] script parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
|
126
|
+
attr_reader :parameters
|
127
|
+
|
128
|
+
### @return {String] the actual code for this script, if it's stored in the database.
|
129
|
+
attr_reader :script_contents
|
130
|
+
|
131
|
+
|
132
|
+
#####################################
|
133
|
+
### Constructor
|
134
|
+
#####################################
|
135
|
+
|
136
|
+
###
|
137
|
+
###
|
138
|
+
###
|
139
|
+
def initialize (args = {})
|
140
|
+
super
|
141
|
+
|
142
|
+
@category = JSS::APIObject.get_name(@init_data[:category])
|
143
|
+
@filename = @init_data[:filename] || @name
|
144
|
+
@info = @init_data[:info]
|
145
|
+
@notes = @init_data[:notes]
|
146
|
+
@os_requirements = @init_data[:os_requirements] ? JSS.to_s_and_a(@init_data[:os_requirements] )[:arrayform] : []
|
147
|
+
@parameters = @init_data[:parameters] ? @init_data[:parameters] : {}
|
148
|
+
@priority = @init_data[:priority] || DEFAULT_PRIORITY
|
149
|
+
@script_contents = @init_data[:script_contents]
|
150
|
+
|
151
|
+
end # initialize
|
152
|
+
|
153
|
+
###
|
154
|
+
### Change the script filename
|
155
|
+
###
|
156
|
+
### Setting it to nil will make it match the script name
|
157
|
+
###
|
158
|
+
### @param new_val[String,Nil] the new filename
|
159
|
+
###
|
160
|
+
### @return [void]
|
161
|
+
###
|
162
|
+
### @note This method does NOT change the filename on the distribution point
|
163
|
+
### if that's where you store your scripts.
|
164
|
+
###
|
165
|
+
def filename= (new_val)
|
166
|
+
|
167
|
+
new_val = nil if new_val == ''
|
168
|
+
new_val = @name unless new_val
|
169
|
+
|
170
|
+
return nil if new_val == @filename
|
171
|
+
|
172
|
+
@filename = new_val
|
173
|
+
@need_to_update = true
|
174
|
+
end #filename=
|
175
|
+
|
176
|
+
###
|
177
|
+
### Change the script's display name
|
178
|
+
###
|
179
|
+
### If the filename is the same as the name, the filename will be changed also
|
180
|
+
###
|
181
|
+
### @param new_val[String] the new display name
|
182
|
+
###
|
183
|
+
### @return [void]
|
184
|
+
###
|
185
|
+
def name= (new_val)
|
186
|
+
return nil if new_val == @name
|
187
|
+
new_val = nil if new_val == ''
|
188
|
+
raise JSS::MissingDataError, "Name can't be empty" unless new_val
|
189
|
+
raise JSS::AlreadyExistsError, "A #{RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" if JSS.send(LIST_METHOD).values.include?
|
190
|
+
|
191
|
+
### if the filename is the same, keep it the same
|
192
|
+
@filename = new_val if @filename == @name
|
193
|
+
@name = new_val
|
194
|
+
|
195
|
+
### if our REST resource is based on the name, update that too
|
196
|
+
@rest_rsrc = "#{RSRC_BASE}/name/#{URI.escape @name}" if @rest_rsrc.include? '/name/'
|
197
|
+
@need_to_update = true
|
198
|
+
end #name=
|
199
|
+
|
200
|
+
###
|
201
|
+
### Change the os_requirements
|
202
|
+
###
|
203
|
+
### Minumum OS's can be specified as a string using the notation ">=10.6.7"
|
204
|
+
### See the {JSS.expand_min_os} method for details.
|
205
|
+
###
|
206
|
+
### @param new_val[String, Array<String>] the new os requirements as a comma-separted String or an Array of Strings
|
207
|
+
###
|
208
|
+
### @return [void]
|
209
|
+
###
|
210
|
+
### @example String value
|
211
|
+
### myscript.os_requirements "10.5, 10.5.3, 10.6.x"
|
212
|
+
###
|
213
|
+
### @example Array value
|
214
|
+
### ok_oses = ['10.5', '10.5.3', '10.6.x']
|
215
|
+
### myscript.os_requirements ok_oses
|
216
|
+
###
|
217
|
+
### @example Minimum OS
|
218
|
+
### myscript.os_requirements ">=10.7.5"
|
219
|
+
###
|
220
|
+
def os_requirements= (new_val)
|
221
|
+
### nil should be an empty array
|
222
|
+
new_val = [] if new_val.to_s.empty?
|
223
|
+
|
224
|
+
### if any value starts with >=, expand it
|
225
|
+
case new_val
|
226
|
+
when String
|
227
|
+
new_val = JSS.expand_min_os(new_val) if new_val =~ /^>=/
|
228
|
+
when Array
|
229
|
+
new_val.map!{|a| a =~ /^>=/ ? JSS.expand_min_os(a) : a }
|
230
|
+
new_val.flatten!
|
231
|
+
new_val.uniq!
|
232
|
+
else
|
233
|
+
raise JSS::InvalidDataError, "os_requirements must be a String or an Array of strings"
|
234
|
+
end # case
|
235
|
+
|
236
|
+
### get the array version
|
237
|
+
@os_requirements = JSS.to_s_and_a(new_val)[:arrayform]
|
238
|
+
@need_to_update = true
|
239
|
+
|
240
|
+
end #os_requirements=
|
241
|
+
|
242
|
+
###
|
243
|
+
### Change the priority of this script
|
244
|
+
###
|
245
|
+
### @param new_val[Integer] the new priority, which must be one of {PRIORITIES}
|
246
|
+
###
|
247
|
+
### @return [void]
|
248
|
+
###
|
249
|
+
def priority= (new_val)
|
250
|
+
return nil if new_val == @priority
|
251
|
+
new_val = DEFAULT_PRIORITY if new_val.nil? or new_val == ""
|
252
|
+
raise JSS::InvalidDataError, ":priority must be one of: #{PRIORITIES.join ', '}" unless PRIORITIES.include? new_val
|
253
|
+
@priority = new_val
|
254
|
+
@need_to_update = true
|
255
|
+
end #priority=
|
256
|
+
|
257
|
+
###
|
258
|
+
### Change the info field
|
259
|
+
###
|
260
|
+
### @param new_val[String] the new info
|
261
|
+
###
|
262
|
+
### @return [void]
|
263
|
+
###
|
264
|
+
def info= (new_val)
|
265
|
+
return nil if new_val == @info
|
266
|
+
### line breaks should be \r
|
267
|
+
new_val = new_val.to_s.gsub(/\n/, "\r")
|
268
|
+
@info = new_val
|
269
|
+
@need_to_update = true
|
270
|
+
end #info=
|
271
|
+
|
272
|
+
###
|
273
|
+
### Change the notes field
|
274
|
+
###
|
275
|
+
### @param new_val[String] the new notes
|
276
|
+
###
|
277
|
+
### @return [void]
|
278
|
+
###
|
279
|
+
def notes= (new_val)
|
280
|
+
return nil if new_val == @notes
|
281
|
+
### line breaks should be \r
|
282
|
+
new_val = new_val.to_s.gsub(/\n/, "\r")
|
283
|
+
@notes = new_val
|
284
|
+
@need_to_update = true
|
285
|
+
end #notes=
|
286
|
+
|
287
|
+
###
|
288
|
+
### Change the category
|
289
|
+
###
|
290
|
+
### @param new_val[String] the name of the new category, which must be in {JSS::Category.all_names}
|
291
|
+
###
|
292
|
+
### @return [void]
|
293
|
+
###
|
294
|
+
def category= (new_val)
|
295
|
+
return nil if new_val == @category
|
296
|
+
new_val = nil if new_val == ''
|
297
|
+
new_val ||= JSS::Category::DEFAULT_CATEGORY
|
298
|
+
raise JSS::InvalidDataError, "Category #{new_val} is not known to the JSS" unless JSS::Category.all_names.include? new_val
|
299
|
+
@need_to_update = true
|
300
|
+
@category = new_val
|
301
|
+
end #category=
|
302
|
+
|
303
|
+
###
|
304
|
+
### Replace all the script parameters at once.
|
305
|
+
###
|
306
|
+
### This will replace the entire set with the hash provided.
|
307
|
+
###
|
308
|
+
### @param new_val[Hash] the Hash keys must exist in {PARAMETER_KEYS}
|
309
|
+
###
|
310
|
+
### @return [void]
|
311
|
+
###
|
312
|
+
def parameters= (new_val)
|
313
|
+
return nil if new_val == @parameters
|
314
|
+
new_val = {} if new_val.nil? or new_val== ''
|
315
|
+
|
316
|
+
### check the values
|
317
|
+
raise JSS::InvalidDataError, ":parameters must be a Hash with keys :parameter4 thru :parameter11" unless new_val.kind_of? Hash and (new_val.keys & PARAMETER_KEYS) == new_val.keys
|
318
|
+
new_val.each do |k,v|
|
319
|
+
raise JSS::InvalidDataError, ":parameter values must be strings or nil" unless v.nil? or v.kind_of? String
|
320
|
+
end
|
321
|
+
|
322
|
+
@parameters = new_val
|
323
|
+
@need_to_update = true
|
324
|
+
end # parameters=
|
325
|
+
|
326
|
+
###
|
327
|
+
### Change one of the stored parameters
|
328
|
+
###
|
329
|
+
### @param param_num[Integer] which param are we setting? must be 4..11
|
330
|
+
###
|
331
|
+
### @param new_val[String] the new value for the parameter
|
332
|
+
###
|
333
|
+
### @return [void]
|
334
|
+
###
|
335
|
+
def set_parameter (param_num, new_val)
|
336
|
+
raise JSS::NoSuchItemError, "Parameter numbers must be from 4-11" unless (4..11).include? param_num
|
337
|
+
pkey = "parameter#{param_num}".to_sym
|
338
|
+
raise JSS::InvalidDataError, "parameter values must be strings or nil" unless new_val.nil? or new_val.kind_of? String
|
339
|
+
return nil if new_val == @parameters[pkey]
|
340
|
+
@parameters[pkey] = new_val
|
341
|
+
@need_to_update = true
|
342
|
+
end
|
343
|
+
|
344
|
+
###
|
345
|
+
### Change the executable code of this script.
|
346
|
+
###
|
347
|
+
### If the arg is a Pathname instance, or a String starting with "/"
|
348
|
+
### Then the arg is assumed to be a file from which to read the code.
|
349
|
+
###
|
350
|
+
### Otherwise it should be a String with the code itself, and it must start with '#!"
|
351
|
+
###
|
352
|
+
### After doing this, use {#create} or {#update} to write it to the database or
|
353
|
+
### use {#upload_master_file} to save it to the master dist. point.
|
354
|
+
###
|
355
|
+
### @param new_val[String,Pathname] the new script contents or a path to a file containing it.
|
356
|
+
###
|
357
|
+
### @return [void]
|
358
|
+
###
|
359
|
+
def script_contents= (new_val)
|
360
|
+
|
361
|
+
new_code = case new_val
|
362
|
+
when String
|
363
|
+
if new_val.start_with? '/'
|
364
|
+
Pathname.new(new_val).read
|
365
|
+
else
|
366
|
+
new_val
|
367
|
+
end #if
|
368
|
+
when Pathname
|
369
|
+
new_val.read
|
370
|
+
else
|
371
|
+
raise JSS::InvalidDataError, "New code must be a String (path or code) or Pathname instance"
|
372
|
+
end # case
|
373
|
+
|
374
|
+
raise JSS::InvalidDataError, "Script contents must start with '#!'" unless new_code.start_with? '#!'
|
375
|
+
|
376
|
+
@script_contents = new_code
|
377
|
+
@need_to_update = true
|
378
|
+
end
|
379
|
+
|
380
|
+
###
|
381
|
+
### Save the @script_contents for this script to a file on the Master Distribution point.
|
382
|
+
###
|
383
|
+
### If you'll be uploading several files you can specify unmount as false, and do it manually when all
|
384
|
+
### are finished.
|
385
|
+
###
|
386
|
+
### use {#script_contents=} to set the script_contents from a String or Pathname
|
387
|
+
###
|
388
|
+
### @param rw_pw[String] the password for the read/write account on the master Distribution Point
|
389
|
+
###
|
390
|
+
### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
|
391
|
+
###
|
392
|
+
### @return [void]
|
393
|
+
###
|
394
|
+
def upload_master_file( rw_pw, unmount = true)
|
395
|
+
raise JSS::MissingDataError, "No code specified. Use #code= first." if @script_contents.nil? or @script_contents.empty?
|
396
|
+
|
397
|
+
mdp = JSS::DistributionPoint.master_distribution_point
|
398
|
+
raise JSS::InvaldDatatError, "Incorrect password for read-write access to master distribution point." unless mdp.check_pw :rw, rw_pw
|
399
|
+
|
400
|
+
destination = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
|
401
|
+
destination.save @script_contents
|
402
|
+
mdp.unmount if unmount
|
403
|
+
end # upload
|
404
|
+
|
405
|
+
###
|
406
|
+
### Delete the filename from the master distribution point, if it exists.
|
407
|
+
###
|
408
|
+
### If you'll be uploading several files you can specify unmount as false, and do it manually when all
|
409
|
+
### are finished.
|
410
|
+
###
|
411
|
+
### @param rw_pw[String] the password for the read/write account on the master Distribution Point
|
412
|
+
###
|
413
|
+
### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
|
414
|
+
###
|
415
|
+
### @return [Boolean] was the file deleted?
|
416
|
+
###
|
417
|
+
def delete_master_file(rw_pw, unmount = true)
|
418
|
+
file = JSS::DistributionPoint.master_distribution_point.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
|
419
|
+
if file.exist?
|
420
|
+
file.delete
|
421
|
+
did_it = true
|
422
|
+
else
|
423
|
+
did_it = false
|
424
|
+
end # if exists
|
425
|
+
JSS::DistributionPoint.master_distribution_point.unmount if unmount
|
426
|
+
return did_it
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
###
|
431
|
+
### Run this script on the current machine using the "jamf runScript" command.
|
432
|
+
###
|
433
|
+
### If the script code is available in the {#script_contents} attribute, then that
|
434
|
+
### code is saved to a tmp file, and executed. Otherwise, the script is assumed
|
435
|
+
### to be stored on the distribution point.
|
436
|
+
###
|
437
|
+
### If the dist. point has http downloads enabled, then the URL is used as the path with the
|
438
|
+
### 'jamf runScript' command.
|
439
|
+
###
|
440
|
+
### If http is not an option, the dist.point is mounted, and the script copied locally before running.
|
441
|
+
### In this case the options must include :ro_pw => 'somepass'
|
442
|
+
### to provide the read-only password for mounting the distribution point. If :unmount => true
|
443
|
+
### is provided, the dist. point will be unmounted immediately after copying
|
444
|
+
### the script locally. Otherwise it will remain mounted, in case there's further need of it.
|
445
|
+
###
|
446
|
+
### Any local on-disk copies of the script are removed after running.
|
447
|
+
###
|
448
|
+
### After the script runs, this method returns a two-item Array.
|
449
|
+
### - the first item is an Integer, the exit status of the script itself (0 means success)
|
450
|
+
### - the second item is a String, the output (stdout + stderr) of the jamf binary, which will include
|
451
|
+
### the script output.
|
452
|
+
### The exit status of the jamf binary process will be available as a Process::Status object
|
453
|
+
### in $? immediately after running.
|
454
|
+
###
|
455
|
+
### @param opts[Hash] the options for running the script
|
456
|
+
###
|
457
|
+
### @option opts :target[String,Pathname] the 'target drive', passed to the script as the first commandline option.
|
458
|
+
### Defaults to '/'
|
459
|
+
###
|
460
|
+
### @option opts :computer_name[String] the name of the computer, passed to the script as the second commandline
|
461
|
+
### option. Defaults to the name of the current machine
|
462
|
+
###
|
463
|
+
### @option opts :username[String] the username to be passed to the script as the third commandline option.
|
464
|
+
###
|
465
|
+
### @option opts :p1..:p8[String] the values to be passed as the 4th - 11th commandline options, overriding
|
466
|
+
### those defined with the script in the JSS
|
467
|
+
###
|
468
|
+
### @option opts :ro_pw[String] the read-only password for mounting the distribution point, if needed
|
469
|
+
###
|
470
|
+
### @option opts :unmount[Boolean} should the dist. point be unmounted when finished, if we mounted it?
|
471
|
+
###
|
472
|
+
### @option opts :verbose[Boolean] should the 'jamf runScript' command be verbose?
|
473
|
+
###
|
474
|
+
### @option opts :show_output[Boolean] should the output (stdout + stderr) of 'jamf runScript' be copied to
|
475
|
+
### stdout in realtime, as well as returned?
|
476
|
+
###
|
477
|
+
### @return [Array<(Integer,String)>] the exit status of the *script* and stdout+stderr of 'jamf runScript'.
|
478
|
+
### The exit status of the jamf binary will be available in $? immediately after running.
|
479
|
+
###
|
480
|
+
### *NOTE* In the WEB UI and API, the definable parameters are numbered 4-11, since 1, 2, & 3 are the
|
481
|
+
### target drive, computer name, and user name respectively. However, the jamf binary refers to them as
|
482
|
+
### p1-p8, and that's how they are expected as options to #run. So if :p1=> "new param" is given as an
|
483
|
+
### aption to #run, it will override any value that the API provided in @parameters[:parameter4]
|
484
|
+
###
|
485
|
+
def run( opts = {} )
|
486
|
+
|
487
|
+
opts[:target] ||= "/"
|
488
|
+
opts[:p1] ||= @parameters[:parameter4]
|
489
|
+
opts[:p2] ||= @parameters[:parameter5]
|
490
|
+
opts[:p3] ||= @parameters[:parameter6]
|
491
|
+
opts[:p4] ||= @parameters[:parameter7]
|
492
|
+
opts[:p5] ||= @parameters[:parameter8]
|
493
|
+
opts[:p6] ||= @parameters[:parameter9]
|
494
|
+
opts[:p7] ||= @parameters[:parameter10]
|
495
|
+
opts[:p8] ||= @parameters[:parameter11]
|
496
|
+
|
497
|
+
dp_mount_pt = nil
|
498
|
+
delete_exec = false
|
499
|
+
|
500
|
+
begin
|
501
|
+
|
502
|
+
# do we have the code already? if so, save it out and make it executable
|
503
|
+
if @script_contents and (not @script_contents.empty?)
|
504
|
+
|
505
|
+
script_path = JSS::Client::DOWNLOADS_FOLDER
|
506
|
+
|
507
|
+
executable = script_path + @filename
|
508
|
+
|
509
|
+
executable.jss_touch
|
510
|
+
executable.chmod 0700
|
511
|
+
executable.jss_save @script_contents
|
512
|
+
delete_exec = true
|
513
|
+
|
514
|
+
# otherwise, get it from the dist. point
|
515
|
+
else
|
516
|
+
dist_point = JSS::DistributionPoint.my_distribution_point
|
517
|
+
|
518
|
+
### how do we access our dist. point?
|
519
|
+
if dist_point.http_downloads_enabled
|
520
|
+
script_path = dist_point.http_url + "/#{DIST_POINT_SCRIPTS_FOLDER}/"
|
521
|
+
|
522
|
+
else
|
523
|
+
dp_mount_pt = dist_point.mount opts[:ro_pw]
|
524
|
+
|
525
|
+
script_path = (dp_mount_pt + DIST_POINT_SCRIPTS_FOLDER)
|
526
|
+
|
527
|
+
end # if http enabled
|
528
|
+
|
529
|
+
end # if @script_contents and (not @script_contents.empty?)
|
530
|
+
|
531
|
+
|
532
|
+
# build the command as an array.
|
533
|
+
command_arry = ["-script", @filename, '-path', script_path.to_s]
|
534
|
+
|
535
|
+
command_arry << "-target"
|
536
|
+
command_arry << opts[:target].to_s
|
537
|
+
|
538
|
+
command_arry << "-computerName" if opts[:computer_name]
|
539
|
+
command_arry << opts[:computer_name] if opts[:computer_name]
|
540
|
+
|
541
|
+
command_arry << "-username" if opts[:username]
|
542
|
+
command_arry << opts[:username] if opts[:username]
|
543
|
+
|
544
|
+
command_arry << "-p1" if opts[:p1]
|
545
|
+
command_arry << opts[:p1] if opts[:p1]
|
546
|
+
|
547
|
+
command_arry << "-p2" if opts[:p2]
|
548
|
+
command_arry << opts[:p2] if opts[:p2]
|
549
|
+
|
550
|
+
command_arry << "-p3" if opts[:p3]
|
551
|
+
command_arry << opts[:p3] if opts[:p3]
|
552
|
+
|
553
|
+
command_arry << "-p4" if opts[:p4]
|
554
|
+
command_arry << opts[:p4] if opts[:p4]
|
555
|
+
|
556
|
+
command_arry << "-p5" if opts[:p5]
|
557
|
+
command_arry << opts[:p5] if opts[:p5]
|
558
|
+
|
559
|
+
command_arry << "-p6" if opts[:p6]
|
560
|
+
command_arry << opts[:p6] if opts[:p6]
|
561
|
+
|
562
|
+
command_arry << "-p7" if opts[:p7]
|
563
|
+
command_arry << opts[:p7] if opts[:p7]
|
564
|
+
|
565
|
+
command_arry << "-p8" if opts[:p8]
|
566
|
+
command_arry << opts[:p8] if opts[:p8]
|
567
|
+
|
568
|
+
command_arry << "-verbose" if opts[:verbose]
|
569
|
+
|
570
|
+
command = command_arry.shelljoin
|
571
|
+
|
572
|
+
jamf_output = JSS::Client.run_jamf "runScript", command, opts[:show_output]
|
573
|
+
|
574
|
+
jamf_output =~ /^.*Script exit code: (\d+)(\D|$)/
|
575
|
+
|
576
|
+
script_exitstatus = $1.to_i
|
577
|
+
|
578
|
+
ensure
|
579
|
+
executable.delete if delete_exec and executable.exist?
|
580
|
+
dist_point.unmount if (dp_mount_pt and dp_mount_pt.mountpoint? and opts[:unmount])
|
581
|
+
end # begin/ensure
|
582
|
+
|
583
|
+
return [script_exitstatus, jamf_output]
|
584
|
+
|
585
|
+
end # def run
|
586
|
+
|
587
|
+
|
588
|
+
# aliases under their methods seem to confuse the YARD documenter, so I'm putting them all here.
|
589
|
+
alias oses os_requirements
|
590
|
+
alias oses= os_requirements=
|
591
|
+
alias code script_contents
|
592
|
+
alias code= script_contents=
|
593
|
+
alias contents script_contents
|
594
|
+
alias contents= script_contents=
|
595
|
+
|
596
|
+
|
597
|
+
#####################################
|
598
|
+
### Private Instance Methods
|
599
|
+
#####################################
|
600
|
+
|
601
|
+
private
|
602
|
+
|
603
|
+
###
|
604
|
+
### Return the xml for creating or updating this script in the JSS
|
605
|
+
###
|
606
|
+
def rest_xml
|
607
|
+
doc = REXML::Document.new
|
608
|
+
scpt = doc.add_element "script"
|
609
|
+
scpt.add_element('category').text = @category
|
610
|
+
scpt.add_element('filename').text = @filename
|
611
|
+
scpt.add_element('id').text = @id
|
612
|
+
scpt.add_element('info').text = @info
|
613
|
+
scpt.add_element('name').text = @name
|
614
|
+
scpt.add_element('notes').text = @notes
|
615
|
+
scpt.add_element('os_requirements').text = JSS.to_s_and_a(@os_requirements)[:stringform]
|
616
|
+
scpt.add_element('priority').text = @priority
|
617
|
+
|
618
|
+
if @parameters.empty?
|
619
|
+
scpt.add_element('parameters').text = nil
|
620
|
+
else
|
621
|
+
pars = scpt.add_element('parameters')
|
622
|
+
PARAMETER_KEYS.each {|p| pars.add_element(p.to_s).text = @parameters[p]}
|
623
|
+
end
|
624
|
+
|
625
|
+
scpt.add_element('script_contents_encoded').text = Base64.encode64(@script_contents)
|
626
|
+
|
627
|
+
return doc.to_s
|
628
|
+
end # rest xml
|
629
|
+
|
630
|
+
end # class Script
|
631
|
+
end # midule
|